SigFollow
Sign in

Public API

Authentication

Every Public API request is authenticated with an API key. Optional layers — HMAC signatures, IP allowlists, and resource whitelists — let you harden the surface as needed.

Bearer token

Pass the API key plaintext (prefix sflo_live_) in the Authorization header:

Authorization: Bearer sflo_live_AbCdEf...XYZ

The server stores only an HMAC hash of the key—your plaintext is unrecoverable after the key is created. Treat keys like passwords: rotate on compromise, scope them tightly, and never embed in client code.

`sflo_live_` is the production prefix
SigFollow does not currently issue test-mode keys. Use a dedicated sandbox tenant (different WABA accounts) for non-production traffic.

Scopes

Keys carry an immutable list of resource:action scopes. Each endpoint declares its required scope; requests missing the scope are rejected with 403 Forbidden and Meta error code 200 (Permission denied).

ScopeEndpoints
messages:sendPOST /v1/:phoneNumberId/messages
templates:readGET /v1/:wabaId/message_templates
templates:writePOST /v1/:wabaId/message_templates
contacts:readGET /v1/contacts*
contacts:writePOST/PATCH/DELETE /v1/contacts*
blacklist:readGET /v1/blacklist*
blacklist:writePOST/PATCH/DELETE /v1/blacklist*

Use the smallest set of scopes that satisfies your integration. A marketing scheduler should hold only messages:send; a CRM sync job should hold contacts:read + contacts:write and nothing else.

allowedPhoneNumberIds covers only :phoneNumberId paths
The phone-number whitelist on a key restricts only endpoints whose URL contains :phoneNumberId (messages.send today). Tenant-level resources like /v1/contacts and /v1/blacklistare governed by tenant scope alone—every key can see every contact in its tenant.

HMAC request signing (optional)

Keys created with Require signature on must include signed timestamps on every request. This prevents bearer-token leaks from being replayable—even if your key escapes a CI log, the attacker also needs the signing secret to forge a valid call.

Two headers are required when signing is enabled:

  • X-SigFollow-Timestamp — current Unix epoch seconds
  • X-SigFollow-Signature sha256=<hex_hmac> over the canonical input

The signed input is:

${timestamp}.${HTTP_METHOD}.${path}.${sha256_hex(raw_body)}

Concretely: timestamp, HTTP method (uppercase), request path (excluding host and query string), and SHA-256 hex of the raw request body, joined with literal periods. HMAC-SHA256 with the signing secret produces the signature.

Example signer in Node.js:

sigfollow-sign.jsjavascript
import crypto from "node:crypto";

function sign({ method, path, body, signingSecret }) {
  const timestamp = Math.floor(Date.now() / 1000);
  const bodyHash = crypto
    .createHash("sha256")
    .update(body ?? "", "utf8")
    .digest("hex");
  const signed = `${timestamp}.${method.toUpperCase()}.${path}.${bodyHash}`;
  const signature = crypto
    .createHmac("sha256", signingSecret)
    .update(signed)
    .digest("hex");
  return {
    "X-SigFollow-Timestamp": String(timestamp),
    "X-SigFollow-Signature": `sha256=${signature}`,
  };
}
5 minute time window
Requests are rejected when |now - timestamp| > 300 seconds to bound replay risk. Keep your client clock in sync (NTP).
Multipart uploads skip HMAC
The /media upload endpoint is exempt from HMAC checks even when the key requires signing — Nest cannot reliably hash a streamed multipart body. API key auth, rate limiting, IP allowlists, and scope checks still apply.

IP allowlist (optional)

Each key can carry a CIDR allowlist (IPv4 / IPv6). When set, requests from any other IP are rejected with 403. Useful when your integration runs from a fixed egress (e.g. a CRM in a single VPC).

Behind a proxy / load balancer? Ensure trust proxy is configured correctly on your SigFollow gateway, otherwise the IP check sees the proxy address instead of the real client.

What happens on failure

  • Missing / malformed Authorization401, Meta error code 190, header WWW-Authenticate: Bearer
  • Key revoked / expired → 401, code 190
  • Missing scope → 403, code 200
  • IP rejected → 403, code 200
  • HMAC missing / invalid / replayed → 401, structured error

See the full error reference for body schemas.