> ## 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.

# Managing Your Trade Desk Taxonomy via the API

> Build, price, and maintain the taxonomy that third-party audiences are sold from at The Trade Desk, over the API

This guide shows you how to build, price, and maintain your taxonomy at The Trade Desk (TTD) over the API, with nobody signed in. The taxonomy is where third-party audiences are listed. Each audience is an element in it, and the element sets who can buy the audience and at what price.

To attach data to an element and deliver it, see [Third-party delivery via the API](https://docs.narrative.io/guides/connectors/the-trade-desk/third-party-delivery-api.md). First-party delivery uses no taxonomy; see [First-party delivery via the API](https://docs.narrative.io/guides/connectors/the-trade-desk/first-party-delivery-api.md).

Before you start, complete the [prerequisites](https://docs.narrative.io/guides/connectors/the-trade-desk/setting-up-api-access.md#prerequisites) and [set up API access](https://docs.narrative.io/guides/connectors/the-trade-desk/setting-up-api-access.md#set-up-api-access).

## 1. Plan the tree

A taxonomy is a tree of [taxonomy elements](https://docs.narrative.io/reference/glossary.md#taxonomy-element). A folder (`buyable: false`) organizes. An audience (`buyable: true`) is what buyers purchase, and it carries the dataset and the [rate cards](https://docs.narrative.io/reference/glossary.md#rate-card).

```text theme={null}
SmartAudiences                      ← root folder, named for your brand
├── Auto Intenders                  ← audience, open to every buyer
├── Frequent Travelers              ← audience, open to every buyer
└── Custom Audiences                ← folder for one-to-one deals
    ├── Acme Q3 Retargeting         ← audience, priced for one advertiser
    └── Zenith Store Visitors       ← audience, priced for one partner
```

**Name the root for your audience product.** Create it even if you never plan to sell openly. It is the level everything else hangs from, and reshaping the top of the tree after audiences are live cannot be cleanly undone.

**Keep one-to-one deals under their own folder.** TTD suggests a folder such as `Custom Audiences` for audiences priced for a single advertiser or partner. It is a naming convention, not a special element type.

**Price every audience on the audience itself.** Give each buyable element its own rate card.

## 2. Create the folders

One endpoint creates every element. `buyable` is what separates a folder from an audience.

```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": "SmartAudiences",
    "description": "Location intelligence audiences",
    "buyable": false,
    "parent_element_id": null,
    "datasets": [],
    "data_rates": []
  }'
```

The response is `201` with the new element, a draft:

```json theme={null}
{
  "buyable": false,
  "children": [],
  "connections": [],
  "data_rates": [],
  "description": "Location intelligence audiences",
  "display_name": "SmartAudiences",
  "element_id": "<element_id>",
  "parent_element_id": null,
  "statistics": null,
  "status": "draft",
  "compliance_status": "NotInQueue",
  ...
}
```

Record the `element_id`. Child elements name it as `parent_element_id`. Then create `Custom Audiences` the same way, with `parent_element_id` set to the root's `element_id`.

| Field | Description |
| - | - |
| `display_name` | Required. Keep it under 50 characters so it reads well in TTD's interface, and avoid tabs and the characters `'`, `"`, and `^`. |
| `description` | Optional. Shown with the segment in TTD. |
| `buyable` | `false` for a folder, `true` for an audience. |
| `parent_element_id` | The parent folder's `element_id`. Send `null` for the root. |
| `datasets` | Narrative dataset IDs that feed the element. Empty for a folder. |
| `data_rates` | Rate cards. Empty for a folder. |

## 3. Create an audience

An audience is the same call with `buyable: true`, a parent, a dataset, and the rate cards that decide who can buy it and at what price.

```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": "Acme Q3 Retargeting",
    "description": "Devices observed in target geofences, Q3",
    "parent_element_id": "<Custom Audiences element_id>",
    "buyable": true,
    "datasets": [12345],
    "data_rates": [
      {
        "type": "create_data_rate_advertiser",
        "advertiser_id": "<TTD advertiser id>",
        "rate_type": "hybrid",
        "cpm_rate": 2.50,
        "percent_of_media_cost_rate": 0.15
      }
    ]
  }'
```

The response is `201`. Each dataset you attached appears under `connections` as a draft, and each rate under `data_rates`:

```json theme={null}
{
  "buyable": true,
  "connections": [
    { "type": "connection_draft", "dataset_id": 12345, "status": "draft", ... }
  ],
  "data_rates": [
    { "type": "data_rate_advertiser", "advertiser_id": "<TTD advertiser id>", "batch": null,
      "cpm_rate": 2.5, "percent_of_media_cost_rate": 0.15, "rate_type": "hybrid", "element_id": "<element_id>" }
  ],
  "display_name": "Acme Q3 Retargeting",
  "element_id": "<element_id>",
  "status": "draft",
  "compliance_status": "NotInQueue",
  ...
}
```

The connector checks each dataset's schema against The Trade Desk when you attach it, and rejects a dataset TTD would not accept with `400 TTD Taxonomies Creation: Invalid Dataset Schema`. To prepare a dataset, follow steps 1 through 4 of [Third-party delivery via the API](https://docs.narrative.io/guides/connectors/the-trade-desk/third-party-delivery-api.md).

### Who can buy it

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

| `type` | Who can buy | Also requires |
| - | - | - |
| `create_data_rate_system` | Every buyer in the marketplace | Nothing |
| `create_data_rate_partner` | One TTD partner and every advertiser beneath it | `partner_id` |
| `create_data_rate_advertiser` | One TTD advertiser | `advertiser_id` |

One element can carry several rates. To price one audience differently for different buyers, add one entry per advertiser or partner:

```json theme={null}
"data_rates": [
  { "type": "create_data_rate_advertiser", "advertiser_id": "<advertiser A>",
    "rate_type": "hybrid", "cpm_rate": 2.50, "percent_of_media_cost_rate": 0.15 },
  { "type": "create_data_rate_advertiser", "advertiser_id": "<advertiser B>",
    "rate_type": "hybrid", "cpm_rate": 4.00, "percent_of_media_cost_rate": 0.20 },
  { "type": "create_data_rate_partner", "partner_id": "<partner id>",
    "rate_type": "hybrid", "cpm_rate": 3.00, "percent_of_media_cost_rate": 0.18 }
]
```

The element returns them as `data_rate_advertiser`, `data_rate_partner`, and `data_rate_system`. Advertiser and partner IDs come from your TTD account representative, and they go straight onto the rate card. You do not register them with Narrative first.

### Setting the rate

Set `rate_type` to `hybrid`, which TTD asks for on marketplace segments, and send both `cpm_rate` (US dollars per thousand impressions) and `percent_of_media_cost_rate` (a fraction, so `0.15` is 15%).

## 4. Revise before you synchronize

Elements you create are drafts, and nothing reaches The Trade Desk until you synchronize. Until then you can change anything with `PUT`. It replaces the whole element, so send every field:

```bash theme={null}
curl -X PUT https://thetradedesk.narrativeconnectors.com/taxonomies/elements/{element_id} \
  -H "Authorization: Bearer $NIO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Acme Q3 Retargeting",
    "description": "Devices observed in target geofences, Q3",
    "parent_element_id": "<Custom Audiences element_id>",
    "buyable": true,
    "datasets": [12345],
    "data_rates": [
      { "type": "create_data_rate_advertiser", "advertiser_id": "<advertiser A>",
        "rate_type": "hybrid", "cpm_rate": 3.00, "percent_of_media_cost_rate": 0.18 },
      { "type": "create_data_rate_advertiser", "advertiser_id": "<advertiser B>",
        "rate_type": "hybrid", "cpm_rate": 4.00, "percent_of_media_cost_rate": 0.20 }
    ]
  }'
# → HTTP 200, the updated element
```

To drop a draft you no longer want, delete it. The connector archives the element and everything beneath it:

```bash theme={null}
curl -X DELETE https://thetradedesk.narrativeconnectors.com/taxonomies/elements/{element_id} \
  -H "Authorization: Bearer $NIO_API_TOKEN"
# → HTTP 204
```

Once an element is `active`, its rates are fixed. A `PUT` that changes them is rejected with `data rates cannot be updated for active taxonomy element`, while one that sends the same rates again goes through. The display name and description can still change, and the connector sends a new name or description on to TTD. See [Maintain the taxonomy](#7-maintain-the-taxonomy).

Put every buyer you expect on the element when you create it. Changing the price or the buyers of a live audience is not self-service. Ask your Narrative relationship manager, who arranges it with The Trade Desk.

## 5. Synchronize

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

The response is `201` with the request and the work it queued:

```json theme={null}
{
  "id": "<request_id>",
  "company_id": 1234,
  "created_at": "2026-10-03T15:00:00Z",
  "jobs": {
    "create_connections":       { "completed": 0, "failed": 0, "failures": [], "in_progress": 0, "pending": 1 },
    "create_data_rate_batches": { "completed": 0, "failed": 0, "failures": [], "in_progress": 0, "pending": 1 },
    "create_taxonomy_elements": { "completed": 0, "failed": 0, "failures": [], "in_progress": 0, "pending": 3 }
  },
  "status": "pending",
  "updated_at": "2026-10-03T15:00:00Z"
}
```

The `jobs` object counts the work in three parts: elements created in TTD (`create_taxonomy_elements`), rate batches (`create_data_rate_batches`), and delivery connections for attached datasets (`create_connections`).

Poll the request list and pick out your request by its `id`. The list holds your whole synchronization history, so your request is not necessarily the first record:

```bash theme={null}
curl -s https://thetradedesk.narrativeconnectors.com/taxonomies/synchronization-requests \
  -H "Authorization: Bearer $NIO_API_TOKEN" \
  | jq '.records[] | select(.id == "<request_id>") | {status, jobs}'
```

`status` moves from `pending` through `in-progress` to `completed` or `failed`. When a part fails, its `failures` list carries the error for each job.

Two rules shape how you automate this:

* **Synchronization publishes every draft in your account**, not only the element you just changed. Create everything you want published, then synchronize once.
* **Only one synchronization runs at a time.** A second request while one is pending or in progress is rejected with `409`. A loop that creates one audience and synchronizes it breaks on its second pass, so batch your changes instead.

  ```json theme={null}
  {"error":"conflict","error_description":"Synchronization request <request_id> is already pending or in progress"}
  ```

A request from a profile with no Brand ID is refused. A request with nothing to send is rejected with `400`:

```json theme={null}
{"error":"invalid_request","error_description":"Nothing to synchronize: there are no draft taxonomy elements, data rates or connections"}
```

## 6. Wait for approval

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

The response is the whole tree under `children`, each element with its own `children`. After synchronization each element is `active`, and each rate card carries the TTD batch it was sent in:

```json theme={null}
{
  "display_name": "Auto Intenders",
  "element_id": "<element_id>",
  "status": "active",
  "compliance_status": "Pending",
  "data_rates": [
    { "type": "data_rate_system", "cpm_rate": 2.5, "percent_of_media_cost_rate": 0.15, "rate_type": "hybrid",
      "batch": { "id": "<batch_id>", "approval_status": "not_reviewed", "processing_status": "not_started", ... }, ... }
  ],
  "connections": [
    { "type": "connection_synchronized", "connection_id": "<connection_id>", "connection_status": "active",
      "dataset_id": 12345, "status": "synchronized", ... }
  ],
  ...
}
```

Each element reports two statuses, and an audience sells only once it is `active` and its `compliance_status` is `Approved`:

* `status` is the element's lifecycle in Narrative, from `draft` to `pending` to `active`.
* `compliance_status` is TTD's review, from `NotInQueue` to `Pending` to `Approved` or `Denied`.

See [Element status and approval](https://docs.narrative.io/reference/connectors/the-trade-desk.md#element-status-and-approval) for every value.

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.

> [!WARNING]
>
> Plan for lead time on your first taxonomy. Ask your Narrative relationship manager how long TTD takes to review elements you add later. If later elements also wait for review, your integration must treat each new audience as pending until TTD approves it.

## 7. Maintain the taxonomy

**Add audiences** by repeating step 3, then synchronizing. Batch several new elements into one synchronization.

**Change the name or description of a live audience** with `PUT`, sending the rates it already has. The connector sends the new name and description to TTD:

```bash theme={null}
curl -X PUT https://thetradedesk.narrativeconnectors.com/taxonomies/elements/{element_id} \
  -H "Authorization: Bearer $NIO_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Auto Intenders v2",
    "description": "Households showing auto purchase intent, renamed",
    "parent_element_id": "<SmartAudiences element_id>",
    "buyable": true,
    "datasets": [12345],
    "data_rates": [
      { "type": "create_data_rate_system", "rate_type": "hybrid",
        "cpm_rate": 2.50, "percent_of_media_cost_rate": 0.15 }
    ]
  }'
# → HTTP 200
```

**Refresh who is in an audience** by writing to its dataset. The delivery connection keeps running, so you do not touch the element or its price. See [Keeping the audience current](https://docs.narrative.io/guides/connectors/the-trade-desk/third-party-delivery-api.md#keeping-the-audience-current).

**Watch the match rate.** Each element's `statistics` reports `received_ids_count`, the identifiers Narrative sent, and `active_ids_count`, the subset TTD matched. See [Confirm delivery](https://docs.narrative.io/guides/connectors/the-trade-desk/third-party-delivery-api.md#8-confirm-delivery).

**Create a new audience rather than reworking an old one** when the price changes, the schema changes, or it is a different audience for a different buyer. A live element's rates are fixed, so a new element is the only self-service way to price separately.

**Retiring a live audience is not self-service.** Contact your TTD Technical Account Manager and your Narrative relationship manager. Removing a custom segment from a named buyer requires 30 days' advance notice to that buyer.

> [!WARNING]
>
> `DELETE /taxonomies/elements/{element_id}` archives the element and everything beneath it in Narrative only. Nothing is sent to The Trade Desk, and an element that is already live there stays buyable. Use it to clean up drafts, not to retire an audience.

## Taxonomy endpoints

All on `https://thetradedesk.narrativeconnectors.com`.

| Method | Path | Purpose |
| - | - | - |
| `GET` | `/whoami` | Confirm your key reaches the connector |
| `GET` | `/taxonomies` | The whole tree: elements, rate cards, statuses, and statistics |
| `POST` | `/taxonomies/elements` | Create a folder or an audience |
| `PUT` | `/taxonomies/elements/{element_id}` | Replace an element. Send the full object |
| `DELETE` | `/taxonomies/elements/{element_id}` | Archive an element and everything beneath it, in Narrative only |
| `POST` | `/taxonomies/synchronization-requests` | Publish every draft to TTD |
| `GET` | `/taxonomies/synchronization-requests` | Synchronization history and status. Pick out a request by its `id` |

## Troubleshooting

| Symptom | Cause | Fix |
| - | - | - |
| `409` on a synchronization request | One is already pending or in progress | Wait for the running one to finish |
| `Nothing to synchronize` | No element, rate, or connection is in draft | Create or change something first |
| Synchronization is rejected with `TTD profile has no brand ID` | The connector profile has no Brand ID | Enter your Brand ID on the profile in the Narrative UI under **Objects → Installed Apps → Trade Desk Connector**, or ask your Narrative relationship manager |
| Elements went live that you were not ready to publish | Synchronization publishes every draft | Create only what you intend to publish, then synchronize |
| An audience has no price | It has no rate card of its own | Add a rate card to the element |
| `data rates cannot be updated for active taxonomy element` | The element is already live | Create a new element, or ask your Narrative relationship manager |
| An audience is active but not selling | `compliance_status` is not yet `Approved` | Poll `/taxonomies`. Review status refreshes every 30 minutes |
| A deleted element is still buyable in TTD | `DELETE` archives in Narrative only | Retire it through your TTD Technical Account Manager |

## Getting help

Contact your Narrative relationship manager with your company ID, the element ID, and the failing request and response. For review status, Brand ID, advertiser IDs, or partner IDs, your TTD account representative is the faster path.

***

## Related content

> **[Third-party delivery via the API](https://docs.narrative.io/guides/connectors/the-trade-desk/third-party-delivery-api.md)**
>
> Attach data to an element and deliver it

> **[First-party delivery via the API](https://docs.narrative.io/guides/connectors/the-trade-desk/first-party-delivery-api.md)**
>
> Deliver into one advertiser's own seat, with no taxonomy

> **[The Trade Desk Connector](https://docs.narrative.io/reference/connectors/the-trade-desk.md)**
>
> Full settings reference, identifiers, and status vocabularies

> **[API Keys](https://docs.narrative.io/account-settings/api-keys.md)**
>
> Create and rotate keys for programmatic access


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.