Authentication

Overview

Delivr.ai data APIs (events, audiences, taxonomy, identity lookup) authenticate with an API key + secret pair, created from the dashboard. A key is scoped either to your whole organization or to a single project. See Project-scoped API keys.

X-Api-Key: dlvr_...
X-Api-Secret: ...

There is one supported way to create keys: the dashboard at https://app.delivr.ai/{org_id}/settings/api-keys. The dashboard is the only place that displays the secret half. We do not recommend (and are phasing out) JWT-based programmatic key creation.

First time here? Click Get started at delivr.ai/api, sign up with your work email and accept the usage-based terms online. Verify your email, set your password from the activation email, then sign in at app.delivr.ai. Joining a team that already has an account? Ask an admin to invite you from Settings > Members. Contract customers are set up and invited by their Delivr contact, and for custom pricing or terms, talk to sales. Once you are signed in, everything from API keys to project settings to inviting teammates is self-serve.

Base URL: https://api.delivr.ai


Create your API key in the dashboard

The fastest, safest way to get a working key + secret pair is to create one through the UI.

  1. Sign in at app.delivr.ai and switch to the organization you want the key to belong to.

  2. Open Settings > API keys. The direct link is https://app.delivr.ai/{org_id}/settings/api-keys (replace {org_id} with your organization ID).

  3. Click Create API key, and give the key a descriptive name such as "Production export job".

  4. Under Scope, keep Organization (every project), or choose one project for a project-scoped key.

  5. Tick Campaign writes only if the key will create, change or launch campaigns. It adds campaigns:write, fixed at creation.

  6. Under Environment, choose Test or Live. New keys default to Test.

  7. Click Create key.

  8. In the confirmation dialog, copy both values into your secrets manager before closing it:

    ValueFormatWhere to use it
    api_keydlvr_ prefixX-Api-Key header on every request
    api_secrethex stringX-Api-Secret header on every request

Test or Live. New keys default to Test. A test key returns deterministic mock data from the sandbox and is never billed. On the intent endpoints, its dry runs return sample counts, not your real billable count. Every response carries the header X-Delivr-Mode: test. A test key creates no pixel.

A live key hits the real pipeline and is billed. See Rate Limits and Metering. To create real pixels, or to judge real data before you pay, use a live key. Previews (masked to company, department and seniority band, with field coverage) and dry_run requests bill nothing and need no payment method.

The full api_secret is shown once, at creation time. If you close the dialog without saving it, rotate the key. The list view shows only the key prefix. It cannot reveal the secret again.

An organization key reaches every project in the organization that owns it. A project-scoped key reaches one project.

Anyone with both halves can call the API with that scope. Treat the pair like a password. If you suspect a leak, rotate the key. Never commit either value to source control.


Rotate a key

Rotate a key when you lose its secret or suspect a leak. Rotate issues a new key and a new secret for the same scope and environment. The old key keeps working for 24 hours, so callers can move to the new pair with no outage.

  1. Open Settings > API keys.
  2. Open the key's row menu and choose Rotate.
  3. Click Rotate key.
  4. Copy the new API key and API secret into your secrets manager. Both values change.
  5. Tick I have saved my credentials and click Close.
  6. Deploy the new key and secret to every caller within 24 hours. After 24 hours, the old key is refused.

During the 24 hours, the old key shows as Rotating. If you know the old key leaked, revoke it as soon as every caller uses the new pair.

To retire a key, choose Revoke in the same menu. Revoke stops the key at once, and every caller that still sends it gets a 401. To replace a key that is in use, rotate it. Do not revoke it and create a new one.


Make your first authenticated request

Once you have the pair, every request to api.delivr.ai carries both headers. The two-header pattern works on every data endpoint (events, audiences, taxonomy, identity lookup).

curl "https://api.delivr.ai/api/v3/events_schema?source=pixel" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "X-Api-Secret: YOUR_API_SECRET"

Response (200 OK)

{
  "fields": [
    { "name": "event_id", "data_type": "Utf8", "nullable": false },
    { "name": "event_type", "data_type": "Utf8", "nullable": false },
    { "name": "ts_millis", "data_type": "Int64", "nullable": false },
    { "name": "resolved", "data_type": "Boolean", "nullable": true }
  ]
}

