> ## Documentation Index
> Fetch the complete documentation index at: https://docs.interchange.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Log Events

> Send custom events to an advertiser for downstream measurement and reporting

## Overview

`POST /api/v2/buyer/advertisers/:advertiserId/log-event` is a single-call ingest endpoint for sending outcome or marketing events to an advertiser. It accepts the standard ADCP event types — `purchase`, `refund`, `lead`, `add_to_cart`, `initiate_checkout`, `view_content`, `content_view`, `watch_milestone`, `follow`, `complete_registration`, `subscribe`, `start_trial`, `page_view`, `app_install`, `app_launch` — and a `custom` event type for events that don't map to any of those.

Use it for:

* **Bespoke conversion definitions** — a "qualified demo booked" or "tier upgrade" event that doesn't fit the standard taxonomy. Send `event_type: custom` with `custom_event_name`.
* **Internal-only events for ML training** — engagement signals you want to feed measurement, even if they aren't reportable conversions.
* **Custom funnel steps** — domain-specific milestones (game level reached, KYC step completed) that drive your bidding logic.
* **Server-to-server batch ingestion** — up to 10,000 events per call.

## How it differs from event sources and measurement data

The three measurement ingest paths overlap; pick the one that matches the shape of your data.

| Surface                                         | What you send                                                       | Granularity                               | Best for                                                                             |
| ----------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------ |
| **Event sources** (`/event-sources/sync`)       | The *configuration* of where events come from (pixels, SDKs, MMPs)  | Source registration                       | Onboarding a new pixel or feed once, then sending events through it                  |
| **Log events** (`/log-event`)                   | Per-user, per-event records keyed to a registered `event_source_id` | One record per event (1–10,000 per call)  | Bespoke conversion taxonomies, custom funnel steps, server-to-server event ingestion |
| **Measurement data** (`/measurement-data/sync`) | Pre-aggregated outcomes for a time window and entity                | One record per metric × window × campaign | MMP, CRM, or warehouse exports of weekly/daily aggregates                            |

`log-event` is the path for **identified, per-user events at advertiser scope**. Event sources are the *configuration layer* you call once; `log-event` is the runtime path you call repeatedly.

<Note>
  Every event must reference an `event_source_id` that is already registered
  on the advertiser via `POST /advertisers/:advertiserId/event-sources/sync`.
  Events sent against an unregistered source are rejected. See the
  [Measurement & Incrementality guide](/v2/guides/measurement-incrementality)
  for event-source setup.
</Note>

## Request shape

All examples use:

```
BASE = https://api.interchange.io/api/v2/buyer
AUTH = Authorization: Bearer scope3_<your_api_key>
```

```bash theme={null}
curl -X POST "$BASE/advertisers/12345/log-event" \
  -H "Authorization: Bearer scope3_<your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "event_source_id": "website_pixel",
    "events": [
      {
        "event_id": "txn_abc123",
        "event_type": "purchase",
        "event_time": "2026-04-26T14:30:00-05:00",
        "action_source": "website",
        "event_source_url": "https://shop.example.com/checkout/complete",
        "user_match": {
          "hashed_email": "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
          "click_id": "fb.1.1700000000.AbCdEfGh",
          "click_id_type": "fbclid"
        },
        "custom_data": {
          "value": 89.99,
          "currency": "USD",
          "order_id": "order_55512",
          "content_ids": ["sku_red_shoe_42"],
          "num_items": 1
        }
      },
      {
        "event_id": "demo_booked_98765",
        "event_type": "custom",
        "custom_event_name": "qualified_demo_booked",
        "event_time": "2026-04-26T15:05:00-05:00",
        "action_source": "website",
        "user_match": {
          "hashed_email": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
        }
      }
    ]
  }'
```

```json Response theme={null}
{
  "events_received": 2,
  "events_processed": 2,
  "warnings": [],
  "match_quality": 0.87
}
```

### Top-level fields

| Field             | Type   | Required | Notes                                                                                      |
| ----------------- | ------ | -------- | ------------------------------------------------------------------------------------------ |
| `event_source_id` | string | yes      | Must match an `event_source_id` registered on the advertiser                               |
| `events`          | array  | yes      | 1–10,000 events per call                                                                   |
| `test_event_code` | string | no       | Marks the request as test traffic — events are validated but excluded from production data |

### Per-event fields

