Tracking & Server-Side Events
Tracking keys, the installer payloads, and the two ways a system posts an outcome it already knows about.
Two different jobs live here. Tracking keys and installers are how the
browser tracker gets onto a site. POST /api/v1/events is how a system that
already knows an outcome happened — an ERP, a POS, a back office — tells Atribu
without a browser at all.
Tracking keys
POST /api/v1/tracking/keys
GET /api/v1/tracking/keysThe POST is idempotent by natural key: a profile that already has an
active key gets that key back rather than a second one. So an agent can call it
unconditionally on every run without accumulating keys.
Installer payloads
GET /api/v1/tracking/snippet
GET /api/v1/tracking/installers/gtm
GET /api/v1/tracking/installers/shopify-pixelsnippet returns the raw <script> (optionally bundled with the Meta Pixel);
the two installers return the payload a Google Tag Manager container or a
Shopify Web Pixel expects. An agent that can edit the site puts the snippet in;
one that cannot hands the payload to the person who can.
The by-hand versions of all three, with screenshots, are in Install the tracker.
Server-side outcome events
POST /api/v1/eventsThis is how a dealer's ERP posts a closed sale. Two rules matter more than the schema:
The idempotency key is required whenever the event carries money
Without one it defaults to a fresh UUID, and a retried sale becomes a second conversion. A payment posted twice is revenue counted twice, and every ROAS on the profile is wrong afterwards.
event_type is free text. What gives a name meaning is a
conversion definition that lists it — there is no fixed
sale_closed type to look up. Post checkout_completed and then define what it
means; the definition is what decides whether it counts as cash, and whether it
is attribution-eligible.
One customer paying for several people — subject_ref
A clinic often bills several patients (children, dependants) to one customer:
the parent whose contact carried the ad conversation. By default Atribu counts
one first payment per customer, so a second patient's first treatment would
read as a repeat payment — not a new customer, not a Purchase at Meta, and
credited only to the ad that acquired the parent.
Send properties.subject_ref — your own opaque id for who the payment is
for — and each distinct subject_ref under one customer gets its own first
payment. A payment without it behaves exactly as before.
Accepted shapes: a canonical UUID (8-4-4-4-12 hex — your internal patient
uuid is ideal), or a short token of up to 32 letters, digits, _ and -.
Turn it on without inflating new customers. A subject counts as new only if
every earlier payment of that customer carries a different subject_ref; an
earlier untagged payment makes it a returning one. So before you start sending
subject_ref on new payments, re-send the customer's historical payments with
the same idempotency key plus their subject_ref — Atribu re-derives the
first-payment flags on that update. A retry that omits subject_ref removes
it from the stored payment again.
Two consequences of that rule, both deliberate:
- A household with earlier payments from an integration keeps the
per-customer rule. Payments that arrive from MercadoPago, Webpay, a CSV
import or Shopify cannot carry a
subject_ref, so they cannot be backfilled; a later patient of that household is never counted as a new customer. (At the time this shipped, no clinic customer had such a mixed history.) - An untagged payment after a tagged one is never a first payment, even within the 7 days that normally group a deposit with its balance — it may be the same patient again. Tag both payments to keep them grouped.
{
"event_name": "payment_received",
"idempotency_key": "invoice_20931",
"properties": { "value": 45000, "currency": "CLP", "subject_ref": "3f6c1e2a-8b7d-4c1e-9a55-0e2b7d9c4f11" },
"user_traits": { "phone": "+56911112222" }
}subject_ref is never personal data
Atribu rejects with 400 invalid_parameter a value that contains an @, 7 or
more digits in a row once separators are removed (a RUT/RUN with or without
check digit, even split by a letter, a phone number, a compact date), a date,
a name, or a 32/40/64 character hex digest. Those checks catch mistakes; they
cannot recognise every name (JuanPerez) or a hashed or encoded personal
identifier, so subject_ref must be an opaque id — that is your
obligation under the data-processing agreement. Atribu uses subject_ref only
to decide first payments and count new customers — it is never sent to Meta,
Google or TikTok, no API returns it, and it is erased with the customer.
POST /api/v1/payments/webpay is the same idea for Transbank Webpay, which has
no OAuth connection to authorize: the payment is reported directly. See
Webpay.
A CRM connection is not required
Readiness's crm_or_outcome_source_connected step is
satisfied by outcome events arriving on this route. A system that posts its own
outcomes needs no CRM and no browser.
| Method | Path | What it does |
|---|---|---|
POST | /api/v1/events | Ingest a server-side outcome event (e.g. a closed sale) |
GET | /api/v1/payments | List payments, with the merchant actions applied to each |
PUT | /api/v1/payments/{payment_id}/installment | Mark (or unmark) a payment as a later installment |
PUT | /api/v1/payments/{payment_id}/payer | Assign who paid a payment |
POST | /api/v1/payments/{payment_id}/refund | Record a refund made outside the provider |
DELETE | /api/v1/payments/{payment_id}/refund | Undo a manually recorded refund |
POST | /api/v1/payments/webpay | Report a Webpay (Transbank) payment |
GET | /api/v1/tracking/custom-domains | List the profile's first-party tracking domains |
POST | /api/v1/tracking/custom-domains | Register a first-party tracking domain |
DELETE | /api/v1/tracking/custom-domains/{id} | Remove a first-party tracking domain |
POST | /api/v1/tracking/custom-domains/{id}/verify | Re-check a domain's DNS and store the verdict |
GET | /api/v1/tracking/events | The raw enriched-event feed |
GET | /api/v1/tracking/installers/gtm | Get the Google Tag Manager installer payload |
GET | /api/v1/tracking/installers/shopify-pixel | Get the Shopify Web Pixel installer payload |
GET | /api/v1/tracking/keys | List the profile's active tracking keys |
POST | /api/v1/tracking/keys | Issue a tracking key (idempotent) |
DELETE | /api/v1/tracking/keys/{id} | Revoke a tracking key |
GET | /api/v1/tracking/ops | Warehouse diagnostics for the profile |
GET | /api/v1/tracking/origin | Where this profile's tracker collects |
GET | /api/v1/tracking/quality | Is this profile's tracking healthy? |
GET | /api/v1/tracking/replay-status | The profile's most recent attribution replay |
GET | /api/v1/tracking/settings | Read the profile's tracker and enrichment settings |
PATCH | /api/v1/tracking/settings | Update the profile's tracker and enrichment settings |
GET | /api/v1/tracking/snippet | Get the raw tracker snippet (with optional Meta Pixel bundle) |
GET | /api/v1/tracking/verification | The latest install-verification attempt |
POST | /api/v1/tracking/verification | Start an install verification |
Generated from openapi.json. The full request and response schema for every operation is in the OpenAPI document.