If you get a 401 with code api_key_invalid, check that you copied the full dlvr_-prefixed value. If you get code api_secret_invalid, the secret half does not match the key on file. Send the key and the secret that were issued together. If the secret is lost, rotate the key.

Error codes

A refused request returns a JSON body with three fields. error is a readable message, code is a stable value for your code to branch on, and help_url links the page that explains the fix.

{
  "error": "API secret is required",
  "code": "api_secret_required",
  "help_url": "https://docs.delivr.ai/docs/authentication#why-both-halves-matter"
}
StatuscodeFix
401api_key_requiredSend the X-Api-Key header.
401api_key_invalid_formatSend the full key, starting with dlvr_.
401api_key_invalidCheck the key. A revoked key, or an old key 24 hours after a rotation, also returns this code. Send the newest key.
401api_key_inactiveThe key is not active. Create a new key in Settings > API keys.
401api_key_expiredThe key has expired. Create a new key in Settings > API keys.
401api_key_disabledThe key was disabled. Contact your Delivr contact.
401api_secret_requiredSend the X-Api-Secret header with the key.
401api_secret_invalidThe secret does not match the key. After a rotation, send the new key and the new secret together. If the secret is lost, rotate the key.
403enterprise_key_requiredThis route needs an enterprise API key.
403enterprise_key_not_allowedAn enterprise key can call only the enterprise routes. Use an organization or project key.
503auth_unavailableWait for Retry-After, then retry.

Match on code, not on the error text.


SDKs and MCP

Typed clients for TypeScript and Python cover the data APIs on https://api.delivr.ai. Both take the same key + secret pair.

LanguagePackageInstall
TypeScript@delivrai/sdknpm i @delivrai/sdk
Pythondelivr-sdkpip install delivr-sdk

See SDKs for setup and coverage.

The hosted MCP server, for AI agents, is at https://api.delivr.ai/mcp. It uses the Streamable HTTP transport. See Use Delivr from an AI agent for OAuth sign-in, API key use, and the tools that bill.


Why both halves matter

A leaked api_key on its own is not enough to call the API. The platform compares the supplied api_secret against a stored hash on every request, in constant time, and rejects mismatches with 401. Pair-based auth means:

  • Keys can be safely surfaced in logs or dashboards. The secret stays out of those code paths.
  • Per-key access logs show exactly which credential made each call.

If your organization has secret_required enabled (default for new keys), a request that omits X-Api-Secret is rejected with 401 and code api_secret_required. Older keys may still accept secret-less calls during migration, but every new integration should send both headers.


Per-endpoint headers

This table describes an organization key. A project-scoped key reaches fewer of these, and only for its own project. See Project-scoped API keys.

