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:
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"] }'{
"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.
scopesmay only subset the caller's own scopes; an empty array inherits the caller's full set. Minting and managing keys needsapikeys.write.- Rotate a key's secret with
POST /sender/api_keys/{key_id}/rotate(the old secret stops working); revoke one withDELETE /sender/api_keys/{key_id}. GET /sender/api_keyslists active keys; passinclude_revoked=trueto 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/jsonKeys 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.
| Scope | Grants |
|---|---|
tenant.read | Read tenants. |
tenant.write | Create and modify tenants, company IDs, and branding. |
content.read | Recipient matching and content reads. |
content.write | Deliver content. |
agreement.read | Read agreements and download covenants. |
agreement.write | Create and revoke agreements. |
consent.read | Read consent requests, decisions, and receipts. |
consent.write | Author, send, and acknowledge consent requests. |
forms.read | Read form templates and responses. |
forms.write | Create and delete form templates and responses. |
campaigns.read | List and read campaigns and their metrics. |
campaigns.write | Create, author, and change the lifecycle of campaigns. |
access.write | Request and respond to access delegation. |
webhooks.write | Manage webhook endpoints and read deliveries. |
apikeys.read | List the organisation's API keys. |
apikeys.write | Mint, scope, rotate, and revoke API keys. |
approval.read | Read the organisation's sender-approval (KYB) status. |
approval.write | Submit and resubmit the sender-approval KYB. |
delivery.read | Read the organisation's delivery-dashboard metrics. |
invoices.read | List and download the organisation's invoices. |
audit.read | Read 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.
Introduction
Keepable is a secure digital mailbox for Nigeria. Send mail, run e-signature agreements, and publish forms to recipients identified by NIN, email, or TIN, all over the Sender API.
Conventions
The headers, idempotency rules, and pagination model that are constant across every Keepable API. Learn them once.