{"id":17808,"date":"2026-10-19T13:00:00","date_gmt":"2026-10-19T11:00:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/?p=17808"},"modified":"2026-10-09T22:39:16","modified_gmt":"2026-10-09T20:39:16","slug":"ga4-measurement-protocol-ladenkaeufe-kundenkarte","status":"publish","type":"post","link":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/","title":{"rendered":"Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel"},"content":{"rendered":"<p>Eine Ladenkasse wei\u00df drei Dinge \u00fcber einen Verkauf: was gekauft wurde, was es gekostet hat und welche Kundenkarte am Tresen \u00fcber den Scanner ging. GA4 wei\u00df nichts davon. GA4 kennt Browser, und es kennt sie \u00fcber einen Cookie-Wert, den es im Laden nie gegeben hat.<\/p>\n\n<p>Das Measurement Protocol ist der einzige Weg, der das Wissen der Kasse in dieselbe Property tragen kann wie die Daten der Website. Es ist ein gew\u00f6hnlicher HTTPS-POST, es braucht keine Bibliothek, und es nimmt fast alles an \u2014 genau deshalb muss dieser Weg sorgf\u00e4ltig gebaut werden statt schnell.<\/p>\n\n<figure class=\"lw-diagram\">\n<img src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/kartenschluessel-de.png\" width=\"1120\" height=\"580\" decoding=\"async\" loading=\"lazy\"\n     alt=\"Die Kette von der Kundenkartennummer \u00fcber einen Hash und eine Zuordnungstabelle bis zur Measurement-Protocol-Nutzlast, darunter eine Zeitachse mit dem angenommenen Zeitfenster, seinen gemessenen Grenzen und dem Anteil verlorener Belege bei drei Upload-Rhythmen\">\n<figcaption>Die Kette von der Kartennummer zur Nutzlast, und das Fenster, in das der Zeitstempel fallen muss.<\/figcaption>\n<\/figure>\n\n<h2>Was \u00fcberbr\u00fcckt werden muss<\/h2>\n\n<p>Zwei Felder einer Measurement-Protocol-Nutzlast tragen Identit\u00e4t, und sie sind nicht austauschbar.<\/p>\n\n<p><code>client_id<\/code> ist die Ger\u00e4teidentit\u00e4t. Diesen Wert schreibt das GA4-Tag im Browser in das <code>_ga<\/code>-Cookie, und er ist <strong>Pflicht<\/strong> \u2014 eine Nutzlast ohne ihn wies der Pr\u00fcfserver mit <code>VALUE_REQUIRED<\/code> ab, selbst wenn eine <code>user_id<\/code> dabei war. <code>user_id<\/code> ist die Personenidentit\u00e4t, freiwillig, und das Feld, das einen Ger\u00e4tewechsel \u00fcbersteht.<\/p>\n\n<p>Die Kundenkartennummer ist weder das eine noch das andere. Diese Nummer ist eine dritte Kennung, die nur im Laden existiert, und die ganze Arbeit besteht darin, aus ihr die beiden anderen zu machen.<\/p>\n\n<h2>Die Kartennummer wird gehasht, bevor sie irgendwohin geht<\/h2>\n\n<p>Eine Kartennummer bezeichnet eine Person und geh\u00f6rt in die Kundenbeziehung. Im Klartext an eine Analytics-Property geschickt macht sie aus einer Ladenkennung einen dauerhaften Eintrag auf fremder Infrastruktur, und GA4 verbietet das Senden personenbeziehbarer Daten ausdr\u00fccklich.<\/p>\n\n<p>Ein Hash mit Schl\u00fcssel l\u00f6st beides zugleich. Der Pfeffer ist ein Geheimnis, das auf dem eigenen Server bleibt; ohne ihn l\u00e4sst sich der Hash aus einer gestohlenen Liste von Kartennummern nicht nachrechnen.<\/p>\n\n\n\n<figure style=\"--cbb-digits:1\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"php\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">PHP<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-php\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-keyword\">function<\/span> <span class=\"cbb-function\">card_key<\/span>(string <span class=\"cbb-variable\">$card<\/span>, string <span class=\"cbb-variable\">$pepper<\/span>): string {\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-comment\">\/\/ Zuerst vereinheitlichen: die Kasse schreibt \"LW 4471 9930\",<\/span>\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-comment\">\/\/ das Webformular \"lw-4471-9930\". Ohne diesen Schritt hashen<\/span>\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-comment\">\/\/ beide Seiten verschieden, und nichts findet je zusammen.<\/span>\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-variable\">$clean<\/span> = <span class=\"cbb-function\">strtoupper<\/span>(<span class=\"cbb-function\">preg_replace<\/span>(<span class=\"cbb-string\">'\/&#91;^A-Za-z0-9&#93;\/'<\/span>, <span class=\"cbb-string\">''<\/span>, <span class=\"cbb-variable\">$card<\/span>));\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">return<\/span> <span class=\"cbb-function\">hash_hmac<\/span>(<span class=\"cbb-string\">'sha256'<\/span>, <span class=\"cbb-variable\">$clean<\/span>, <span class=\"cbb-variable\">$pepper<\/span>);\n<\/span><span class=\"cbb-line\">}<\/span><\/code><\/pre><\/figure>\n\n\n\n<p>Diese Vereinheitlichung ist die Stelle, an der Integrationen still kaputtgehen. Zwei Systeme, die dieselbe Karte verschieden schreiben, erzeugen zwei verschiedene Hashes, und die Zusammenf\u00fchrung scheitert danach ohne Fehlermeldung an irgendeiner Stelle.<\/p>\n\n<h2>Die Paarung festhalten, solange die Kundin oder der Kunde online ist<\/h2>\n\n<p>Die Br\u00fccke entsteht einmal, im Browser, in dem einzigen Moment, in dem beide Kennungen am selben Ort vorliegen: wenn eine Kartennummer im Kundenkonto eingetragen oder beim Online-Kauf verkn\u00fcpft wird.<\/p>\n\n<p>Die <code>client_id<\/code> ist ein Teil des Werts im <code>_ga<\/code>-Cookie. Der folgende Code liest sie aus, indem er die letzten beiden Abschnitte mit einem Punkt verbindet.<\/p>\n\n\n\n<figure style=\"--cbb-digits:1\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"php\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">PHP<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-php\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ _ga sieht aus wie GA1.1.1899482740.1757000000<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ Die client_id sind die letzten beiden Abschnitte, mit Punkt verbunden.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">function<\/span> <span class=\"cbb-function\">ga_client_id<\/span>(): ?string {\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">if<\/span> (<span class=\"cbb-keyword\">empty<\/span>(<span class=\"cbb-variable\">$_COOKIE<\/span>&#91;<span class=\"cbb-string\">'_ga'<\/span>&#93;)) { <span class=\"cbb-keyword\">return<\/span> <span class=\"cbb-constant\">null<\/span>; }\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-variable\">$parts<\/span> = <span class=\"cbb-function\">explode<\/span>(<span class=\"cbb-string\">'.'<\/span>, <span class=\"cbb-variable\">$_COOKIE<\/span>&#91;<span class=\"cbb-string\">'_ga'<\/span>&#93;);\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">if<\/span> (<span class=\"cbb-function\">count<\/span>(<span class=\"cbb-variable\">$parts<\/span>) &lt; <span class=\"cbb-number\">4<\/span>) { <span class=\"cbb-keyword\">return<\/span> <span class=\"cbb-constant\">null<\/span>; }\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">return<\/span> <span class=\"cbb-variable\">$parts<\/span>&#91;<span class=\"cbb-function\">count<\/span>(<span class=\"cbb-variable\">$parts<\/span>) - <span class=\"cbb-number\">2<\/span>&#93; . <span class=\"cbb-string\">'.'<\/span> . <span class=\"cbb-variable\">$parts<\/span>&#91;<span class=\"cbb-function\">count<\/span>(<span class=\"cbb-variable\">$parts<\/span>) - <span class=\"cbb-number\">1<\/span>&#93;;\n<\/span><span class=\"cbb-line\">}<\/span><\/code><\/pre><\/figure>\n\n\n\n<p>Im selben Moment setzt die Website <strong>denselben Hash<\/strong> als <code>user_id<\/code> auf dem Web-Stream. Rechnen Website und Uploader hier verschiedene Werte aus, bekommt GA4 zwei einander fremde Personen, und keine der beiden Seiten meldet einen Fehler.<\/p>\n\n<h2>Die Zuordnungstabelle<\/h2>\n\n<p>Eine Karte geh\u00f6rt zu einer Person, doch diese Person kann mehrere Browser nutzen. Die Tabelle muss deshalb mehrere Zuordnungen je Karte speichern k\u00f6nnen. Der Uploader braucht au\u00dferdem eine Regel, nach der er eine Zeile ausw\u00e4hlt.<\/p>\n\n\n\n<figure style=\"--cbb-digits:1\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"sql\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">SQL<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-sql\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-keyword\">CREATE<\/span> <span class=\"cbb-keyword\">TABLE<\/span> <span class=\"cbb-function\">card_pairing<\/span> (\n<\/span><span class=\"cbb-line\">  card_hash  <span class=\"cbb-type\">CHAR<\/span>(<span class=\"cbb-number\">64<\/span>)    <span class=\"cbb-keyword\">NOT<\/span> <span class=\"cbb-constant\">NULL<\/span>,\n<\/span><span class=\"cbb-line\">  client_id  <span class=\"cbb-type\">VARCHAR<\/span>(<span class=\"cbb-number\">64<\/span>) <span class=\"cbb-keyword\">NOT<\/span> <span class=\"cbb-constant\">NULL<\/span>,\n<\/span><span class=\"cbb-line\">  first_seen <span class=\"cbb-type\">DATETIME<\/span>    <span class=\"cbb-keyword\">NOT<\/span> <span class=\"cbb-constant\">NULL<\/span>,\n<\/span><span class=\"cbb-line\">  last_seen  <span class=\"cbb-type\">DATETIME<\/span>    <span class=\"cbb-keyword\">NOT<\/span> <span class=\"cbb-constant\">NULL<\/span>,\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-keyword\">PRIMARY<\/span> <span class=\"cbb-keyword\">KEY<\/span> (card_hash, client_id),\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-keyword\">KEY<\/span> <span class=\"cbb-function\">by_time<\/span> (card_hash, last_seen)\n<\/span><span class=\"cbb-line\">) <span class=\"cbb-keyword\">ENGINE<\/span>=InnoDB;<\/span><\/code><\/pre><\/figure>\n\n\n\n<p>Am besten tr\u00e4gt die zuletzt gesehene Paarung: <code>ORDER BY last_seen DESC LIMIT 1<\/code>. Ein Telefon aus der vergangenen Woche ist die bessere Sch\u00e4tzung als ein Schreibtischrechner, der einmal im Jahr benutzt wird; die \u00e4lteste Zeile h\u00e4ngt den Kauf an ein Ger\u00e4t, das l\u00e4ngst ersetzt sein kann.<\/p>\n\n<h2>Eine client_id f\u00fcr alle, die nie online waren<\/h2>\n\n<p>Im Laden hat die Mehrzahl der Karten \u00fcberhaupt keine Paarung. Auch diese Belege m\u00fcssen irgendwohin, und eine <code>client_id<\/code> zu erfinden ist erlaubt \u2014 aber nicht in beliebiger Form.<\/p>\n\n<p>Der Pr\u00fcfserver erzwingt ein Format. Am 9. September 2026 mit gesetztem <code>ENFORCE_RECOMMENDATIONS<\/code> gemessen: <code>card-LW-4471-9930<\/code>, ein zwanzig Zeichen langes Hash-Pr\u00e4fix und die einzelne Ziffer <code>7<\/code> wurden allesamt abgewiesen, mit <code>client_id [...] has invalid format. It should be in &lt;number&gt;.&lt;number&gt; format.<\/code> Ein Komma statt des Punktes wurde abgewiesen, ein dritter Abschnitt ebenso. Angenommen wurden: <code>1.1<\/code>, <code>0.0<\/code>, zwanzigstellige Zahlen auf beiden Seiten und \u2014 unerwartet \u2014 <code>1899482740.<\/code> und <code>.1757000000<\/code>, jeweils mit fehlender H\u00e4lfte.<\/p>\n\n<p>Eine feste Ableitung aus dem Kartenhash erf\u00fcllt das Format und bleibt \u00fcber alle L\u00e4ufe hinweg gleich.<\/p>\n\n\n\n<figure style=\"--cbb-digits:1\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"php\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">PHP<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-php\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ Zwei 32-Bit-Zahlen aus den ersten 16 Hex-Zeichen.<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-variable\">$h<\/span>   = <span class=\"cbb-function\">card_key<\/span>(<span class=\"cbb-variable\">$card<\/span>, <span class=\"cbb-variable\">$pepper<\/span>);\n<\/span><span class=\"cbb-line\"><span class=\"cbb-variable\">$cid<\/span> = <span class=\"cbb-function\">hexdec<\/span>(<span class=\"cbb-function\">substr<\/span>(<span class=\"cbb-variable\">$h<\/span>, <span class=\"cbb-number\">0<\/span>, <span class=\"cbb-number\">8<\/span>)) . <span class=\"cbb-string\">'.'<\/span> . <span class=\"cbb-function\">hexdec<\/span>(<span class=\"cbb-function\">substr<\/span>(<span class=\"cbb-variable\">$h<\/span>, <span class=\"cbb-number\">8<\/span>, <span class=\"cbb-number\">8<\/span>));\n<\/span><span class=\"cbb-line\"><span class=\"cbb-comment\">\/\/ LW-4471-9930 -&gt; 2531008582.2372831494, vom Pruefserver angenommen<\/span><\/span><\/code><\/pre><\/figure>\n\n\n\n<p>Was das bringt und was nicht: Die Belege einer Karte landen jetzt auf einer festen Identit\u00e4t, statt sich zu verstreuen. Mit den Web-Sitzungen derselben Person wachsen sie nicht zusammen, denn der abgeleitete Wert passt zu keinem <code>_ga<\/code>-Cookie auf der Welt. Diese Verbindung tr\u00e4gt allein die <code>user_id<\/code>, und auch die nur in einer Property, deren Berichtsidentit\u00e4t sie benutzen darf.<\/p>\n\n<h2>Die Nutzlast, Feld f\u00fcr Feld<\/h2>\n\n<p>Zwei Zugangsdaten in der Abfragezeichenfolge, alles andere im JSON-Rumpf. <code>measurement_id<\/code> und <code>api_secret<\/code> geh\u00f6ren nie in den Rumpf \u2014 dort werden sie zu unbekannten Feldern, und ein unbekanntes Feld wird nicht \u00fcbergangen, sondern verwirft die ganze Nutzlast.<\/p>\n\n\n\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"http\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">HTTP<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-http\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-keyword\">POST<\/span> \/mp\/collect?measurement_id=G-XXXXXXXXXX&amp;api_secret=SECRET <span class=\"cbb-meta\">HTTP\/1.1<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-attr\">Host<\/span>: www.google-analytics.com\n<\/span><span class=\"cbb-line\"><span class=\"cbb-attr\">Content-Type<\/span>: application\/json\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">{\n<\/span><span class=\"cbb-line\">  \"client_id\": \"2531008582.2372831494\",\n<\/span><span class=\"cbb-line\">  \"user_id\": \"9f2c4b1e8a7d3c5f0b6e2a9d4c8f1b3e7a0d5c2f9b4e8a1d6c3f0b7e2a5d9c4f\",\n<\/span><span class=\"cbb-line\">  \"consent\": { \"ad_user_data\": \"GRANTED\", \"ad_personalization\": \"DENIED\" },\n<\/span><span class=\"cbb-line\">  \"events\": &#91;\n<\/span><span class=\"cbb-line\">    {\n<\/span><span class=\"cbb-line\">      \"name\": \"purchase\",\n<\/span><span class=\"cbb-line\">      \"timestamp_micros\": \"1792301400000000\",\n<\/span><span class=\"cbb-line\">      \"params\": {\n<\/span><span class=\"cbb-line\">        \"transaction_id\": \"HH-ALT-2026-1016-0007\",\n<\/span><span class=\"cbb-line\">        \"value\": 48.90,\n<\/span><span class=\"cbb-line\">        \"currency\": \"EUR\",\n<\/span><span class=\"cbb-line\">        \"affiliation\": \"Filiale Hamburg-Altona\",\n<\/span><span class=\"cbb-line\">        \"session_id\": \"1792301400\",\n<\/span><span class=\"cbb-line\">        \"engagement_time_msec\": \"1\",\n<\/span><span class=\"cbb-line\">        \"items\": &#91;\n<\/span><span class=\"cbb-line\">          { \"item_id\": \"SKU-3391\", \"item_name\": \"Handkaffeemuehle\",\n<\/span><span class=\"cbb-line\">            \"price\": 48.90, \"quantity\": 1 }\n<\/span><span class=\"cbb-line\">        &#93;\n<\/span><span class=\"cbb-line\">      }\n<\/span><span class=\"cbb-line\">    }\n<\/span><span class=\"cbb-line\">  &#93;\n<\/span><span class=\"cbb-line\">}<\/span><\/code><\/pre><\/figure>\n\n\n\n<p><code>transaction_id<\/code> ist die Belegnummer und das Einzige, was einen wiederholten Upload davon abh\u00e4lt, einen Verkauf doppelt zu z\u00e4hlen. <code>affiliation<\/code> tr\u00e4gt die Filiale \u2014 das Feld, das die Daten \u00fcberhaupt erst wertvoll macht. <code>session_id<\/code> und <code>engagement_time_msec<\/code> h\u00e4ngen das Ereignis in den Berichten an eine Sitzung; das Protokoll verlangt sie nicht, und ihr Fehlen wird nirgends gemeldet.<\/p>\n\n<p>Zwei Typregeln, beide gemessen. Ein Item-<code>price<\/code> von <code>\"48.90\"<\/code> als Text wird angenommen, <code>\"48,90\"<\/code> mit <code>Known item param [price] should have type [int64] or [double]<\/code> abgewiesen, ebenso <code>\"abc\"<\/code> und eine leere Zeichenkette. Der <code>value<\/code> auf Ereignisebene wird <strong>nicht<\/strong> ebenso gepr\u00fcft: dort ging <code>\"abc\"<\/code> kommentarlos durch. Ein deutsch formatierter Betrag aus der Kasse scheitert also an der einen Stelle und rutscht an der anderen still hindurch.<\/p>\n\n<h2>Der Zeitstempel und ein Fenster, das nur an einem Ende offen ist<\/h2>\n\n<p>Dieses Feld entscheidet, ob ein Offline-Upload \u00fcberhaupt funktioniert, und beide Kanten wurden gemessen statt aus der Doku \u00fcbernommen.<\/p>\n\n<table>\n<tr><th>Abstand zu jetzt<\/th><th>Urteil des Pr\u00fcfservers<\/th><\/tr>\n<tr><td>71 h 59 min 55 s in der Vergangenheit<\/td><td>angenommen<\/td><\/tr>\n<tr><td>72 h 00 min 00 s in der Vergangenheit<\/td><td>abgewiesen, <code>VALUE_INVALID<\/code><\/td><\/tr>\n<tr><td>0 s \u2014 die laufende Sekunde<\/td><td>angenommen<\/td><\/tr>\n<tr><td>5 s in der Zukunft<\/td><td>abgewiesen, <code>VALUE_INVALID<\/code><\/td><\/tr>\n<tr><td>15 min in der Zukunft<\/td><td>abgewiesen<\/td><\/tr>\n<\/table>\n\n<p>Die hintere Kante ist exakt und schlie\u00dft aus: 72 Stunden auf die Sekunde genau sind bereits zu alt. Die vordere Kante ist die \u00dcberraschung. Der Meldungstext lautet <code>Measurement protocol only supports timestamps [15m] into the future<\/code>, abgewiesen wurde aber jeder gepr\u00fcfte Wert nach vorn, bis hinunter zu f\u00fcnf Sekunden. Die Uhr der sendenden Maschine lag im selben Lauf innerhalb von 0,8 Sekunden am <code>Date<\/code>-Kopf von Google \u2014 an der Messseite liegt es also nicht.<\/p>\n\n<p>F\u00fcr einen Laden hei\u00dft das: die Kassenuhr ist ein laufendes Risiko. Ein Terminal, das eine Minute vorgeht, erzeugt Belege, die das Protokoll als Zukunftsereignisse behandelt, und der Produktivendpunkt nimmt sie trotzdem mit 204 entgegen. Ein Uploader sollte die Belegzeit gegen die eigene, verl\u00e4ssliche Uhr halten und alles Vordatierte zur\u00fcckhalten, statt es zu senden.<\/p>\n\n<p>Der Wert muss in Mikrosekunden vorliegen. Ein Zeitstempel in Sekunden wird als Datum im Jahr 1970 gelesen und mit <em>too far in the past<\/em> beantwortet. Die Meldung kann einen R\u00fcckdatierungsfehler vermuten lassen, obwohl tats\u00e4chlich die Zeiteinheit falsch ist.<\/p>\n\n<h2>Eine Anfrage je Kunde, und warum B\u00fcndelung hier anders funktioniert<\/h2>\n\n<p><code>client_id<\/code> und <code>user_id<\/code> stehen auf der obersten Ebene der Nutzlast, neben <code>events<\/code>, nicht innerhalb eines Ereignisses. Eine <code>client_id<\/code> in ein Ereignis zu verschieben wurde rundheraus abgewiesen: die Nutzlast lie\u00df sich nicht mehr lesen, mit <code>invalid JSON in google.analytics.measurement.Measurement @ events[1]<\/code>. Bei einer <code>user_id<\/code> im Ereignis geschah dasselbe.<\/p>\n\n<p>Eine Anfrage kann also keine Kunden mischen. F\u00fcnfundzwanzig Belege von f\u00fcnfundzwanzig Karteninhabern in einen Aufruf zu packen geht nicht, und die Arbeitseinheit des Uploaders ist der Kunde, nicht das B\u00fcndel.<\/p>\n\n<p>Was dagegen <em>geht<\/em>, ist ein Zeitstempel je Ereignis: <code>timestamp_micros<\/code> innerhalb eines Ereignisses wurde angenommen, auch in einer Nutzlast mit einem obersten Zeitstempel von vor einer Stunde und einem zweiten Ereignis von vor zwei Stunden. Eine Anfrage kann damit s\u00e4mtliche Belege einer Karte aus einem ganzen Zeitraum tragen, jeden mit seiner eigenen Zeit.<\/p>\n\n<p>Die dokumentierte Obergrenze liegt bei f\u00fcnfundzwanzig Ereignissen und 130 kB je Anfrage. Der Pr\u00fcfserver erzwingt keine davon \u2014 sechsundzwanzig Ereignisse gingen durch, und hundert Belege mit je zwei Positionen kamen auf 28,8 kB und ebenfalls durch \u2014, aber eine dokumentierte und unerzwungene Grenze bleibt eine Grenze, und die Folgen zeigen sich in den Berichten.<\/p>\n\n<h2>Pr\u00fcfen vor dem Senden, weil der Produktivbetrieb nie antwortet<\/h2>\n\n<p>Der Produktivendpunkt gibt auf alles <strong>204 No Content<\/strong> zur\u00fcck. Mit den Nutzlasten aus diesem Artikel gemessen: ein g\u00fcltiger Beleg, ein Beleg mit Datum vor drei\u00dfig Tagen, ein Zeitstempel in Sekunden statt Mikrosekunden, ein vierbuchstabiger W\u00e4hrungscode und eine Nutzlast ganz ohne <code>client_id<\/code> \u2014 alle 204, alle unter 220 Millisekunden, alle mit leerem Rumpf. Der Fall ohne <code>client_id<\/code> ist der aufschlussreiche, denn genau diese Nutzlast weist der Pr\u00fcfserver ab.<\/p>\n\n<p>Der Pr\u00fcfendpunkt ist dieselbe Adresse mit <code>\/debug<\/code> vor dem Pfad. Er kontrolliert weder <code>measurement_id<\/code> noch <code>api_secret<\/code>, eine Nutzlast l\u00e4sst sich also pr\u00fcfen, ohne dass ein Geheimnis das Haus verl\u00e4sst.<\/p>\n\n\n\n<figure style=\"--cbb-digits:1\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"bash\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">Shell<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-bash\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-function\">curl<\/span> <span class=\"cbb-attr\">-s<\/span> <span class=\"cbb-attr\">-X<\/span> POST \\\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-string\">'https:\/\/www.google-analytics.com\/debug\/mp\/collect?measurement_id=G-XXXXXXXXXX&amp;api_secret=platzhalter'<\/span> \\\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-attr\">-H<\/span> <span class=\"cbb-string\">'Content-Type: application\/json'<\/span> \\\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-attr\">-d<\/span> <span class=\"cbb-string\">'{\"client_id\":\"2531008582.2372831494\",<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-string\">       \"validation_behavior\":\"ENFORCE_RECOMMENDATIONS\",<\/span>\n<\/span><span class=\"cbb-line\"><span class=\"cbb-string\">       \"events\":&#91;{\"name\":\"purchase\",\"params\":{\"currency\":\"EUR\",\"value\":48.90}}&#93;}'<\/span><\/span><\/code><\/pre><\/figure>\n\n\n\n<p><code>validation_behavior<\/code> geh\u00f6rt in die Pr\u00fcfung und sonst nirgendwohin. An den Produktivendpunkt geschickt ist es ein unbekanntes Feld, und ein unbekanntes Feld verwirft genau die Nutzlast, die es absichern sollte.<\/p>\n\n<p>Eine Eigenschaft des Pr\u00fcfservers pr\u00e4gt den Umgang mit ihm: er meldet <strong>nur den ersten Fund<\/strong>. Drei Probleme in einem Ereignis ergeben eine Meldung, und die zweite erscheint erst, wenn die erste behoben ist. Eine frisch gebaute Nutzlast braucht deshalb mehrere Runden \u2014 genau daf\u00fcr gibt es einen Pr\u00fcfer, der alle Fehler auf einmal auflistet.<\/p>\n\n<h2>Was die Pr\u00fcfung nicht bemerkt<\/h2>\n\n<p>Mit gesetztem <code>ENFORCE_RECOMMENDATIONS<\/code> ging all das ohne eine einzige Meldung durch: ein <code>purchase<\/code> ohne <code>transaction_id<\/code>, ohne <code>value<\/code>, ohne <code>currency<\/code>, ohne <code>items<\/code>, mit leerem <code>items<\/code>-Feld und ganz ohne Parameter. Zwei Ereignisse mit derselben <code>transaction_id<\/code> in einer Anfrage gingen durch. Eine <code>user_id<\/code> mit 257 Zeichen ging durch, ein Zeichen \u00fcber dem dokumentierten H\u00f6chstwert. Ein W\u00e4hrungscode <code>EURO<\/code> ging durch, kleingeschriebenes <code>eur<\/code> ebenfalls.<\/p>\n\n<p>Nichts davon ist ein Fehler im Pr\u00fcfer; es ist die Grenze dessen, was eine Protokollpr\u00fcfung wissen kann. Ob ein Beleg eine Nummer hat, ob ein Betrag ein echter Betrag ist und ob derselbe Verkauf bereits hochgeladen wurde, sind Fragen an die Daten des Ladens, und sie m\u00fcssen beantwortet sein, bevor die Nutzlast gebaut wird.<\/p>\n\n<h2>Der Upload-Rhythmus, und was ein Wochenlauf kostet<\/h2>\n\n<p>Das 72-Stunden-Fenster macht aus dem Zeitplan eine Entwurfsentscheidung. F\u00fcr einen Uploader, der um 03:00 Uhr l\u00e4uft und ganze Tage abdeckt:<\/p>\n\n<table>\n<tr><th>Rhythmus<\/th><th>\u00c4ltester Beleg beim Upload<\/th><th>Anteil au\u00dferhalb des Fensters<\/th><\/tr>\n<tr><td>jede Nacht<\/td><td>27 h<\/td><td>0 %<\/td><\/tr>\n<tr><td>alle drei Tage<\/td><td>75 h<\/td><td>4,2 %<\/td><\/tr>\n<tr><td>w\u00f6chentlich<\/td><td>171 h<\/td><td>58,9 %<\/td><\/tr>\n<\/table>\n\n<p>Ein Wochenlauf verliert mehr als die H\u00e4lfte seiner Fracht, und er verliert sie lautlos: der Endpunkt antwortet den abgewiesenen Belegen mit genau demselben 204 wie den angenommenen. Ein Nachtlauf hat 45 Stunden Luft, und diese Luft macht eine ausgefallene Nacht \u00fcberhaupt erst wiederholbar \u2014 ein vers\u00e4umter Lauf kann am Folgetag nachgeholt werden, und alles kommt trotzdem an.<\/p>\n\n<p>Dieser Zeitpuffer spricht auch daf\u00fcr, fehlgeschlagene Uploads in einer Warteschlange aufzubewahren. Jeder noch nicht gesendete Beleg beh\u00e4lt seine eigene Frist. H\u00e4lt die Warteschlange die Belegzeit fest, kann der Uploader erkennen, welche Belege noch im Upload-Fenster liegen und welche bereits zu alt sind.<\/p>\n\n<h2>Was das nicht l\u00f6st<\/h2>\n\n<p>Drei Grenzen geh\u00f6ren klar benannt.<\/p>\n\n<p>Die Messungen oben beschreiben, was das <em>Protokoll<\/em> annimmt. Ob ein zusammengef\u00fchrter Kauf danach in einem bestimmten Bericht auftaucht, h\u00e4ngt an der Berichtsidentit\u00e4t der Property und daran, ob auf der Website \u00fcberhaupt eine <code>user_id<\/code> erhoben wird \u2014 eine Frage der GA4-Einstellungen, keine des Protokolls, und nichts davon wurde hier gemessen.<\/p>\n\n<p>R\u00fcckgaben und Stornos haben auf diesem Weg keinen bequemen Platz. Ein <code>refund<\/code>-Ereignis gibt es, aber eine R\u00fcckgabe, die vier Tage nach dem Verkauf eintrifft, f\u00e4llt genauso aus dem Fenster wie ein versp\u00e4teter Beleg.<\/p>\n\n<p>Was das Fenster ausschlie\u00dft, l\u00e4sst ein anderer Weg weiterhin zu. Belege, die \u00e4lter als drei Tage sind, R\u00fcckgaben, die eine Woche sp\u00e4ter eintreffen, und jedes Nacharbeiten von Vergangenheit geh\u00f6ren gar nicht in dieses Protokoll: Bei einer Property mit eingeschaltetem BigQuery-Export geh\u00f6ren sie in einen Join gegen die rohen <code>events_*<\/code>-Tabellen, \u00fcber dieselben Kennungen, die dieser Artikel aufbaut. Dieser Weg kennt keine Zeitgrenze. Er ist auch der Grund, warum die 72-Stunden-Kante eine Frage des Zeitplans bleibt und kein dauerhafter Verlust \u2014 solange die Belege selbst aufgehoben werden.<\/p>\n\n<p>Und das Protokoll selbst ist fertig. Google hat das Measurement Protocol im Juni 2026 in einen endg\u00fcltigen Zustand versetzt \u2014 keine Abschaltung, keine neuen Funktionen \u2014 und verweist neue Server-zu-Server-Anbindungen auf die Data Manager API. F\u00fcr einen Laden, dessen Kasse bereits Belege exportiert, ist das kein Notfall: die hier gemessenen Grenzen sind die Grenzen, die bleiben.<\/p>\n\n<div class=\"lw-faq\">\n<h2>Fragen und Antworten<\/h2>\n\n<h3>Was geschieht, wenn der Pfeffer gewechselt werden muss, etwa nach einem Leck?<\/h3>\n<p>Jeder Hash \u00e4ndert sich, und damit rei\u00dft die Personengeschichte in GA4 an diesem Tag ab. Betroffen sind drei Stellen:<\/p>\n<ol>\n<li>Die user_id. Website und Uploader rechnen mit demselben Pfeffer. Wechselt nur eine Seite, entstehen genau die zwei einander fremden Personen, vor denen der Artikel warnt. Beide m\u00fcssen deshalb im selben Moment umgestellt werden.<\/li>\n<li>Die Zuordnungstabelle. Ihre Schl\u00fcssel sind alte Hashes, und ein HMAC l\u00e4sst sich nicht zur\u00fcckrechnen. Neu aufbauen l\u00e4sst sich die Tabelle nur, indem jede Kartennummer aus dem Kundenstamm mit altem und neuem Pfeffer gehasht und der alte Schl\u00fcssel durch den neuen ersetzt wird.<\/li>\n<li>Die abgeleiteten client_ids. Auch sie stammen aus dem Hash. Jede Karte ohne Paarung erscheint ab dem Wechsel als neue Identit\u00e4t, und die Belege davor und danach wachsen nicht zusammen.<\/li>\n<\/ol>\n<p>Ein Pfefferwechsel ist damit eher eine Datenmigration als eine gew\u00f6hnliche Wartungsaufgabe. Der Pfeffer liegt \u00fcberall dort, wo der Hash berechnet wird, also bei der Website und beim Uploader. Beide Stellen m\u00fcssen ihn gleich gut sch\u00fctzen.<\/p>\n\n<h3>Warum ein Hash mit Schl\u00fcssel und nicht einfach SHA-256 der Kartennummer?<\/h3>\n<p>Weil es zu wenige Kartennummern gibt. Bei einem angenommenen Format aus festem K\u00fcrzel und acht Ziffern, wie im Beispiel LW 4471 9930, sind nur 100 Millionen Nummern m\u00f6glich; sie alle ohne Schl\u00fcssel zu hashen und jeden Hash seiner Nummer zuzuordnen, dauert auf gew\u00f6hnlicher Hardware nicht lange. Der Pfeffer macht diese Rechnung unm\u00f6glich, solange er geheim bleibt.<\/p>\n\n<h3>Woran erkennt ein Uploader einen gescheiterten Upload, wenn der Produktivendpunkt immer mit 204 antwortet?<\/h3>\n<p>Nur an der Verbindung selbst: Kommt keine Antwort, bricht die Verbindung ab oder erscheint ein anderer Status als 204, geh\u00f6rt der Beleg in die Warteschlange. Alles, was den Inhalt betrifft, muss vor dem Senden entschieden sein, weil der Endpunkt es danach nicht meldet. Vier Pr\u00fcfungen geh\u00f6ren deshalb in den Uploader selbst:<\/p>\n<ul>\n<li>Die Belegzeit gegen die eigene Uhr: Was \u00e4lter als 72 Stunden ist, geh\u00f6rt nicht mehr in dieses Protokoll, sondern auf den Weg \u00fcber BigQuery; Vordatiertes bleibt zur\u00fcck.<\/li>\n<li>Die Formate: Preise als Zahl mit Dezimalpunkt, die client_id als &lt;Zahl&gt;.&lt;Zahl&gt;, der Zeitstempel in Mikrosekunden.<\/li>\n<li>Die Angaben, deren Fehlen der Pr\u00fcfserver durchgehen l\u00e4sst: transaction_id, value, currency und items.<\/li>\n<li>Jede neue Form der Nutzlast gegen den Pr\u00fcfendpunkt, und zwar so oft, bis keine Meldung mehr kommt, weil er nur den ersten Fund nennt.<\/li>\n<\/ul>\n<p>Ob die Belege danach wirklich angekommen sind, l\u00e4sst sich nur in den Daten der Property selbst nachsehen, bei eingeschaltetem BigQuery-Export etwa \u00fcber die Belegnummern.<\/p>\n<\/div>\n\n<div class=\"lw-quellen\">\n<h2>Quellen<\/h2>\n<ul>\n<li><a href=\"https:\/\/developers.google.com\/analytics\/devguides\/collection\/protocol\/ga4\/reference\" target=\"_blank\" rel=\"noopener noreferrer\">Measurement Protocol (Google Analytics 4) \u2014 Reference<\/a><\/li>\n<li><a href=\"https:\/\/developers.google.com\/analytics\/devguides\/collection\/protocol\/ga4\/validating-events\" target=\"_blank\" rel=\"noopener noreferrer\">Validate events for Measurement Protocol<\/a><\/li>\n<li><a href=\"https:\/\/support.google.com\/analytics\/answer\/9213390\" target=\"_blank\" rel=\"noopener noreferrer\">Reporting identity in Google Analytics<\/a><\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Eine Kasse liefert Beleg, Kartennummer und Zeitstempel. GA4 kennt Browser. Dieses Tutorial baut die Br\u00fccke dazwischen mit dem Measurement Protocol: die Kartennummer hashen, die Paarung im Online-Konto festhalten, f\u00fcr alle \u00fcbrigen eine client_id ableiten und einen purchase senden, der ankommt. Jede Grenze darin wurde am 9. September 2026 gemessen, auch die, die der Server in seiner Fehlermeldung falsch angibt.<\/p>\n","protected":false},"author":1,"featured_media":19916,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[115],"tags":[92023,92091,92235],"class_list":["post-17808","post","type-post","status-publish","format-standard","hentry","category-tutorials-de","tag-conversion-tracking-de","tag-google-analytics-4-de","tag-tutorial-de"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel | Lukas Wojcik<\/title>\n<meta name=\"description\" content=\"Ladenk\u00e4ufe nach GA4 per Measurement Protocol: die Kundenkarte als Schl\u00fcssel, das gemessene 72-Stunden-Fenster und eine vor dem Senden gepr\u00fcfte Nutzlast.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"Ladenk\u00e4ufe nach GA4 per Measurement Protocol: die Kundenkarte als Schl\u00fcssel, das gemessene 72-Stunden-Fenster und eine vor dem Senden gepr\u00fcfte Nutzlast.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-19T11:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-17808-ga4-measurement-protocol-ladenkaeufe-gk.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Lukas Wojcik\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lukas Wojcik\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/\"},\"author\":{\"name\":\"Lukas Wojcik\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"headline\":\"Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel\",\"datePublished\":\"2026-10-19T11:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/\"},\"wordCount\":2391,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/hero-17808-ga4-measurement-protocol-ladenkaeufe-gk.png\",\"keywords\":[\"Conversion Tracking\",\"Google Analytics 4\",\"Tutorial\"],\"articleSection\":[\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/\",\"name\":\"Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/hero-17808-ga4-measurement-protocol-ladenkaeufe-gk.png\",\"datePublished\":\"2026-10-19T11:00:00+00:00\",\"description\":\"Ladenk\u00e4ufe nach GA4 per Measurement Protocol: die Kundenkarte als Schl\u00fcssel, das gemessene 72-Stunden-Fenster und eine vor dem Senden gepr\u00fcfte Nutzlast.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/hero-17808-ga4-measurement-protocol-ladenkaeufe-gk.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/hero-17808-ga4-measurement-protocol-ladenkaeufe-gk.png\",\"width\":1200,\"height\":630,\"caption\":\"Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/digital-analytics-de\\\/tutorials-de\\\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel | Lukas Wojcik","description":"Ladenk\u00e4ufe nach GA4 per Measurement Protocol: die Kundenkarte als Schl\u00fcssel, das gemessene 72-Stunden-Fenster und eine vor dem Senden gepr\u00fcfte Nutzlast.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/","og_locale":"en_US","og_type":"article","og_title":"Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel | Lukas Wojcik","og_description":"Ladenk\u00e4ufe nach GA4 per Measurement Protocol: die Kundenkarte als Schl\u00fcssel, das gemessene 72-Stunden-Fenster und eine vor dem Senden gepr\u00fcfte Nutzlast.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/","og_site_name":"Lukas Wojcik - Blog","article_published_time":"2026-10-19T11:00:00+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-17808-ga4-measurement-protocol-ladenkaeufe-gk.png","type":"image\/png"}],"author":"Lukas Wojcik","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Lukas Wojcik","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/#article","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/"},"author":{"name":"Lukas Wojcik","@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"headline":"Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel","datePublished":"2026-10-19T11:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/"},"wordCount":2391,"commentCount":0,"publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-17808-ga4-measurement-protocol-ladenkaeufe-gk.png","keywords":["Conversion Tracking","Google Analytics 4","Tutorial"],"articleSection":["Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/","url":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/","name":"Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/#primaryimage"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-17808-ga4-measurement-protocol-ladenkaeufe-gk.png","datePublished":"2026-10-19T11:00:00+00:00","description":"Ladenk\u00e4ufe nach GA4 per Measurement Protocol: die Kundenkarte als Schl\u00fcssel, das gemessene 72-Stunden-Fenster und eine vor dem Senden gepr\u00fcfte Nutzlast.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/#primaryimage","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-17808-ga4-measurement-protocol-ladenkaeufe-gk.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-17808-ga4-measurement-protocol-ladenkaeufe-gk.png","width":1200,"height":630,"caption":"Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel"},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/digital-analytics-de\/tutorials-de\/ga4-measurement-protocol-ladenkaeufe-kundenkarte\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Ladenk\u00e4ufe per Measurement Protocol nach GA4 hochladen: Die Kundenkartennummer als Schl\u00fcssel"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/17808","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=17808"}],"version-history":[{"count":3,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/17808\/revisions"}],"predecessor-version":[{"id":22729,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/17808\/revisions\/22729"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/19916"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=17808"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/categories?post=17808"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=17808"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}