# Oriva > Server-side, first-party conversion tracking and attribution. A tracking code and a server API receive clicks and conversions, Oriva deduplicates them and sends them back to Meta, TikTok, Google Ads, ChatGPT Ads and affiliate networks. Full technical reference, also as a web page: https://www.orivaforge.com/en/docs Everything Oriva accepts, down to the field: the tracking code commands, the server API, the list of events and what each one becomes at each platform. To connect a platform step by step, use the [Integrations pages](https://www.orivaforge.com/en/integrations), or [Shopify](https://www.orivaforge.com/en/integrations/shopify) if that is your store. If you code with an AI assistant, give it this page or its text version, [/en/llms.txt](https://www.orivaforge.com/en/llms.txt). ## Two ways to send an event | | Tracking code (browser) | Server API | | --- | --- | --- | | Key | `pk_`, public, in the page | `sk_`, secret, on your server | | `source` | `browser` | `server` | | Triggers an affiliate postback | No | Yes | | `ip` and `user_agent` | Read by Oriva from the connection | Yours to provide (the visitor's) | A `pk_` key can only send `browser` events, an `sk_` key only `server` events. The affiliate postback only fires on a server sale: a public key is visible to everyone, so it must not be able to create a commission. ## The tracking code You'll find it, already filled in with your subdomain and key, in the “Tracking code” step of your workspace. It loads `oriva.js` from your tracking subdomain (`t.`) and exposes one function, `oriva(command, …)`. You can call it before the script has loaded: calls are queued and replayed. ```html ``` ### oriva('init', options) Already in the code you were given. Sends a `page_view` on load. | Option | What it does | | --- | --- | | `key` | Your `pk_` key. Required. | | `host` | The tracking subdomain. Defaults to the one `oriva.js` is loaded from. | | `page_view` | `false` to skip the automatic `page_view`. | | `consent_wait` | Maximum time, in milliseconds, that events wait for your banner to give consent back when the page loads. `1500` by default, `0` to not wait. | ### oriva('consent', { ads }) **Oriva starts without ad consent.** Until you have called `oriva('consent', { ads: true })`, events are still sent, but without a visitor cookie, click id, email or phone, and **nothing is sent to Meta, TikTok, Google Ads or ChatGPT Ads**. `oriva('consent', { ads: false })` withdraws consent and clears the visitor cookie. Consent is only kept in memory: on every page, your banner has to give it back. To leave it time to do so, events wait for its signal for up to 1.5 seconds when the page loads (the `consent_wait` option), then go out without consent if it has not come. They go out at once if the visitor leaves the page. [Examples for Axeptio, Didomi, Cookiebot, tarteaucitron, Complianz and CookieYes](https://www.orivaforge.com/en/integrations/any-website#consent). ### oriva('identify', data) Remembers the visitor's email, phone or other data for every following event on the page. They are normalized and hashed with SHA-256 **in the browser**, before they leave. Call it before a `lead` or a `purchase`, for example when the form is submitted. ```js oriva('identify', { email: 'jane@example.com', phone: '06 12 34 56 78' }); ``` ### oriva('track', name, data) Sends an event. ```js oriva('track', 'purchase', { event_id: 'commande-1001', value: 49.9, currency: 'EUR', order_id: '1001', content_ids: ['sku-42'], }); ``` - `event_id`: your stable reference. Without it, the code generates a random one, and the same sale also sent by your server will be counted twice at the platforms. - `value`: in units, not in cents (`49.9` means 49.90). - `currency`: 3-letter ISO code. When absent, Oriva uses your domain's currency. - `user_data`: like `identify`, but for this event only. - **Any other key goes into `custom_data`** (see [what is forwarded to the platforms](https://www.orivaforge.com/en/docs#custom-data)). On `purchase`, refreshing the thank-you page sends the same `event_id` again: the sale is not counted twice. ### What the code captures on its own Only with ad consent, when the page loads: - the click ids in the URL: `fbclid`, `ttclid`, `gclid`, `gbraid`, `wbraid`, `msclkid`, `aff_click_id`, `oppref`; - the `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term` parameters; - `subid`, `s1`, `s2`, `s3`, `s4`, `s5`; - the cookies of the pixels already on the page: `_fbc`, `_fbp` (Meta), `_ttp`, `ttclid` (TikTok), `__oppref`, `__obref` (OpenAI). You don't have to do anything for that. ## The server API ```http POST https://t./api/v1/collect X-Oriva-Key: sk_… Content-Type: application/json ``` One event per request, with the [fields below](https://www.orivaforge.com/en/docs#fields). [Full example and good practices](https://www.orivaforge.com/en/integrations/server-api). Your secret key stays on your server, in an environment variable. ## The fields of an event Valid for the server API. The tracking code fills in `event_id`, `source`, `occurred_at`, `url`, `referrer`, `consent`, `visitor_id` and the click ids itself. | Field | Required | Notes | | --- | --- | --- | | `event_id` | Yes | Text, 128 characters at most. Unique per domain: a second request with the same `event_id` is accepted but ignored. | | `name` | Yes | One of the [events](https://www.orivaforge.com/en/docs#events), or `custom:`. | | `source` | No | `browser` or `server`, `browser` by default. Must match the key. | | `occurred_at` | No | ISO 8601 date with time zone. Now by default. At most 7 days in the past and 5 minutes in the future. | | `value` | No | Positive number, in units and not in cents: `49.9` means 49.90. | | `currency` | No | 3-letter ISO code (`EUR`). When absent, Oriva uses your domain's currency. | | `consent` | No | `{ "ads": true }` or `{ "ads": false }`. **When absent, Oriva treats the visitor as having refused.** | | `visitor_id` | No | UUID: the visitor's `or_vid` cookie, to tie the sale to their visit. | | `user_data` | No | Object: `email`, `phone`, `first_name`, `last_name`, `zip`, `external_id`. See [Personal data](https://www.orivaforge.com/en/docs#personal-data). | | `custom_data` | No | Free-form object, 8 KB at most. See [custom_data](https://www.orivaforge.com/en/docs#custom-data). | | `click_ids` | No | Object: `fbclid`, `ttclid`, `gclid`, `gbraid`, `wbraid`, `msclkid`, `aff_click_id`, `fbc`, `fbp`, `ttp`, `oppref`, `obref`. | | `utm` | No | Object: `source`, `medium`, `campaign`, `content`, `term`. | | `sub_ids` | No | Object: `subid`, `s1`, `s2`, `s3`, `s4`, `s5`. | | `url`, `referrer` | No | Absolute URL. Filled in by the tracking code. | | `ip`, `user_agent` | No | **Server only**: the visitor's, never your server's. Rejected on a `browser` event. | An empty string counts as absent. Unknown fields are ignored. ## Events | Event | Sent to the platforms | What it is | | --- | --- | --- | | `page_view` | No | A page view. Sent automatically by the tracking code. | | `view_content` | No | A product page or other content viewed. | | `add_to_cart` | If you turn it on for the destination (Meta, TikTok, ChatGPT Ads) | An add to cart. | | `initiate_checkout` | If you turn it on for the destination (Meta, TikTok, ChatGPT Ads) | The start of checkout. | | `lead` | Yes | A contact: form, quote request, list signup. | | `purchase` | Yes | A purchase. | | `complete_registration` | Yes | An account creation. | | `subscribe` | Yes | A paid subscription started. | | `start_trial` | Yes | A trial started. | | `custom:` | Yes | Any other event that matters to you: an outbound click, a download. | The first four are for your Oriva dashboard. **They are not sent to any platform.** The others become conversions, under these names: | Oriva event | Meta and TikTok | ChatGPT Ads | | --- | --- | --- | | `lead` | `Lead` | `lead_created` | | `purchase` | `Purchase` | `order_created` | | `complete_registration` | `CompleteRegistration` | `registration_completed` | | `subscribe` | `Subscribe` | `subscription_created` | | `start_trial` | `StartTrial` | `trial_started` | | `add_to_cart` | `AddToCart` | `items_added` | | `initiate_checkout` | `InitiateCheckout` | `checkout_started` | | `custom:` | `` | ``, as a custom event | - **Google Ads**: a destination follows a single event, the one you choose when you create it (any of those that go to the platforms). To follow a lead and a purchase, create two destinations. - **Affiliate postback**: every event that goes to the platforms, or a single one if you pick one, and **only if they come from your server**. - **`custom:`**: `` is 1 to 64 characters long, made of lowercase letters, digits, `_` and `-`, and starts with a letter or a digit (`custom:clic_partenaire`). For ChatGPT Ads, first create a conversion event with the exact name (`clic_partenaire`) in your ads manager: without it, OpenAI accepts the request but counts nothing. ## custom_data Oriva stores everything you put there (up to 8 KB), but only forwards part of it: - **Meta and TikTok** only receive these keys: `content_ids`, `content_id`, `content_name`, `content_type`, `content_category`, `contents`, `num_items`, `order_id`, `predicted_ltv`, `search_string`, `status`, `delivery_category`, `quantity`, `query`. Any other key stays with Oriva. - **ChatGPT Ads** receives the same ones, plus `plan_id` on `subscribe` and `start_trial`. - **Google Ads** does not read `custom_data`. - **Postback**: `payout` feeds the `{payout}` variable of your URL. [All the variables](https://www.orivaforge.com/en/integrations/affiliate-postback). `order_id` is also used by the Health tab to spot the same order sent by both the browser and the server. ## Personal data Fields accepted in `user_data`: `email`, `phone`, `first_name`, `last_name`, `zip`, `external_id`. Oriva never keeps these values in clear text: they are normalized, then hashed with SHA-256 before they are written. You can send them in clear text or already hashed (64 hexadecimal characters): an already hashed value is kept as is, **provided you normalized it the same way**: - everything: leading and trailing spaces removed, lowercase; - phone: digits only, with the country code, without `+` (`33612345678`). A number that starts with `0` and has no country code is read as French; - postal code: no spaces or hyphens, 5 characters at most; - `external_id`: case kept. Without ad consent, `user_data` is not stored at all. ## Responses and errors **Success**: `202`. ```json { "ok": true, "event_id": "commande-1001", "deduped": false } ``` `deduped: true`: this `event_id` was already known, the event is ignored. `buffered: true`: Oriva set it aside during an incident and will write it as soon as possible. In both cases, don't send it again. **Errors**: always `{ "ok": false, "error": "…" }`. | Status | error | What it means | | --- | --- | --- | | `400` | `invalid_payload` | A field is invalid. `issues` gives the path and the reason for each one. | | `400` | `invalid_payload:ip_and_user_agent_are_server_only` | `ip` or `user_agent` sent on a `browser` event. | | `401` | `missing_api_key` | No key, neither in the `X-Oriva-Key` header nor in `?k=`. | | `401` | `invalid_api_key:malformed` | The key does not have the right format. | | `401` | `invalid_api_key:unknown` | Unknown key. | | `401` | `invalid_api_key:revoked` | Revoked key. | | `401` | `key_scope:public_key_cannot_send_server_events` | A `pk_` key sending a `server` event. | | `401` | `key_scope:secret_key_cannot_send_browser_events` | An `sk_` key sending a `browser` event. | | `403` | `origin_not_allowed` | Call from a browser, on a site other than your domain. | | `429` | `rate_limited` | Too many calls. `retry_after` and the `Retry-After` header tell you how many seconds to wait. | Only a `429` can be retried as is. A `400` or a `401` will fail the same way until the request or the key changes. ## The read API Pro and Agency plans. An `rk_` token, created in the app (account menu, **API**), to pull your numbers into your own tools: a spreadsheet, a client report, an alert. It can't change anything. To **send** events, use [the server API](https://www.orivaforge.com/en/docs#server-api) and an `sk_` key, on every plan. ```bash curl -H "Authorization: Bearer rk_…" \ https://www.orivaforge.com/api/v1/read/properties ``` All routes are `GET` and answer in JSON. Amounts in cents, dates in ISO (UTC). `:id` is a domain's id, returned by the first route. No code needed: the **API** page of the app gives you a ready-to-paste Google Sheets script that fills a tab every morning, keeps a history and can warn you on Slack when a destination starts failing. ### Your domains `GET /api/v1/read/properties`: The account's domains, with the id to pass to the other routes. ```json [ { "id": "0192f3a4-7b1c-7d2e-9f10-2a3b4c5d6e7f", "domain": "boutique.fr", "name": "boutique.fr", "status": "active" } ] ``` ### Health `GET /api/v1/read/properties/:id/health`: The same state as the Health tab: each destination, the checks to fix, collection. ```json { "diagnostics": [ { "code": "DESTINATION_FAILING", "severity": "high", "message": "…" } ], "destinations": [ { "name": "Meta", "kind": "meta_capi", "displayStatus": "failing", "sent": 41, "failed": 5, "lastFailureError": "HTTP 400: OAuthException code 190 …", "deliveredRate": 0.89 } ], "pulse": { "lastEventReceivedAt": "2026-09-27T06:58:12.000Z", "browserEvents24h": 1320 } } ``` ### Key figures `GET /api/v1/read/properties/:id/summary`: Conversions, revenue, spend, ROAS and the gap with the platforms over the period. - from, to: ISO dates (2026-09-01), last 30 days by default, 90 days at most. ```json { "conversions": 128, "purchases": 97, "revenueByCurrency": [ { "currency": "EUR", "revenueCents": 684250, "spendCents": 210000, "roas": 3.26 } ], "platformGap": { "total": -12, "orivaConversions": 88, "platformConversions": 100, "ratio": -0.12, "matchedRows": 14 } } ``` ### Delivery log `GET /api/v1/read/properties/:id/conversions`: Recent conversions and their status at each destination. - limit: 50 by default, 200 at most. Newest first. ```json [ { "id": "0192f3b8-…", "occurredAt": "2026-09-27T06:41:03.000Z", "eventName": "purchase", "valueCents": 5990, "currency": "EUR", "campaign": "rentree-2026", "orderId": "1042", "deliveries": [ { "destinationName": "Meta", "destinationKind": "meta_capi", "status": "sent", "error": null } ] } ] ``` ### Click to conversion delay `GET /api/v1/read/properties/:id/attribution-delays`: Per campaign, the median delay and the one by which 8 conversions in 10 had arrived. - from, to: same as for the key figures. ```json [ { "campaign": "rentree-2026", "conversions": 42, "medianDelayMs": 5400000, "p80DelayMs": 172800000 } ] ``` ### Limits and errors - 60 calls per minute and per token. - 90 days at most per call, 30 by default. - 200 conversions at most per call. | Status | error | What it means | | --- | --- | --- | | `401` | `missing_token` | No `Authorization: Bearer ` header, or it is malformed. | | `401` | `invalid_token` | Unknown or revoked token. | | `403` | `plan_restricted` | The account is no longer on a Pro or Agency plan. | | `404` | `not_found` | This domain doesn't exist, or doesn't belong to the token's account. | | `429` | `rate_limited` | More than 60 calls per minute. The `Retry-After` header says in how many seconds to retry. | Errors use the `{ "error": "…" }` format. Only a `429` can be retried as is. ## Further reading - [Integrations](https://www.orivaforge.com/en/integrations): connect each platform and each kind of site. - [Shopify](https://www.orivaforge.com/en/integrations/shopify) and [the guide to the Shopify cutoff](https://www.orivaforge.com/en/guides/shopify-additional-scripts-thank-you-page). - [Double counting between the pixel and the server API](https://www.orivaforge.com/en/guides/pixel-and-server-api-deduplication). - [The text version of this page](https://www.orivaforge.com/en/llms.txt), for your AI assistant. ## Pages - [Oriva | Server-side conversion tracking for Meta, TikTok and Google Ads](https://www.orivaforge.com/en) - [Shopify: track purchases after August 26 | Oriva](https://www.orivaforge.com/en/integrations/shopify) - [Shopify removed Additional Scripts: is your tracking broken?](https://www.orivaforge.com/en/guides/shopify-additional-scripts-thank-you-page) - [Oriva documentation: tracking code, server API, events](https://www.orivaforge.com/en/docs) - [Privacy policy | Oriva](https://www.orivaforge.com/en/privacy) - [Contact Oriva: support, partnerships, privacy requests](https://www.orivaforge.com/en/contact)