Dynamo Developer DocsDynamo Developer Docs
Getting Started

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-SHA256 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. They differ only in the base URL and in x-api-version (required by the External API, accepted but ignored by the Audience API).

The headers

HeaderValue
x-api-keyYour API key (the public identifier).
x-dateThe request timestamp formatted as an IMF-fixdate string (GMT, US locale): EEE, dd MMM yyyy HH:mm:ss z — e.g. Sun, 06 Dec 2020 12:59:11 GMT.
x-signatureHMAC-SHA256(message = x-date, key = secret), hex-encoded.
x-api-versionThe API version you're coding against — currently 1. Required by the External API (a missing or unknown value returns 400); the Audience API accepts and ignores it.

The signature is computed over the x-date string only — not the body, path, or any other header. x-api-version is not part of the signature.

Generate the signature

  1. Format the timestamp. Take the current request time (milliseconds) and format it as an IMF-fixdate string in GMT, US locale: EEE, dd MMM yyyy HH:mm:ss z (e.g. Sun, 06 Dec 2020 12:59:11 GMT). In JavaScript this is exactly new Date().toUTCString(). Use this string as the x-date header value.
  2. Sign it. Compute HMAC-SHA256 over the x-date value, keyed by your organization secret, and encode the result as a hexadecimal string. Use this as the x-signature header value.
  3. Send x-api-key, x-date, x-signature, and x-api-version on the request.

The signature has a TTL of 30 seconds — generate x-date immediately before each request. An expired signature returns 403 Signature expired. A key pair is scoped to a single organization; every read and write happens within that org.

Signing a request

Sign a request (Node.js)
const crypto = require("crypto");

function authHeaders(apiKey, secret) {
  const xDate = new Date().toUTCString(); // e.g. Sun, 06 Dec 2020 12:59:11 GMT
  const signature = crypto
    .createHmac("sha256", secret)
    .update(xDate)
    .digest("hex");
  return {
    "x-api-key": apiKey,
    "x-date": xDate,
    "x-signature": signature,
    "x-api-version": "1", // External API only; ignored by the Audience API
  };
}
Sign a request (bash + openssl)
XDATE=$(LC_ALL=C TZ=GMT date "+%a, %d %b %Y %H:%M:%S GMT") # IMF-fixdate

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" \
     -H "x-api-version: 1" \
     "$BASE/channels"

The same helper works for both APIs — just point it at the right base URL:

APIBase URL
External APIhttps://us-central1-seamless-pro.cloudfunctions.net/externalApi/v1
Audience APIhttps://us-central1-seamless-pro.cloudfunctions.net/audience

Example

Check your implementation against this known pair — the same x-date and secret must produce the same signature:

HMAC-SHA256 over x-date (CryptoJS)
const date = 'Mon, 14 Feb 2022 20:35:03 GMT'; // new Date().toUTCString()
const secret = 'xZcLnmVhtl7MAlEgMnYI';

const signature = CryptoJS.HmacSHA256(date, secret).toString(CryptoJS.enc.Hex);
// bf4a8eea118be91013805e443cc4218e8410df136ed4276da5efb5dcab6812f7

Auth error responses

StatusMeaning
403 Missing request headersOne of x-api-key / x-date / x-signature is absent.
403 Invalid signature or api key - Trace 1API key not found.
403 Invalid signature or api key - Trace 2Signature does not match.
403 Signature expiredThe signature is older than its 30-second TTL.
402 Payment RequiredThe organization is not active.
400 Missing or unsupported x-api-version header. Supported versions: 1.(External API) x-api-version is absent or not a supported value.

API version

Send x-api-version: 1 on every External API request — it's required, and a missing or unknown value returns 400. Pinning a version means our deploys won't break you: today the only value is 1, and any future breaking change ships as a new version (announced here, with Deprecation / Sunset / Link response headers giving a migration window before an old version is retired). The /v1 in the base URL is the stable API generation; x-api-version is the revision within it. The Audience API doesn't require the header — it's accepted and ignored there.

Next steps

On this page