Atribu
API Reference

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

Endpoint
POST /api/v1/tracking/keys
GET  /api/v1/tracking/keys

The 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

Endpoints
GET /api/v1/tracking/snippet
GET /api/v1/tracking/installers/gtm
GET /api/v1/tracking/installers/shopify-pixel

snippet 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

Endpoint
POST /api/v1/events

This 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.
A second patient's first payment
{
  "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.

Retracting an event you sent — POST /api/v1/events/retractions

A resend cannot correct a wrong row: Atribu never overwrites a customer it already resolved with "none". When a backfill or a bug ingested an event that should not exist (a payment credited to the wrong contact, say), retract it by the key you sent it with:

POST /api/v1/events/retractions
{ "idempotency_key": "invoice_20931", "reason": "Backfill credited the wrong contact" }

One transaction deletes the event, the conversions extracted from it and their attribution, recomputes the first-payment flag of the customer's other payments (a second payment can become the first), queues the profile's attribution recompute and writes an audit record naming your key and reason. The answer is counts only. It is idempotent: the same key again answers 200 with status: "already_retracted" and zero counts.

Not after an export

Once a conversion export exists for the event, in any status, the retraction is refused with 409 event_already_exported and nothing is deleted: an ad platform may already have seen it. A WhatsApp or Instagram conversation that recorded the event as its lead refuses it too (409 event_linked_to_conversation).

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.

MethodPathWhat it does
POST/api/v1/eventsIngest a server-side outcome event (e.g. a closed sale)
POST/api/v1/events/retractionsRetract an event you ingested
GET/api/v1/paymentsList payments, with the merchant actions applied to each
PUT/api/v1/payments/{payment_id}/installmentMark (or unmark) a payment as a later installment
PUT/api/v1/payments/{payment_id}/payerAssign who paid a payment
POST/api/v1/payments/{payment_id}/refundRecord a refund made outside the provider
DELETE/api/v1/payments/{payment_id}/refundUndo a manually recorded refund
GET/api/v1/payments/linkageHow each sale reached an ad («Cómo llega la plata»)
POST/api/v1/payments/webpayReport a Webpay (Transbank) payment
GET/api/v1/tracking/custom-domainsList the profile's first-party tracking domains
POST/api/v1/tracking/custom-domainsRegister 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}/verifyRe-check a domain's DNS and store the verdict
GET/api/v1/tracking/eventsThe raw enriched-event feed
GET/api/v1/tracking/installers/gtmGet the Google Tag Manager installer payload
GET/api/v1/tracking/installers/shopify-pixelGet the Shopify Web Pixel installer payload
GET/api/v1/tracking/keysList the profile's active tracking keys
POST/api/v1/tracking/keysIssue a tracking key (idempotent)
DELETE/api/v1/tracking/keys/{id}Revoke a tracking key
GET/api/v1/tracking/opsWarehouse diagnostics for the profile
GET/api/v1/tracking/originWhere this profile's tracker collects
GET/api/v1/tracking/qualityIs this profile's tracking healthy?
GET/api/v1/tracking/replay-statusThe profile's most recent attribution replay
GET/api/v1/tracking/settingsRead the profile's tracker and enrichment settings
PATCH/api/v1/tracking/settingsUpdate the profile's tracker and enrichment settings
GET/api/v1/tracking/snippetGet the raw tracker snippet (with optional Meta Pixel bundle)
GET/api/v1/tracking/verificationThe latest install-verification attempt
POST/api/v1/tracking/verificationStart an install verification

Generated from openapi.json. The full request and response schema for every operation is in the OpenAPI document.

Next steps

On this page