Authentication
Sign every request with your API key and secret using HMAC-SHA256.
Every request to a Dynamo API is signed. There are no cookies or bearer tokens — you authenticate by computing an HMAC signature over the request timestamp with your organization's secret.
You need two credentials, both issued per organization:
- API key — a public identifier (a document id), sent as
x-api-key. - Secret — used only to sign requests; never sent over the wire.
The External API and the Audience API share the exact same authentication — the same key/secret and the same signing scheme. Only the base URL differs.
The three headers
| Header | Value |
|---|---|
x-api-key | Your API key (the public identifier). |
x-date | The current timestamp as a standard date string (ISO-8601 recommended). |
x-signature | HMAC-SHA256(message = x-date, key = secret), hex-encoded (lowercase). |
The signature is computed over the x-date string only — not the body, path, or
any other header.
Generate x-date immediately before each request — the server only accepts a
timestamp within −5 seconds to +60 seconds of its own clock (replay
protection). A key pair is scoped to a single organization; every read and
write happens within that org.
Signing a request
const crypto = require("crypto");
function authHeaders(apiKey, secret) {
const xDate = new Date().toISOString(); // e.g. 2026-05-30T13:00:00.000Z
const signature = crypto
.createHmac("sha256", secret)
.update(xDate)
.digest("hex");
return { "x-api-key": apiKey, "x-date": xDate, "x-signature": signature };
}XDATE=$(date -u +%Y-%m-%dT%H:%M:%S.000Z)
SIG=$(printf '%s' "$XDATE" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
curl -H "x-api-key: $KEY" \
-H "x-date: $XDATE" \
-H "x-signature: $SIG" \
"$BASE/channels"The same helper works for both APIs — just point it at the right base URL:
| API | Base URL |
|---|---|
| External API | https://us-central1-seamless-pro.cloudfunctions.net/externalApi/v1 |
| Audience API | https://us-central1-seamless-pro.cloudfunctions.net/audience |
x-date just needs to be a date string the server can parse and that is fresh.
ISO-8601 (new Date().toISOString()) is recommended, but an HTTP/IMF-fixdate
string (new Date().toUTCString(), e.g. Mon, 14 Feb 2022 20:35:03 GMT) is also
accepted — as long as x-signature is the HMAC of that exact string.
Auth error responses
| Status | Meaning |
|---|---|
403 Missing request headers | One of x-api-key / x-date / x-signature is absent. |
403 Invalid signature or api key - Trace 1 | API key not found. |
403 Invalid signature or api key - Trace 2 | Signature does not match. |
403 Signature expired | x-date is outside the −5s..+60s freshness window. |
402 Payment Required | The organization is not active. |