Keepable docs
Foundations

Authentication

The Sender API authenticates with an API key, an opaque bearer credential you mint in the portal or the sandbox and send in the Authorization header, scoped to exactly the operations it needs.

The Sender API authenticates with an API key: an opaque bearer credential for pure server-to-server calls, with no user in the loop. There is no token exchange and no client_id / client_secret; the key is the credential. You send it in the Authorization header on every request.

Get an API key

Every key is prefixed to show its data plane: kpk_test_ keys are sandbox, kpk_live_ keys are live. The prefix alone selects the plane; there are no separate hostnames or accounts (see Environments). There are two ways to get your first key:

Sandbox, self-serve. Verify an email and get a kpk_test_ key in about a minute, with no contract and no KYB. Start at Sandbox.

Live, once approved. After your organisation clears approval (KYB) and accepts the sender agreement, mint kpk_live_ keys from the portal dashboard or, once you hold a key with apikeys.write, over the API. See Going live.

Once you hold a key with the apikeys.write scope, you can mint further keys over the API:

Mint a key
curl -X POST https://api.keepable.co/sender/api_keys \
  -H "Authorization: Bearer $KEEPABLE_TOKEN" \
  -H "Keepable-Version: 2026-05-24" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Production worker: invoice ingest", "scopes": ["content.write", "webhooks.write"] }'
201 Created: the secret is shown exactly once
{
  "id": "apk_01HXP",
  "name": "Production worker: invoice ingest",
  "prefix": "kpk_aZ8mQq",
  "scopes": ["content.write", "webhooks.write"],
  "created_at": "2026-05-30T08:00:00Z",
  "key": "kpk_aZ8mQqWRtY2I4eK7hPnLrXv..."
}

The full key is returned only on create; Keepable stores only a hash of it, never the secret. Save it immediately; list responses redact it to the prefix. If you lose it, rotate the key rather than minting a new one.

  • scopes may only subset the caller's own scopes; an empty array inherits the caller's full set. Minting and managing keys needs apikeys.write.
  • Rotate a key's secret with POST /sender/api_keys/{key_id}/rotate (the old secret stops working); revoke one with DELETE /sender/api_keys/{key_id}.
  • GET /sender/api_keys lists active keys; pass include_revoked=true to see revoked ones for audit.

Use the key

Send the key as a bearer token on every business request, alongside the Keepable-Version header:

POST /sender/tenants/ten_01HXP/contents HTTP/1.1
Host: api.keepable.co
Authorization: Bearer kpk_live_aZ8mQqWRtY2I4eK7hPnLrXv...
Keepable-Version: 2026-05-24
Idempotency-Key: 9f1c8e2a-7b3d-4f10-9a2e-6c5b4d3e2f1a
Content-Type: application/json

Keys are long-lived: hold a single durable credential and send it on every call. A request with a missing, malformed, or revoked key returns 401 Unauthorized.

Confirm your wiring with whoami

GET /sender/whoami reflects the caller the auth middleware derived from your key. It touches no business state, so it is the cheapest way to confirm an SDK has its bearer wired correctly:

curl https://api.keepable.co/sender/whoami \
  -H "Authorization: Bearer $KEEPABLE_TOKEN" \
  -H "Keepable-Version: 2026-05-24"
{ "kind": "sender", "id": "ten_01HXP", "scopes": ["tenant.read", "content.write"], "mode": "live" }

Scopes

A key is granted a set of scopes at mint time and can call only the operations those scopes cover. Request the least privilege each integration needs: a delivery worker needs content.write, not tenant.write.

ScopeGrants
tenant.readRead tenants.
tenant.writeCreate and modify tenants, company IDs, and branding.
content.readRecipient matching and content reads.
content.writeDeliver content.
agreement.readRead agreements and download covenants.
agreement.writeCreate and revoke agreements.
consent.readRead consent requests, decisions, and receipts.
consent.writeAuthor, send, and acknowledge consent requests.
forms.readRead form templates and responses.
forms.writeCreate and delete form templates and responses.
campaigns.readList and read campaigns and their metrics.
campaigns.writeCreate, author, and change the lifecycle of campaigns.
access.writeRequest and respond to access delegation.
webhooks.writeManage webhook endpoints and read deliveries.
apikeys.readList the organisation's API keys.
apikeys.writeMint, scope, rotate, and revoke API keys.
approval.readRead the organisation's sender-approval (KYB) status.
approval.writeSubmit and resubmit the sender-approval KYB.
delivery.readRead the organisation's delivery-dashboard metrics.
invoices.readList and download the organisation's invoices.
audit.readRead the organisation's audit log.

A call whose key lacks the required scope returns 403 Forbidden. A minted key can only carry scopes the caller minting it holds, so widening a key means holding those scopes yourself first.

mTLS for tier-1 senders

Tier-1 senders (the central bank, any CBN-licensed bank, and federal agencies) must present a client certificate (mTLS) in addition to the API-key bearer. The certificate is verified at the gateway and layered on top of the key above; it does not replace it, and the tenant in the key must match the tenant the certificate was issued for. A tier-1 grade keeps sending disabled until the certificate is live. There is no weaker interim path. Partner Engineering provisions it from a Keepable-operated CA during onboarding.

Treat an API key like a password. Never embed one in a browser, mobile app, or any distributed client; it belongs only in back-end services and server-side secret storage. If a key is exposed, revoke or rotate it immediately.