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

# Delivering Audiences to The Trade Desk via the API

> Build an unattended integration that uploads an audience to Narrative and delivers it to The Trade Desk over the API

This guide shows you how to build an automated integration that uploads an audience to Narrative and delivers it to The Trade Desk (TTD). Everything here runs unattended: a person signs in once to create an API key, and the integration uses that key from then on.

For the connector's full settings reference, identifier requirements, and status vocabularies, see [The Trade Desk Connector](/reference/connectors/the-trade-desk).

## Choose a delivery model first

The Trade Desk Connector supports two delivery models, and the choice changes almost every step below. **First-party** delivers into one advertiser's own TTD seat: light setup, no taxonomy, no metering, and no further control once the data lands. **Third-party** lists your audience in TTD's marketplace: a taxonomy that TTD reviews before anything activates, pricing per element, and usage reporting from TTD.

Read [First-party and third-party delivery](/reference/connectors/the-trade-desk#overview) before you commit, because third-party setup is measured in weeks and first-party in minutes.

The rest of this guide covers third-party delivery and notes where first-party differs.

## Prerequisites

Before you begin, you need:

* **An API key**, created under **Settings → API Keys** with read and write on `datasets`, `uploads`, `connections`, `jobs`, `mappings`, `attributes`, and `installations`. See [API Keys](/account-settings/api-keys) for the procedure and the [Permissions Reference](/reference/security/permissions) for what each resource covers.
* **A TTD Brand ID**, from your Trade Desk account representative or your Narrative relationship manager. Both delivery models require it — it identifies your organization to TTD and is stored on your connector profile.
* **For first-party delivery only**, the Advertiser ID and secret key of the receiving seat, from whoever owns it.

Two things to know about the key. Create it from a shared team account rather than a personal login, so your integration does not break when someone changes roles. And keys expire in at most 365 days, so plan on annual rotation.

## Two hosts

Audience data lives on the main Narrative API. Taxonomy management lives on the Trade Desk connector's own service.

| Host                 | Base URL                                       | Handles                                                                          |
| -------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------- |
| Narrative API        | `https://api.narrative.io`                     | Datasets, uploads, mappings, profiles, connections                               |
| Trade Desk connector | `https://thetradedesk.narrativeconnectors.com` | Taxonomy elements, synchronization, advertiser registration, delivery statistics |

Both accept the same credential: `Authorization: Bearer <your API key>`.

Confirm both before you build anything:

```bash theme={null}
curl -s https://api.narrative.io/company-info/whoami \
  -H "Authorization: Bearer $NIO_API_TOKEN"
# → {"id": 1234}

curl -s https://thetradedesk.narrativeconnectors.com/whoami \
  -H "Authorization: Bearer $NIO_API_TOKEN"
# → 1234
```

Both must return your company ID.

<Tip>
  API keys contain characters that shells mangle. Read the key from a file or an environment variable rather than pasting it inline.
</Tip>

## 1. Create the dataset

Your schema decides whether The Trade Desk will accept the audience, so settle it before you upload anything.

The dataset must carry at least one identifier the connector recognizes, and each one is an object with a `value` property inside it. See [Supported identifiers](/reference/connectors/the-trade-desk#supported-identifiers) for the full list.

```bash theme={null}
curl -X POST https://api.narrative.io/datasets \
  -H "Authorization: Bearer $NIO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q3 In-Market Auto",
    "description": "Audience for TTD marketplace",
    "write_mode": "overwrite",
    "schema": {
      "file_config": { "type": "json" },
      "type": "object",
      "properties": {
        "android_advertising_id": {
          "type": "object",
          "display_name": "Android Advertising ID",
          "properties": {
            "value": { "type": "string", "display_name": "Value" }
          }
        }
      }
    }
  }'
```

`write_mode` is `overwrite` or `append`. `file_config.type` is `json` for JSON Lines, `parquet`, or `flat` for CSV. Both fields are required.

The dataset comes back `pending`. Record its ID.

<Note>
  You do not need to supply UID2. The connector generates UID2 tokens from hashed email addresses automatically.
</Note>

## 2. Activate the dataset

```bash theme={null}
curl -X POST https://api.narrative.io/datasets/{dataset_id}/activate \
  -H "Authorization: Bearer $NIO_API_TOKEN"
```

Activation locks the schema. You cannot change it afterward, so activate only once the shape is settled.

## 3. Upload your file

Request an upload URL, then send the file straight to storage:

```bash theme={null}
curl -X POST https://api.narrative.io/uploads/audiences/q3-auto.json \
  -H "Authorization: Bearer $NIO_API_TOKEN"
# → {"path": "a1b2c3....json", "url": "https://...", "expiry": "..."}

curl -X PUT "<url from the response>" --upload-file ./q3-auto.json
```

The upload URL is valid for 30 minutes and carries its own signature, so send no authorization header with the `PUT`.

<Warning>
  Keep the `path` from the response. Narrative assigns its own storage path, which will not match the one you requested, and the next step needs Narrative's path rather than yours.
</Warning>

## 4. Ingest the file

```bash theme={null}
curl -X POST https://api.narrative.io/datasets/{dataset_id}/upload \
  -H "Authorization: Bearer $NIO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"source_file": "a1b2c3....json"}'
```

Ingestion runs in the background. Watch the record count on the dataset to know when it has finished:

```bash theme={null}
curl -s https://api.narrative.io/datasets/{dataset_id} \
  -H "Authorization: Bearer $NIO_API_TOKEN"
# → .stats.active_dataset_stored_records
```

The count moves from zero to your row count, typically within a couple of minutes.

## 5. Confirm The Trade Desk accepts the dataset

```bash theme={null}
curl -s https://api.narrative.io/datasets/{dataset_id}/interfaces \
  -H "Authorization: Bearer $NIO_API_TOKEN"
```

Look for `{"app_id": 11, "interface_id": "third_party_v2"}` in the accepted list. `third_party_v2` is the interface to target for new third-party integrations — see [Delivery interfaces](/reference/connectors/the-trade-desk#delivery-interfaces) for the alternatives and why.

If TTD appears under rejected instead, the response tells you what is missing:

```json theme={null}
{
  "app_id": 11,
  "interface_id": "third_party_v2",
  "details": {
    "valid": false,
    "errors": { "required": "required property 'android_advertising_id' not found" }
  }
}
```

That is a schema problem. Because the schema is locked at activation, fixing it means creating a new dataset.

This endpoint returns `404` until the dataset is activated.

### Mapping identifiers

Mapping your identifier column to a [Rosetta Stone](/concepts/rosetta-stone/overview) attribute is **not required** for The Trade Desk to accept the dataset — acceptance is decided by your schema alone. Map anyway: normalized attributes make the dataset usable across other destinations and Narrative features.

Mapping needs a data sample first:

```bash theme={null}
curl -X POST https://api.narrative.io/datasets/{dataset_id}/request-sample \
  -H "Authorization: Bearer $NIO_API_TOKEN"
# poll https://api.narrative.io/jobs?dataset_id={dataset_id}
# until the datasets_sample job completes (roughly 8 minutes)

curl -X POST https://api.narrative.io/mappings/companies/{company_id} \
  -H "Authorization: Bearer $NIO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "dataset_id": 12345,
    "attribute_id": 70,
    "mapping": {
      "type": "object_mapping",
      "property_mappings": [
        { "path": "value", "expression": "android_advertising_id.\"value\"" }
      ]
    }
  }'
```

`value` is a [reserved keyword](/nql/general/reserved-keywords) in mapping expressions, so quote it.

Attribute IDs for the common mobile identifiers: `mobile_id_unique_identifier` is 68, `apple_idfa` is 69, `android_advertising_id` is 70.

## 6. Find your connector profile

Your profile holds your Brand ID and backs every delivery.

```bash theme={null}
curl -s "https://api.narrative.io/installations?app_categories=destination_connector" \
  -H "Authorization: Bearer $NIO_API_TOKEN"
# find the record with "app_id": 11 and note its "id"

curl -s https://api.narrative.io/installations/{installation_id}/profiles \
  -H "Authorization: Bearer $NIO_API_TOKEN"
# → [{"id": "f692fb18-...", "status": "enabled", ...}]
```

Record the profile ID. Its status must be `enabled`. If you have no profile yet, create one in the Narrative UI under **Objects → Installed Apps → Trade Desk Connector** and enter your Brand ID there.

## 7. Build your taxonomy

<Note>
  Third-party only. Skip to [Deliver the audience](#9-deliver-the-audience) for first-party delivery.
</Note>

The Trade Desk organizes marketplace data into a hierarchy of [taxonomy elements](/reference/glossary#taxonomy-element). Folders group; leaf elements are what buyers actually purchase. Every audience you sell attaches to a leaf element, and that element carries the price and the access rules.

Create a folder:

```bash theme={null}
curl -X POST https://thetradedesk.narrativeconnectors.com/taxonomies/elements \
  -H "Authorization: Bearer $NIO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Automotive",
    "description": "Auto intent audiences",
    "buyable": false,
    "datasets": [],
    "data_rates": []
  }'
```

Then the sellable element, with your dataset attached and a [rate card](/reference/glossary#rate-card):

```bash theme={null}
curl -X POST https://thetradedesk.narrativeconnectors.com/taxonomies/elements \
  -H "Authorization: Bearer $NIO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "In-Market Auto Q3",
    "description": "Households showing auto purchase intent",
    "parent_element_id": "6f0c1e94-2b3a-4d51-9c7e-5a8b0d2f1e33",
    "buyable": true,
    "datasets": [12345],
    "data_rates": [
      {
        "type": "create_data_rate_system",
        "cpm_rate": 2.50,
        "percent_of_media_cost_rate": 0.0,
        "rate_type": "cpm"
      }
    ]
  }'
```

Each entry in `data_rates` sets a price and who gets it:

| `type`                        | Also requires   | Applies to                                     |
| ----------------------------- | --------------- | ---------------------------------------------- |
| `create_data_rate_system`     | —               | Every buyer. This is your list price           |
| `create_data_rate_advertiser` | `advertiser_id` | One TTD advertiser, overriding the system rate |
| `create_data_rate_partner`    | `partner_id`    | One TTD partner, overriding the system rate    |

Every rate carries `cpm_rate`, `percent_of_media_cost_rate`, and a `rate_type` of `cpm`, `percent_of_media_cost`, or `hybrid`. A rate of \$0 is valid. Elements with no rate card inherit from their parent.

**Naming.** Display names accept up to 256 characters, but stay under 50 so they read well in TTD's interface. Avoid tabs and the characters `'`, `"`, and `^`.

Elements you create are drafts. Nothing reaches The Trade Desk until you synchronize, and you can revise (`PUT`) or remove (`DELETE`) an element at any time before it goes live.

## 8. Synchronize and wait for approval

```bash theme={null}
curl -X POST https://thetradedesk.narrativeconnectors.com/taxonomies/synchronization-requests \
  -H "Authorization: Bearer $NIO_API_TOKEN"

# then poll
curl -s https://thetradedesk.narrativeconnectors.com/taxonomies/synchronization-requests \
  -H "Authorization: Bearer $NIO_API_TOKEN"
```

Synchronization moves through `pending`, `in_progress`, and `completed`. If it fails, the response carries per-element detail.

Once synchronized, The Trade Desk reviews your taxonomy. Watch each element's sync status and compliance status:

```bash theme={null}
curl -s https://thetradedesk.narrativeconnectors.com/taxonomies \
  -H "Authorization: Bearer $NIO_API_TOKEN"
```

An element must be approved before it can be sold. Narrative re-checks review status with TTD every 30 minutes, so a decision on their side can take up to half an hour to appear — poll on that cadence, because anything faster tells you nothing new. See [Element status and approval](/reference/connectors/the-trade-desk#element-status-and-approval) for both status vocabularies and what each value means.

<Warning>
  Your first taxonomy submission is reviewed by The Trade Desk, and that review takes weeks rather than days. Ask your Narrative relationship manager about expected turnaround for elements you add later, because that answer decides whether your integration has to tolerate a long pending state for every new audience or only the first one.
</Warning>

## 9. Deliver the audience

Create a connection joining the dataset, the profile, and an approved taxonomy element:

```bash theme={null}
curl -X POST https://api.narrative.io/v2/connections \
  -H "Authorization: Bearer $NIO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "connections_dataset",
    "dataset_id": 12345,
    "profile_id": "f692fb18-9d47-4c2a-b1e6-3c5f8a0b7d21",
    "quick_settings": {
      "type": "third_party_v2",
      "third_party_data_type": "third_party",
      "provider_elements": [
        {
          "provider_element_id": "8c3d5b21-7e4f-4a90-b6c2-1f9a0e7d4b58",
          "display_name": "In-Market Auto Q3"
        }
      ],
      "targeting_time_to_live_in_minutes": 129600,
      "historical_data_enabled": true
    }
  }'
```

Both `type` fields are required. The outer one identifies what you are connecting; the inner one selects the delivery model. Always send them.

`targeting_time_to_live_in_minutes` is how long a delivered identifier stays active in TTD before expiring, and it defaults to 90 days. The clock rides on every record, so each delivery renews it — keep your refresh cadence shorter than this value or members will lapse between deliveries. See [Audience membership duration](/reference/connectors/the-trade-desk#audience-membership-duration) for the full behavior.

<Warning>
  `historical_data_enabled` decides whether data already in the dataset is delivered or only rows written after the connection is created. It cannot be changed once the connection exists.
</Warning>

Your source dataset must refresh at least every 90 days.

### First-party delivery

For first-party, skip the taxonomy entirely. Register the receiving advertiser once:

```bash theme={null}
curl -X POST https://thetradedesk.narrativeconnectors.com/advertisers \
  -H "Authorization: Bearer $NIO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Motors",
    "ttd_id": "<their TTD advertiser id>",
    "secret": "<their secret key>"
  }'
```

Then create the connection with first-party settings:

```json theme={null}
{
  "type": "connections_dataset",
  "dataset_id": 12345,
  "profile_id": "f692fb18-9d47-4c2a-b1e6-3c5f8a0b7d21",
  "quick_settings": {
    "type": "first_party",
    "first_party_data_type": "first_party_external",
    "advertiser_ids": ["<their TTD advertiser id>"],
    "data_segment_names": ["In-Market Auto Q3"],
    "targeting_time_to_live_in_minutes": 129600,
    "historical_data_enabled": true
  }
}
```

<Warning>
  `advertiser_ids` takes the advertiser's **Trade Desk** ID — the same value you supplied as `ttd_id` above, not the identifier Narrative returns for the registration record. Supplying the wrong one causes the delivery to fall back to third-party behavior without raising an error.
</Warning>

## 10. Confirm delivery

```bash theme={null}
curl -s https://thetradedesk.narrativeconnectors.com/taxonomies \
  -H "Authorization: Bearer $NIO_API_TOKEN"
```

Each element reports `statistics.received_ids`, the identifiers Narrative sent, and `statistics.active_ids`, the subset The Trade Desk matched. The gap between them is your match rate.

## Adding audiences later

Once your taxonomy is approved, adding an audience is steps 1 through 5, then a connection pointing at an approved element. No new taxonomy work, provided you reuse an element that already exists.

## Troubleshooting

| Symptom                                                           | Cause                                                                  | Fix                                                                                             |
| ----------------------------------------------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `403` on `/company-info/whoami`                                   | The key was not created under **Settings → API Keys**                  | Create one there. Application credentials do not work with this flow                            |
| `400 Missing required field .write_mode` or `.schema.file_config` | Both are required on dataset creation                                  | Add them                                                                                        |
| `400 file not found` on ingest                                    | You sent the path you requested rather than the one Narrative returned | Use the `path` from the upload response                                                         |
| Record count stays at zero                                        | The file did not match the schema                                      | Check that the format matches `file_config.type` and that field names match your schema exactly |
| `404` on `/datasets/{id}/interfaces`                              | The dataset is not activated yet                                       | Activate it first                                                                               |
| TTD listed as rejected                                            | The schema has no recognized identifier in the expected shape          | Read `details.errors`, then create a new dataset with a corrected schema                        |
| `400 No sample is available` on mapping                           | Mapping needs a data sample                                            | `POST /datasets/{id}/request-sample`, wait for the job, retry                                   |
| Connection rejected for a missing element                         | The element is not synchronized and approved yet                       | Check that its sync status is `active` and its compliance status is approved                    |

## Getting help

Contact your Narrative relationship manager with your company ID, the dataset ID, and the failing request and response. For Trade Desk review status and Brand ID questions, your TTD account representative is the faster path.

***

## Related content

<CardGroup cols={2}>
  <Card title="The Trade Desk Connector" icon="bullhorn" href="/reference/connectors/the-trade-desk">
    Full settings reference, identifiers, and status vocabularies
  </Card>

  <Card title="Connector Interfaces" icon="plug" href="/concepts/data-activation/connector-interfaces">
    Why a dataset connects to an interface rather than a connector
  </Card>

  <Card title="Structuring Audiences" icon="sitemap" href="/guides/activation/audience-strategies">
    Organize datasets for delivery
  </Card>

  <Card title="API Keys" icon="key" href="/account-settings/api-keys">
    Create and rotate keys for programmatic access
  </Card>
</CardGroup>
