---
url: https://docs.dataflair.ai/marketplace/ad-server-api/end-to-end.md
description: >-
  One booking followed from first connection to final reconciliation, with real
  request and response bodies and consistent ids.
---

# End-to-end example

One booking, from first connection to final reconciliation, with real request and response bodies. The ids are consistent in every step, so you can trace them.

* Account timezone: `Europe/Berlin`. Currency: `EUR`.
* Ad slot: `slot_728x90_home` ("Homepage Leaderboard").
* Advertiser: `Acme Corp`.
* DataFlair campaign reference: `CMP-VDWZQ4IT`.

## Step 1: Connect (once)

An operator enters your `base_url` and the credential you issued. DataFlair probes with [`GET /health`](/marketplace/ad-server-api/operations/health).

::: code-group

```http [HTTP]
GET /health
Authorization: Bearer sk_live_9f2c…
```

```bash [curl]
curl "$BASE_URL/health" -H "Authorization: Bearer $API_KEY"
```

:::

Your response:

::: code-group

```json \[exemplary/health/expected-response.json]
{
  "account_id": "acct_1029",
  "account_name": "Example Media Network",
  "timezone": "Europe/Berlin",
  "currency": "EUR",
  "capabilities": {
    "forecast": true,
    "reporting": true,
    "draft_booking": true
  }
}

```

:::

DataFlair records the account, the timezone and the currency, and marks the connection **connected**.

## Step 2: Discover and map inventory

DataFlair calls [`GET /inventory`](/marketplace/ad-server-api/operations/inventory).

::: code-group

```http [HTTP]
GET /inventory?limit=100
Authorization: Bearer sk_live_9f2c…
```

```bash [curl]
curl "$BASE_URL/inventory?limit=100" -H "Authorization: Bearer $API_KEY"
```

:::

Your response:

```json
{
  "data": [
    { "inventory_id": "slot_728x90_home", "name": "Homepage Leaderboard", "sizes": ["728x90"], "format": "display", "status": "active" }
  ],
  "next_cursor": null
}
```

The operator maps the DataFlair placement "Homepage Leaderboard" to `slot_728x90_home`. DataFlair stores that mapping once and reuses it for every future booking of this slot.

## Step 3: An advertiser forecasts before booking

Acme Corp is considering all of August, targeting Germany and Austria. DataFlair calls [`POST /forecast`](/marketplace/ad-server-api/operations/forecast).

::: code-group

```http [HTTP]
POST /forecast
Authorization: Bearer sk_live_9f2c…
Content-Type: application/json
```

```bash [curl]
curl -X POST "$BASE_URL/forecast" -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" -d @request.json
```

:::

The request body:

::: code-group

```json \[exemplary/forecast/request.json]
{
  "inventory_id": "slot_728x90_home",
  "flight": {
    "start_date": "2026-08-01",
    "end_date": "2026-08-31"
  },
  "targeting": {
    "geo": [
      "DE",
      "AT"
    ]
  },
  "sizes": [
    "728x90"
  ]
}

```

:::

Your response:

::: code-group

```json \[exemplary/forecast/expected-response.json]
{
  "inventory_id": "slot_728x90_home",
  "available_impressions": 1420000,
  "forecasted_impressions": 2100000,
  "unit_type": "IMPRESSIONS"
}

```

:::

DataFlair shows Acme that about 1.42M impressions are bookable. Acme books 100,000, pays into their DataFlair wallet and submits the reservation.

## Step 4: You approve the reservation (inside DataFlair)

Your team reviews the reservation in DataFlair and approves it. This is the trigger. Nothing has reached your ad server yet. The approval causes the next call.

## Step 5: DataFlair pushes the booking as a draft

DataFlair calls [`POST /orders`](/marketplace/ad-server-api/operations/orders).

::: code-group

```http [HTTP]
POST /orders
Authorization: Bearer sk_live_9f2c…
Content-Type: application/json
Idempotency-Key: DF-CMP-VDWZQ4IT
```

```bash [curl]
curl -X POST "$BASE_URL/orders" -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" -H "Idempotency-Key: DF-CMP-VDWZQ4IT" \
  -d @request.json
```

:::

The request body:

::: code-group

