Skip to content

We call youDataFlair calls YOUR server. You implement this endpoint. You do not call DataFlair.

Conventions ​

Rules that apply to every endpoint. Get these right and the integration works on the unhappy paths as well as the happy one.

Transport and versioning ​

  • HTTPS only. DataFlair rejects plain HTTP before it sends a request.
  • JSON in, JSON out. Send Content-Type: application/json on requests with a body. Return application/json.
  • Version in the path. Root your API at a versioned base, for example https://ads.example.com/api/v1. Ship breaking changes as /v2. Do not change the meaning of a field in place.

Dates, timezone and flights ​

  • All dates are YYYY-MM-DD. They are calendar dates, not timestamps, unless a field is explicitly a datetime.
  • Dates are read in the account timezone you return from GET /health. DataFlair sends flight start_date and end_date in that zone. A flight of 2026-08-01 to 2026-08-31 means the whole of August in your timezone, including both ends.
  • Declare the timezone. GAM and Revive both drive line item flights from the network timezone. A silent mismatch is the classic "the campaign started a day early or late" bug.

Currency ​

  • Use ISO-4217 codes (EUR, USD and so on), declared once in GET /health.
  • DataFlair checks a booking's currency against it before it pushes an order. It stops on a mismatch and does not guess. Its GAM integration follows the same discipline.
  • You do not receive prices on the wire. Billing belongs to DataFlair.

Units ​

  • Impressions and clicks are integers.
  • Impression goals and forecast numbers are impression counts (unit_type: "IMPRESSIONS").

Idempotency ​

  • Write calls (POST /orders) carry an idempotency_key in the body and an Idempotency-Key header with the same value.
  • idempotency_key protects one call against being replayed. It is not a stable identifier for the life of the order. A repeat with the same key and the same body must return the same result (200) and create nothing new. A repeat with the same key and a different body is a 409. It means something upstream retried incorrectly. Do not apply the new body.
  • To update an order or line item you already created, for example to attach a creative once it clears approval, DataFlair sends a new POST /orders. It carries a fresh idempotency_key and the same order.external_ref and line_items[].external_ref as the original call. Match on external_ref, update only what changed, and return 200 with the existing order_id and line_item_id values. The full recovery contract is on POST /orders.
  • Read calls (/health, /inventory, /forecast, /reports) are idempotent by nature. DataFlair can repeat them freely.

Pagination ​

List responses (GET /inventory, and any large POST /reports) use cursor pagination, not offset.

  • Request. limit and cursor travel as query parameters on a GET (?limit=<n>&cursor=<opaque>). On a POST they travel as body fields next to the rest of the JSON payload, because that call already has a body. Use a sane default for limit (for example 50) and a cap (for example 200).
  • Response. Include next_cursor. Leave it out, or return null, on the last page.
  • Keyset paging, not OFFSET, keeps DataFlair from missing or double-reading rows when data changes in the middle of a page.

Rate limiting ​

  • If DataFlair goes over a budget, return 429 Too Many Requests with a Retry-After header in seconds.
  • DataFlair backs off and retries per Retry-After. It does not hammer your server. Publish your per-credential budget in your handoff notes so DataFlair can pace itself.

Error model ​

Every non-2xx response returns a JSON body:

json
{
  "code": "inventory_not_found",
  "message": "No ad slot exists for inventory_id 'slot_x'.",
  "details": {
    "inventory_id": "slot_x"
  }
}
FieldTypeRequiredMeaning
codestringyesA stable, machine-readable snake_case slug.
messagestringyesHuman-readable text. DataFlair keeps it as the last error. Do not put secrets in it.
detailsobjectnoOptional structured context.

HTTP status usage ​

StatusWhenDataFlair's reaction
200, 201SuccessProceeds
400Malformed requestTreats it as a bug, logs it and shows it
401Missing, invalid or expired credentialMarks the connection as error. The operator re-checks the credential.
403Authenticated, but missing a scopeMarks the connection as error. The operator widens the credential's scope.
404Unknown inventory_id, order_id or line_item_idSkips that item and reports it. It does not force the item onto a booking.
409Idempotency conflict (same key, different payload)Logs it. DataFlair does not retry it blindly.
422Valid shape, invalid values (a bad date range, an incompatible size)Shows your message, so the operator can fix the booking
429Rate limitedBacks off per Retry-After and retries
501A capability is not implemented (for example forecast for one slot)Degrades gracefully. See POST /forecast.
5xxServer errorRetries with backoff. A persistent 5xx shows as "your platform is unreachable".

Security expectations ​

  • Hash credentials at rest. Do not log them in plaintext. DataFlair holds the keys it stores to the same rule.
  • Do not echo the credential, or any secret, in a response body or an error.
  • Do not put credentials or personal data in a URL or query string. DataFlair sends credentials only in the Authorization header.
  • The credential you issue must be revocable, so either side can rotate it if it leaks, without downtime.

Docs version 1.0.1