| Field               | Type     | Required    | Notes                                                                                                                                                                                                                                                         |
| ------------------- | -------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event_id`          | string   | yes         | Unique within `event_type` + `event_source_id`. Resends with the same `event_id` are deduplicated.                                                                                                                                                            |
| `event_type`        | enum     | yes         | Canonical ADCP event type such as `purchase`, `lead`, `add_to_cart`, `initiate_checkout`, `view_content`, `content_view`, `watch_milestone`, `follow`, `complete_registration`, `page_view`, `app_install`, `subscribe`, `start_trial`, `refund`, or `custom` |
| `event_time`        | ISO 8601 | yes         | Must include offset (e.g. `2026-04-26T14:30:00-05:00`)                                                                                                                                                                                                        |
| `custom_event_name` | string   | conditional | Required when `event_type: custom`                                                                                                                                                                                                                            |
| `action_source`     | enum     | no          | `website`, `app`, `in_store`, `phone_call`, `system_generated`, `other`                                                                                                                                                                                       |
| `event_source_url`  | URL      | conditional | Required when `action_source: website`                                                                                                                                                                                                                        |
| `user_match`        | object   | no          | Identity fields for attribution (see below)                                                                                                                                                                                                                   |
| `custom_data`       | object   | no          | Value, currency, order ID, items                                                                                                                                                                                                                              |

### `user_match` — identity fields

Send at least one identifier per event. More identifiers = higher match rate.

| Field                        | Format                     | Notes                                                            |
| ---------------------------- | -------------------------- | ---------------------------------------------------------------- |
| `uids`                       | array of `{ type, value }` | Universal IDs: `rampid`, `id5`, `uid2`, `euid`, `pairid`, `maid` |
| `hashed_email`               | 64-char lowercase hex      | SHA-256 of lowercased, trimmed email                             |
| `hashed_phone`               | 64-char lowercase hex      | SHA-256 of E.164-formatted phone                                 |
| `click_id` + `click_id_type` | string                     | Platform click identifier (`fbclid`, `gclid`, `ttclid`)          |
| `client_ip`                  | string                     | IP for probabilistic matching                                    |
| `client_user_agent`          | string                     | UA for probabilistic matching                                    |

### `custom_data` — event payload

| Field          | Type                                      | Notes                                                         |
| -------------- | ----------------------------------------- | ------------------------------------------------------------- |
| `value`        | number                                    | Monetary value of the event                                   |
| `currency`     | string                                    | ISO 4217 (`USD`, `EUR`, `GBP`) — required when `value` is set |
| `order_id`     | string                                    | Unique transaction ID                                         |
| `content_ids`  | array of strings                          | SKU / product IDs                                             |
| `content_type` | string                                    | Category (`product`, `service`)                               |
| `num_items`    | integer                                   | Item count                                                    |
| `contents`     | array of `{ id, quantity, price, brand }` | Per-item detail                                               |

### Response shape

| Field              | Type             | Notes                                                         |
| ------------------ | ---------------- | ------------------------------------------------------------- |
| `events_received`  | integer          | Count of events in the request                                |
| `events_processed` | integer          | Count successfully queued                                     |
| `partial_failures` | array            | Per-event failures with `event_id`, `code`, `message`         |
| `warnings`         | array of strings | Non-fatal issues — low match quality, missing optional fields |
| `match_quality`    | number (0–1)     | Aggregate match-quality score for the batch                   |

A `200` with `partial_failures` is normal — bad events are rejected individually, the rest succeed. Treat any failure as something to fix at source, not a transient error.

## Best practices

<AccordionGroup>
  <Accordion title="Pick stable, descriptive custom_event_name values">
    Custom event names become the join key for downstream measurement and
    reporting. Pick names you won't want to change (`qualified_demo_booked`,
    `kyc_step_2_complete`). Renaming after launch breaks historical
    continuity.
  </Accordion>

  <Accordion title="Use a standard event_type when one fits">
    If your event is genuinely a purchase or a lead, use `purchase` or
    `lead`. Standard types get first-class treatment in measurement and
    optimization; `custom` events fall back to generic handling.
  </Accordion>

  <Accordion title="Use event_id for idempotency">
    `event_id` is the dedup key, scoped to `event_type` + `event_source_id`.
    Re-sending the same `event_id` is safe — duplicates are dropped. Use
    your stable upstream identifier (transaction ID, demo booking ID) so
    retries don't double-count.
  </Accordion>

  <Accordion title="Send events in real time when you can">
    Recent events match better — click IDs and probabilistic signals decay
    quickly. Aim for sub-hour latency for behavioral events; daily batches
    are fine for offline conversions like CRM exports.
  </Accordion>

  <Accordion title="Use test_event_code in non-prod">
    Set `test_event_code` for test traffic. Events validate end-to-end
    but are excluded from production reporting and the measurement engine.
  </Accordion>

  <Accordion title="Batch up to 10,000 events">
    The endpoint accepts up to 10,000 events per call. Batch larger flows
    rather than firing one HTTP request per event — same data, much less
    overhead.
  </Accordion>
</AccordionGroup>

## Privacy

<Warning>
  **Never send raw PII.** No plaintext emails, phone numbers, names, or
  addresses. Only pre-hashed identifiers (SHA-256, lowercase, 64-char hex)
  or pre-resolved identity tokens (RampID, UID2, ID5, etc.). Normalize the
  email (lowercase, trimmed) and phone (E.164) **before** hashing — see the
  [Conversion API guide](/v2/integrations/conversion-api) for hashing
  rules. Malformed hashes are silently dropped.
</Warning>

The same privacy contract applies whether an event is `purchase` or `custom`. Custom events are not a back door for raw PII.

## Related

<CardGroup cols={2}>
  <Card title="Conversion API" href="/v2/integrations/conversion-api" icon="bolt">
    Detailed identity-hashing rules and the same `log-event` payload viewed
    as a conversion-tracking integration.
  </Card>

  <Card title="Measurement & Incrementality" href="/v2/guides/measurement-incrementality" icon="chart-line">
    Register event sources, configure measurement, and run incrementality
    tests against the events you log.
  </Card>
</CardGroup>
