Skip to main content

Authentication

Two key types.

Platform key (server-to-server, full CRUD)​

Use a Bearer token from your backend:

Authorization: Bearer <key_id>.<key_secret>

The format is key_id.key_secret, both halves are required. key_id is the 24-character identifier that already begins with dzpk_live_ (for example dzpk_live_xxxxxxxxxxxxxx.<48-hex secret>), so don't add the prefix again. The key secret is 48 hex characters and is shown once at creation, never again.

Public key (external sites & apps; HMAC-signed)​

For counting end-user signups and custom events from a site or app you run outside DZBuild. Your backend calls POST /v1/signups / POST /v1/events and signs each request — never expose the signing secret to a browser.

Headers:

Authorization: DZ-Public <key_id>
X-DZ-Timestamp: <unix_seconds>
X-DZ-Nonce: <32-hex>
X-DZ-Signature: hex(hmac_sha256(signing_secret, key_id + "\n" + nonce + "\n" + ts + "\n" + sha256(body)))

Here too, key_id already begins with dzpub_live_ — pass it exactly as returned. The signing secret is 64 hex characters.

The nonce is single-use per key for one hour and prevents replay. The timestamp must be within ±5 minutes.

A new public key is inert until it is activated

A type: public key you create through POST /v1/keys returns 401 until it has been activated for API traffic. Contact support right after creating one to have it activated. Platform (Bearer) keys are not affected — they work immediately.

Scopes​

Every key has a list of scopes. Default platform scopes: store:read, store:write, products:read, products:write, orders:read, orders:write, customers:read, landing_pages:read, landing_pages:write, promos:read, promos:write, pixels:read, pixels:write, shipping:read, shipping:write, webhooks:read, webhooks:write, usage:read, analytics:read, whatsapp:read, whatsapp:send. A key created in the dashboard gets exactly this list. delivery:send and ai:generate are not in it, so a key created in the dashboard or through POST /v1/keys gets 403 on POST /v1/orders/{id}/send-to-delivery and POST /v1/landing-pages/generate.

Default public scopes: signups:write, events:write.

Scopes are checked on reads as well as writes: a key without orders:read gets 403 with Missing scope: orders:read on GET /v1/orders. store:write covers the store settings, design, theme and home page writes. signups:write and events:write cover POST /v1/signups and POST /v1/events.

Key management (/v1/keys) is gated by key type, not by scope: a public key calling it gets 403 — "Key management requires a platform key".

A platform key minted through POST /v1/keys gets only the scopes the calling key already holds (403 when they share none). A type: public key always gets signups:write and events:write.

Creating a key​

The owner of a store on an active Enterprise plan creates personal keys in the dashboard at Settings → API (/dashboard/api). A new key is enrolled in the pilot automatically and works at once: there is nothing to ask support for. The page shows two values, once, when the key is created:

  • Bearer token: the whole credential, already in the key_id.key_secret form. Send it as it is, Authorization: Bearer <bearer token>, without adding anything to it.
  • Signing secret: never goes in the Authorization header, and Bearer calls don't use it. Keep it private like the token.

A store holds at most 3 active personal keys. Keys held by installed apps, Copilot or a connector don't take one of those slots.

Assistant connections​

A connection made for Copilot or a connected AI assistant (Claude or ChatGPT) can cover several of the merchant's stores, with one key per store. Two endpoints let a key held by such a connection read which stores it covers and change which one is active. Any other key, including one created in the dashboard, gets 404 not_found with This key does not belong to an assistant connection.

GET /v1/connection​

Needs store:read.

curl https://api.dzbuild.app/v1/connection \
-H "Authorization: Bearer $DZ_KEY"
{ "data": { "connection_id": 100, "client_name": "Claude", "active_store_id": 10,
"stores": [ { "id": 10, "name": "My store", "slug": "my-store" },
{ "id": 20, "name": "My second store", "slug": "my-second-store" } ] },
"meta": { "request_id": "...", "api_version": "v1" } }

client_name is the name of the assistant app. A connection approved before connections could cover several stores lists its one store.

POST /v1/connection/active-store​

Needs store:write and an Idempotency-Key. store_id in the body names the store to make active.

curl -X POST https://api.dzbuild.app/v1/connection/active-store \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: active-store-20" \
-d '{"store_id": 20}'

The answer is the connection after the move, in the same shape as GET /v1/connection. The active store is a pointer, not a permission: every request still acts on the one store its key belongs to. A store outside the connection answers 403 store_not_in_connection, and a body without store_id answers 400 bad_request.

This page for AI toolsView as MarkdownOpen in ChatGPTOpen in Claude