APIRequired headersNotes
Events (/api/v3/events/{tier})X-Api-Key, X-Api-SecretThe tier route fixes both the billing tier and the identity fields returned. Scope with source plus pixel_id, project_id, or campaign_id. See Events API v3.
Audiences (/api/v1/audiences*)X-Api-Key, X-Api-SecretName the project with the X-Project-Id header or a ?project_id= query param (equivalent). It is required for create/list/sample.
Taxonomy (/api/v1/taxonomy/*)X-Api-Key, X-Api-SecretRead-only across the org.
Identity (/api/v1/lookup)X-Api-Key, X-Api-SecretHEM/email resolution.
Organization (/api/v1/organization)X-Api-Key, X-Api-SecretGET to read, PUT to update, or DELETE to remove your own organization.
Organization members (/api/v1/organization/user*)X-Api-Key, X-Api-SecretCreate/list/retrieve/update/delete members of the key's organization.
Project management (/api/v1/project*)X-Api-Key, X-Api-SecretCreate/list/retrieve/update/delete projects in the key's org. Create and list need no project id. Retrieve is GET /api/v1/project/retrieve/{project_id}. Update and delete take project_id in the body. A project outside the key's org is rejected.
Project members (/api/v1/project/user*)X-Api-Key, X-Api-SecretManage members of a project. Name the target project with a project_id field (writes) or ?project_id= (reads). The project must belong to the key's org.
Pixel management (/api/v1/pixel*)X-Api-Key, X-Api-SecretCreate/list/retrieve/update/delete pixels. Name the target project with a project_id field (writes) or a ?project_id= query parameter (reads).

Project-scoped API keys

A project-scoped key is bound to one project when you create it. It uses the same X-Api-Key and X-Api-Secret headers as an organization key, but it reads and writes only its own project's data. Use one to give a client, a team, or a single integration access to one project without exposing the rest of your organization. If you run projects for several clients, issue one key per client.

Create one

In Settings > API keys, click Create key and choose a project under Scope. A project admin creating a key from inside their project gets a key bound to that project. The scope selector is not shown.

The scope is fixed at creation. To change it, revoke the key and create another.

What a project key confines

  • Naming another project, or a pixel, campaign or record that belongs to another project, is refused. The status code depends on the API. See the table below.
  • An endpoint a project key cannot use answers 403, with a message saying the endpoint is not available to a project-scoped API key.
  • Usage on the events API, identity lookup, the intent API and audience downloads is attributed to the key's project.
  • A project key is held to its project's budget as well as your organization's, on every route that bills. When the organization's budget refuses a project key, the 402 omits the organization's spend and budget figures. See Project budgets.

Where a project key works

APIRoutesWith a project key
Events/api/v3/events/{tier}, /api/v3/events/{tier}/visitors and /api/v3/events/{tier}/schema, /api/v3/events/web_event/counts, /api/v3/events_schema, /api/v1/events, /api/v1/event_countsA request scoped to the organization reads only the key's project. Another project's project_id returns 404 (project not found), another project's pixel 404 (pixel not found), and another project's DSP campaign 404 (campaign not found). The visitors and counts routes need project_id or pixel_id, which must be the key's own.
Identity lookup/api/v1/lookup, /api/v1/lookup/batchSame as an organization key: lookup takes no project.
Audiences/api/v1/audiences*Name the project as usual (X-Project-Id or ?project_id=, required). It must be the key's project. Any other returns 403 (access denied). Cloning an audience into another project is refused with 403 too. The audience schema and the audience template catalog work as they do for an organization key.
Audience usageGET /api/v1/audiences/linkage-usageThe key's project only. Any other returns 403 (project_id does not match API key scope).
Intent/api/v1/intent/*Same as an organization key: intent data belongs to no project. An audience_id on /api/v1/intent/people must belong to your organization.
Intent topic stats/api/v1/stats/tables/intent-daily-stats, /api/v1/stats/tables/intent-daily-per-topic-statsSame as an organization key: topic-level data.
Taxonomy/api/v1/taxonomy/*Same as an organization key: a shared vocabulary.
Field catalog/api/v1/field-catalog*Same as an organization key: a shared catalog.
Lists, exports, integrations, campaigns/api/v1/lists*, /api/v1/exports*, /api/v1/integrations*, /api/v1/campaigns*A request without project_id is answered for the key's project. Another project's project_id returns 403 (project_id does not match API key scope). The campaign_id and audience_id filters return 403. A record in another project, requested by id, returns 404.
Resolution statisticsGET /api/v1/resolution/statisticsThe key's project only.
Billing usageGET /api/v1/billing/usage, /api/v1/billing/usage/by-meter, /api/v1/billing/usage/daily-by-meterUsage for the key's project only.
Contact queryPOST /api/v1/contact/querySame as an organization key.
Who am IGET /api/v1/whoamiLists only the key's project.
Pixels/api/v1/pixel*Pixels in the key's project only. Name the project as usual. Another project returns 401.
Your projectGET /api/v1/project/retrieve/{project_id}, PUT /api/v1/project/updateThe key's own project only. Another returns 401.

Not available to a project key (403): the organization itself (/api/v1/organization), organization members (/api/v1/organization/user*), creating, listing and deleting projects, and project members (/api/v1/project/user*). Also not available is the billing estimate (GET /api/v1/billing/estimate), which is priced on the whole organization's usage. The subscription, invoices and usage limits (GET /api/v1/billing/subscription, GET /api/v1/billing/invoices, GET /api/v1/billing/usage-limits) are not available either, because they belong to the whole organization. Use an organization key for these.


Password sign-in is being removed

Signing in with an email and password to get a token, through POST /auth/v1/login or the Partner API's POST /public/core/auth/issue, is being removed. If an integration signs in that way, move it to a dashboard-created API key + secret pair now.

Calls that send X-Api-Key without X-Api-Secret still work on older keys, but they are also on the deprecation path. Send both headers.


Next steps


Did this page help you?