```json \[exemplary/orders/request.json]
{
  "idempotency_key": "DF-CMP-VDWZQ4IT",
  "advertiser": {
    "name": "Acme Corp",
    "external_ref": "brand_5501"
  },
  "order": {
    "name": "Summer Launch (DF-CMP-VDWZQ4IT)",
    "external_ref": "CMP-VDWZQ4IT"
  },
  "line_items": [
    {
      "external_ref": "DF-CMP-VDWZQ4IT-329",
      "inventory_id": "slot_728x90_home",
      "flight": {
        "start_date": "2026-08-01",
        "end_date": "2026-08-31"
      },
      "goal_impressions": 100000,
      "targeting": {
        "geo": [
          "DE",
          "AT"
        ]
      },
      "sizes": [
        "728x90"
      ],
      "creatives": [
        {
          "type": "third_party_tag",
          "width": 728,
          "height": 90,
          "tag": "<iframe src='https://t.dataflair.ai/ad/ABC123?df_source=custom&df_ad_server=custom_platform' width='728' height='90' frameborder='0' scrolling='no' referrerpolicy='no-referrer-when-downgrade' sandbox='allow-scripts allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation allow-same-origin' style='border:0;display:block' title='Advertisement'></iframe>"
        }
      ]
    }
  ]
}

```

:::

Your platform resolves or creates the advertiser "Acme Corp". It creates a **draft** order and one **draft** line item. The line item targets `slot_728x90_home` for August in DE and AT, with a goal of 100,000 impressions. It attaches the DataFlair tag and returns your ids:

::: code-group

```json \[exemplary/orders/expected-response.json]
{
  "order_id": "ord_55021",
  "status": "DRAFT",
  "line_items": [
    {
      "external_ref": "DF-CMP-VDWZQ4IT-329",
      "line_item_id": "li_88012",
      "status": "DRAFT"
    }
  ]
}

```

:::

DataFlair stores `DF-CMP-VDWZQ4IT-329 → li_88012`. Nothing is serving yet.

## Step 6: A person takes it live (on your side)

Your ad-ops open the draft order `ord_55021` in your own console, review it and activate it. DataFlair takes no part in this step. The line starts serving on 1 August.

## Step 7: DataFlair pulls delivery to reconcile

Part way through the flight, and after it, DataFlair calls [`POST /reports`](/marketplace/ad-server-api/operations/reports/).

::: code-group

```http [HTTP]
POST /reports
Authorization: Bearer sk_live_9f2c…
Content-Type: application/json
```

```bash [curl]
curl -X POST "$BASE_URL/reports" -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" -d @request.json
```

:::

The request body:

```json
{
  "line_item_ids": ["li_88012"],
  "date_range": { "start_date": "2026-08-01", "end_date": "2026-08-31" },
  "granularity": "total"
}
```

Your response:

```json
{
  "rows": [
    { "line_item_id": "li_88012", "impressions": 98240, "clicks": 173 }
  ]
}
```

DataFlair matches `li_88012` to the booked line `DF-CMP-VDWZQ4IT-329`. It records 98,240 delivered impressions against the goal of 100,000. It cross-checks its own tracking counts: clicks from the `/go` link and impressions from the in-frame pixel. Then it settles the campaign and pays the publisher net of commission. Reconciliation is done.

## Idempotency in action

Now take a different run of the same booking. Acme's creative was still in review when you approved the reservation. The first `POST /orders` carried no `creatives` array, and the creative followed once it was approved. That is an **update**, not a retry. The second push uses a **new** `idempotency_key` with the **same** order and line `external_ref` values:

```json
{ "idempotency_key": "DF-CMP-VDWZQ4IT-2", "order": { "external_ref": "CMP-VDWZQ4IT" }, "...": "same line external_refs, now with the creative" }
```

Your platform matches on `external_ref` and does **not** create a second order or line. It updates the existing line `li_88012` with the creative and returns the same ids with `200`. DataFlair's stored mapping does not change.

Posting the *original* `idempotency_key` again with a changed body is a different case. That is a `409`, and it is not an update path. See [POST /orders](/marketplace/ad-server-api/operations/orders#idempotency-and-recovery) and [Conventions](/marketplace/ad-server-api/conventions#idempotency).
