adaptlive
← Integrations

Integration security

Treat API keys + webhook secrets as production credentials. Verify every webhook signature you receive. Prefer narrow scopes. Rotate keys you no longer trust.

API keys

Keys are formatted ak_live_<32 base32 chars> for production, or ak_test_… for the sandbox. We persist a SHA-256 hash plus the first 8 + last 4 characters — the full secret is shown once at mint time and is unrecoverable afterwards.

Storage

Never commit keys to version control. Use environment variables, a secret manager (Vault, AWS Secrets Manager, Doppler, 1Password Connect), or your platform's deployment-time secret store (e.g. Vercel env vars).

Least scope

Mint a key with the smallest scope (READ, WRITE, or ADMIN) that the integration needs. Webhook subscription CRUD requires ADMIN — a service that only sends SMS does not.

Suspected leak

Revoke immediately at /portal/api-keys. Revocation propagates on the next cache flip — within a minute every request using that key returns 401 unauthorized. Mint a replacement before you revoke if you need zero downtime.

# Environment variable setup (recommended)
export ADAPT_API_KEY="ak_live_..."

# Don't do this — keys in source ship to every reader of the repo
const apiKey = "ak_live_..."

Verifying webhook signatures

Every webhook delivery carries a X-AdaptLive-Signature header in this shape:

X-AdaptLive-Signature: t=1717009200,v1=8a3e…<64 hex chars>
  • t — the unix-second timestamp at the moment we signed.
  • v1 — HMAC-SHA256(${t}.${rawBody}) using the subscription's whsec_… signing secret, hex encoded.

Reconstruct the same payload (`${t}.${rawBody}`) on your side, recompute the HMAC, and compare with crypto.timingSafeEqual(or your language's constant-time equivalent).

Replay protection. Reject anything where |now - t| > 300s.

// Node.js — copy/paste verifier
import crypto from "node:crypto";

export function verifyAdaptLiveWebhook(args: {
  signingSecret: string; // 'whsec_...' from POST /v1/webhooks
  rawBody: string;       // the exact bytes you received (don't re-stringify)
  signatureHeader: string; // value of 'X-AdaptLive-Signature'
  toleranceSeconds?: number;
}): { ok: true } | { ok: false; reason: string } {
  const parts = args.signatureHeader.split(",").map((s) => s.trim());
  const tEntry = parts.find((p) => p.startsWith("t="));
  const v1Entry = parts.find((p) => p.startsWith("v1="));
  if (!tEntry || !v1Entry) return { ok: false, reason: "Malformed header" };

  const t = Number(tEntry.slice(2));
  const v1 = v1Entry.slice(3);
  const now = Math.floor(Date.now() / 1000);
  const tolerance = args.toleranceSeconds ?? 300;
  if (!Number.isFinite(t) || Math.abs(now - t) > tolerance) {
    return { ok: false, reason: "Timestamp outside tolerance window" };
  }

  const expected = crypto
    .createHmac("sha256", args.signingSecret)
    .update(`${t}.${args.rawBody}`)
    .digest("hex");

  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(v1, "hex");
  const ok = a.length === b.length && crypto.timingSafeEqual(a, b);
  return ok ? { ok: true } : { ok: false, reason: "Signature mismatch" };
}

Python, Ruby, and Go verifier snippets live on the Webhooks reference page.

HTTPS only

Subscription URLs must use HTTPS. Plain HTTP is rejected at create time.

Idempotent handlers

Deliveries are retried on transport error. Dedupe on the eventId in the body so a retry never double-processes the same event.

Receiver hardening

Use the raw body for verification

JSON re-stringification reorders keys and changes whitespace, which breaks the HMAC. Wire your framework's raw-body middleware (express.raw(), await req.text() in App Router before JSON parsing) and verify against those exact bytes.

Respond fast, defer work

Verify, ack with 2xx, then push the payload to a background queue. Anything >10s gets retried as if it failed.

TLS 1.2 or higher on the receiver

Use HTTPS endpoints with TLS 1.2 or higher. If you self-terminate TLS, configure your reverse proxy to disable legacy versions.

Security checklist

  • Store API keys in environment variables or a secret manager — never source control.
  • Use the smallest API key scope the integration needs.
  • Verify every webhook signature with constant-time comparison.
  • Reject deliveries whose timestamp is more than 5 minutes off.
  • Verify against the raw request body, not a re-serialized copy.
  • Dedupe on eventId so retries are safe.
  • Revoke + rotate the moment a key looks compromised.

For platform-level compliance posture (SOC 2, GDPR, CCPA, TCPA, HIPAA-aware) see the Trust Center.

We use essential cookies to keep the app secure. Optional cookies help us improve reliability and measure campaigns. Cookie policy