> For the complete documentation index, see [llms.txt](https://docs.onside.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.onside.io/api/store-attribution-webhooks.md).

# Publisher Conversion Webhooks

Receive conversion events, attribution referrers, session identifiers, and advertising context on your backend.

> **Availability.** Publisher conversion webhooks are available through your Onside account manager. Contact them to enable and configure delivery for your applications.

Onside delivers conversion events starting with the Onside Store installation milestone (`store_app_installed`), their attribution referrer, and available advertising identifiers to your backend. Your backend selects the advertising platform and forwards each event using your own connector and credentials.

## Set up webhooks

Share your HTTPS endpoint and the applications and landing routes you own with your Onside account manager. They will help configure the signing secret, event selection, traffic scope, and any additional fields.

| Setting         | Behavior                                                                                                                                        |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Endpoint        | One HTTPS URL receiving JSON POST requests. Redirects are not followed.                                                                         |
| Applications    | Only events within your authorized application and landing scope.                                                                               |
| Events          | System conversions starting with `store_app_installed` and accepted SDK/S2S business events in scope, or an explicit list of those event names. |
| Traffic scope   | `with_referrer` by default, or `all`.                                                                                                           |
| Additional data | Explicitly agreed fields only. Additional cookies and event parameters are not exported automatically.                                          |

`with_referrer` delivers events with a non-empty `referrer`.

`all` delivers all events for your application. The `referrer` may be absent for organic traffic or when Onside could not reliably attribute the event.

Delivery starts when your account manager enables the integration. Request historical delivery explicitly through your account manager. Changing a filter does not automatically resend history.

## Events

| `conversion_label`    | Meaning                                                                                                                               |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `store_app_installed` | Onside Store installation milestone. It does not mean that the publisher's app has been installed or launched.                        |
| `app_installed`       | Publisher app installation milestone reported by Onside's install flow. It does not prove that the app has launched.                  |
| `app_launched`        | App initialization observed through the Onside SDK. Requires SDK integration; do not assume that it reports every foreground opening. |
| `purchase_completed`  | A recognized purchase event, with amount, currency, and order ID when supplied.                                                       |
| Publisher event name  | An accepted SDK or S2S business event, using its agreed public name. Custom names are not restricted to the system list.              |

Install milestones describe the signals observed by Onside's install flow; they are not independent device-OS confirmation. Existing users can skip earlier funnel stages. Your backend must accept partial funnels and events arriving out of order.

Browser events such as `page_view`, `eligible_page_view`, and `install_button_click` are outside the current webhook service. Collect landing-page events through GTM as described below. Browser events may be added to the webhook service later; they are not part of this contract.

## Request body

Each request contains **one JSON object**, not an array. All examples below use synthetic identifiers.

```json
{
  "onside_event_id": "019b71ab-0000-7000-8000-000000000002",
  "conversion_label": "purchase_completed",
  "conversion_time": "2026-09-07T10:00:00Z",
  "app_id": "1234567890",
  "value": {"amount": "19.99", "currency": "USD"},
  "order_id": "example-order-42",
  "referrer": "https://ads.example/click?utm_source=meta&utm_campaign=example&fbclid=example-click",
  "sessions": {
    "web_session_id": "example-landing-1",
    "store_session_id": "example-store-1",
    "app_session_id": "example-app-installation-1"
  },
  "web_context": {
    "cookies": {
      "_fbp": "fb.1.1788773400000.1234567890",
      "_fbc": "fb.1.1788773400000.example-click"
    },
    "click_ids": {"fbclid": "example-click"}
  }
}
```

| Field              | Type                | Meaning                                                                                                                                 |
| ------------------ | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `onside_event_id`  | UUID string         | Stable Onside event ID. Retries and source reprocessing preserve it.                                                                    |
| `conversion_label` | String              | Public event name to map to your platform event.                                                                                        |
| `conversion_time`  | RFC 3339 UTC string | Original event time, not webhook delivery time.                                                                                         |
| `app_id`           | String or null      | Publisher application's Apple ID, encoded as a string. Null when no application ID applies.                                             |
| `value`            | Object or null      | `{ "amount": "19.99", "currency": "USD" }`. Decimal amount in original major currency units; ISO 4217 currency. No currency conversion. |
| `order_id`         | String or null      | Original transaction/order ID, if supplied. Never generated from an event or session ID.                                                |
| `referrer`         | String or null      | Captured referrer for the touchpoint credited to this event, including available query parameters.                                      |
| `sessions`         | Object              | The three nullable funnel identifiers described below.                                                                                  |
| `web_context`      | Object              | Captured advertising cookies and click IDs described below.                                                                             |

The nine core keys are always present. `sessions` always contains all three keys. `web_context` always contains both maps, even when they are empty. Missing values use JSON `null`; missing cookie or click-ID entries are omitted. Zero is a valid monetary amount. If a valid amount/currency pair is unavailable, `value` is null.

Arbitrary SDK/S2S parameters are not included by default. Additional fields are agreed for a specific integration need. Attribution models, confidence scores, matching rules, and internal diagnostic data are not part of the payload.

## Landing-page events with Google Tag Manager

Install your Google Tag Manager (GTM) container on the publisher landing page to collect the browser events and conversions you need and send them to your advertising systems. You can configure the container and tags yourself, or ask Onside to set them up for you.

Onside offers GTM configuration for Google Ads, Meta Ads, TikTok, Snapchat, and ExoClick. Other advertising systems are available on request.

`page_view`, `eligible_page_view`, `install_button_click`, and any browser event IDs are handled through this GTM integration. The webhook stream starts with `store_app_installed` and covers the subsequent app milestones and accepted SDK/S2S business events. Existing users can skip Store installation, so a later conversion does not require a preceding Store-install webhook.

GTM support for an advertising system does not automatically extend the webhook's `web_context` fields. The cookies and click IDs included in the webhook are listed separately below.

## Session and installation identifiers

| Field                       | Meaning                                                                                                                                                    |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sessions.web_session_id`   | Onside landing-visit ID on the acquisition route. It identifies a visit, not a user across every visit.                                                    |
| `sessions.store_session_id` | Onside Store session/installation ID on that route.                                                                                                        |
| `sessions.app_session_id`   | Publisher app's SDK installation identifier when linked to the event. It can persist across app launches; it is not a new ID for every foreground session. |

The SDK identifier often called `installation_id` is represented by **`sessions.app_session_id`** in this contract. There is no additional top-level `installation_id` field. The Store identifier is a different ID and must not be used in its place.

For `store_app_installed`, `sessions.app_session_id` is always null. The Store milestone does not supply the publisher app's installation identifier. The Store ID and web visit ID can still be present:

```json
{
  "onside_event_id": "019b71ab-0000-7000-8000-000000000004",
  "conversion_label": "store_app_installed",
  "conversion_time": "2026-09-07T09:35:00Z",
  "app_id": "1234567890",
  "value": null,
  "order_id": null,
  "referrer": "https://ads.example/click?utm_source=example-network",
  "sessions": {
    "web_session_id": "example-landing-1",
    "store_session_id": "example-store-1",
    "app_session_id": null
  },
  "web_context": {"cookies": {}, "click_ids": {}}
}
```

An SDK `app_launched` event carries the app installation ID when supplied and linked. Later SDK events can carry the same ID. S2S events need the correct SDK installation association to retain it; an event occurring after a launch does not itself guarantee a non-null ID.

Use the available shared IDs to join stages, scoped by application where applicable. One web or Store session can lead to multiple app installations. Missing IDs are not fabricated. An earlier webhook is not resent merely because a later event supplies another session ID.

## Referrer, cookies, and click IDs

`referrer` is the captured attribution value selected by Onside, not the HTTP `Referer` of the webhook or S2S request. It is not a normalized network name or a reconstruction from UTM fields. Parse it on your backend to select a destination; a referrer does not always identify exactly one advertising network.

`web_context` contains only these documented maps:

| Map         | Keys in the core profile                                             |
| ----------- | -------------------------------------------------------------------- |
| `cookies`   | `_fbp`, `_fbc`, when captured and exportable.                        |
| `click_ids` | `fbclid`, `gclid`, `gbraid`, `wbraid`, when captured and exportable. |

Cookie values and click IDs are strings. Onside does not export the entire browser cookie jar or invent cookie values when none were captured. Missing cookies do not prevent event delivery.

Referrer, cookies, and click IDs belong to the **same credited touchpoint**. For a later event, that touchpoint can differ from the initial acquisition visit represented by `sessions.web_session_id`. Do not infer that the session tuple identifies the source of every attribution field.

Snapchat, TikTok, and other additional network identifiers require an explicitly agreed extension with documented field names and collection support. They are not implied by an event's referrer or by the presence of `web_context`.

### Landing URLs and visit identifiers

Preserve the campaign and click parameters needed for attribution on the landing URL and through redirects. This webhook contract does not require replacing them with a short visit identifier.

A short identifier is sufficient only when your integration can resolve it to the original advertising identifiers and parameters. The resolver and capture path must be agreed separately; this contract does not define an automatic lookup for arbitrary publisher visit IDs. Cookies are captured in the browser context, not by appending the entire cookie jar to a URL.

Landing and redirect URL limits belong to that integration; this webhook contract does not define a universal URL-length limit. Validate the complete campaign URL through the selected landing and redirect flow. The webhook body limit below is not a landing-URL limit.

## Delivery and authentication

Onside sends UTF-8 JSON with `Content-Type: application/json` to your endpoint. Maximum request-body size is 256 KiB. The connection timeout is 3 seconds and total request timeout is 10 seconds.

```
Onside-Delivery-Id: <delivery UUID>
Onside-Webhook-Version: publisher_conversion_v1
Onside-Webhook-Timestamp: <Unix timestamp in seconds>
Onside-Webhook-Key-Id: <signing key version>
Onside-Webhook-Signature: v1=<lowercase hex HMAC-SHA256>
```

Verify the signature using your integration's signing secret. The signing input is the following prefix followed by the exact raw request-body bytes:

```
<timestamp>.<contract_version>.<delivery_id>.<raw_body>
```

The timestamp, contract version, and delivery ID are taken from the corresponding headers. Their values contain no dots. Do not parse and reserialize the JSON before verification. Select the agreed key using `Onside-Webhook-Key-Id`, compare signatures in constant time, and reject timestamps more than five minutes from your server time. Synchronize your server clock. Signing secrets are shared through a secure channel and can be rotated with an agreed overlap.

### Acknowledge before forwarding

1. Verify the signature, timestamp, and supported contract version.
2. Durably save the event and queue your downstream work, using `Onside-Delivery-Id` as a unique inbox key.
3. Return a `2xx` response after that commit. Return `2xx` for a duplicate already accepted.
4. Forward to advertising systems asynchronously and manage their retries independently.

Do not wait for an advertising API call before acknowledging the webhook. A `2xx` confirms your receipt; it does not confirm advertising-platform acceptance.

Delivery is **at least once**, with no arrival-order guarantee. A retry keeps the same delivery ID, event ID, contract version, and exact body. Its timestamp and signature can change. Retain inbox deduplication IDs for the integration lifetime, including redeliveries. Treat the same delivery ID with different body bytes as an integrity error.

| Response or failure                                  | Onside behavior                                                                       |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Any `2xx`                                            | Delivery accepted; no further automatic retries.                                      |
| Connection/TLS failure, timeout, `408`, `429`, `5xx` | Retry within the automatic retry budget.                                              |
| `401` or `403`                                       | Hold delivery until the authentication or permission issue is resolved.               |
| `410`                                                | Hold delivery until the endpoint is corrected.                                        |
| Other `4xx`                                          | Mark this delivery as failed; no automatic retry.                                     |
| `3xx`                                                | Do not follow the redirect; hold delivery until the configured endpoint is corrected. |

Automatic retries use delays of 5 seconds, 30 seconds, 2 minutes, 10 minutes, 1 hour, 6 hours, and 12 hours, with up to 10% jitter. There are at most eight attempts, including the first, within 24 hours. A valid `Retry-After` on `429` or `503` can delay a retry, but cannot extend that budget. Failed deliveries can be reviewed for explicit redelivery.

Each event is an immutable snapshot. Later attribution changes, cookies, or session links do not silently produce an updated webhook in this version.

## Forwarding to advertising systems

Your backend owns network selection, platform event-name mapping, credentials, account or dataset configuration, and downstream delivery. Use the original event time and available matching identifiers. Landing-page interactions are handled by your GTM integration.

The shared payload is not a complete request for every advertising API or event mode. Agree any required additional evidence that your backend does not already hold before activating that connector. A referrer is not an event-source page URL; session IDs are not mobile advertising IDs. Do not infer that an event occurred on a website merely because it was submitted through S2S or has web attribution context.

When your backend takes responsibility for a platform, agree the corresponding Onside direct-postback cutoff to avoid reporting the same conversion twice. Webhook activation alone does not disable another sender.

## Versioning and migration

Your account manager configures `publisher_conversion_v1` for your integration. The previous Store Attribution Webhooks used an array and top-level Google identifiers. That [legacy JSON schema](https://onside.io/schema/ads-conversions.json) does not describe this version. Existing receivers are not switched automatically.

For migration, update the receiver for the object body, nested click IDs, nullable fields, signatures, and delivery deduplication. Agree a cutoff and event-ID continuity before moving an existing stream. Do not assume a newly generated ID will deduplicate historical legacy deliveries.

Receivers should tolerate unknown additive fields. Onside exports additional data only when agreed for your integration. Changes to existing field meaning or required-field behavior need an explicitly selected contract version.

## Full JSON Schema

The schema below defines the current conversion payload. It describes the exact current field set; a receiver may ignore unknown additive fields before validating its supported projection. Enable JSON Schema format validation for UUIDs and timestamps. Currency codes must also be valid ISO 4217 codes, beyond the three-letter syntax checked here.

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Publisher Conversion Webhooks v1",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "onside_event_id",
    "conversion_label",
    "conversion_time",
    "app_id",
    "value",
    "order_id",
    "referrer",
    "sessions",
    "web_context"
  ],
  "properties": {
    "onside_event_id": {
      "type": "string",
      "format": "uuid"
    },
    "conversion_label": {
      "type": "string",
      "minLength": 1
    },
    "conversion_time": {
      "type": "string",
      "format": "date-time",
      "pattern": "Z$"
    },
    "app_id": {
      "type": [
        "string",
        "null"
      ],
      "minLength": 1
    },
    "value": {
      "anyOf": [
        {
          "type": "null"
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "amount",
            "currency"
          ],
          "properties": {
            "amount": {
              "type": "string",
              "pattern": "^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$"
            },
            "currency": {
              "type": "string",
              "pattern": "^[A-Z]{3}$"
            }
          }
        }
      ]
    },
    "order_id": {
      "type": [
        "string",
        "null"
      ],
      "minLength": 1
    },
    "referrer": {
      "type": [
        "string",
        "null"
      ],
      "minLength": 1
    },
    "sessions": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "web_session_id",
        "store_session_id",
        "app_session_id"
      ],
      "properties": {
        "web_session_id": {
          "type": [
            "string",
            "null"
          ],
          "minLength": 1
        },
        "store_session_id": {
          "type": [
            "string",
            "null"
          ],
          "minLength": 1
        },
        "app_session_id": {
          "type": [
            "string",
            "null"
          ],
          "minLength": 1
        }
      }
    },
    "web_context": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "cookies",
        "click_ids"
      ],
      "properties": {
        "cookies": {
          "type": "object",
          "additionalProperties": false,
          "required": [],
          "properties": {
            "_fbp": {
              "type": "string",
              "minLength": 1
            },
            "_fbc": {
              "type": "string",
              "minLength": 1
            }
          }
        },
        "click_ids": {
          "type": "object",
          "additionalProperties": false,
          "required": [],
          "properties": {
            "fbclid": {
              "type": "string",
              "minLength": 1
            },
            "gclid": {
              "type": "string",
              "minLength": 1
            },
            "gbraid": {
              "type": "string",
              "minLength": 1
            },
            "wbraid": {
              "type": "string",
              "minLength": 1
            }
          }
        }
      }
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "conversion_label": {
            "const": "store_app_installed"
          }
        }
      },
      "then": {
        "properties": {
          "sessions": {
            "properties": {
              "app_session_id": {
                "type": "null"
              }
            }
          }
        }
      }
    }
  ]
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.onside.io/api/store-attribution-webhooks.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
