Authentication
The Sender API uses OAuth2 client-credentials. Exchange your client_id and client_secret for a short-lived bearer token, scoped to exactly the operations a credential needs.
The Sender API authenticates with OAuth2 client-credentials, a pure
server-to-server flow with no user in the loop. You exchange a client_id and
client_secret for a short-lived bearer token, then send that token on every
request.
Get a token
POST your credentials to the token endpoint with grant_type=client_credentials.
Request only the scopes the credential actually needs (see Scopes).
curl https://api.keepable.co/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$KEEPABLE_CLIENT_ID" \
-d "client_secret=$KEEPABLE_CLIENT_SECRET" \
-d "scope=content.read content.write"const res = await fetch("https://api.keepable.co/oauth2/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.KEEPABLE_CLIENT_ID!,
client_secret: process.env.KEEPABLE_CLIENT_SECRET!,
scope: "content.read content.write",
}),
});
const { access_token, expires_in } = await res.json();form := url.Values{
"grant_type": {"client_credentials"},
"client_id": {os.Getenv("KEEPABLE_CLIENT_ID")},
"client_secret": {os.Getenv("KEEPABLE_CLIENT_SECRET")},
"scope": {"content.read content.write"},
}
resp, err := http.PostForm("https://api.keepable.co/oauth2/token", form)
if err != nil {
return err
}
defer resp.Body.Close()
var tok struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
}
json.NewDecoder(resp.Body).Decode(&tok)The response is a standard OAuth2 token document:
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 3600
}Use the token
Send it as a bearer token on every business request, alongside the
Keepable-Version header:
POST /tenants/ten_01HXP/contents HTTP/1.1
Host: api.keepable.co
Authorization: Bearer <access_token>
Keepable-Version: 2026-05-24
Idempotency-Key: 9f1c8e2a-7b3d-4f10-9a2e-6c5b4d3e2f1a
Content-Type: application/jsonTokens are short-lived. Cache the token until shortly before expires_in
elapses and refresh it then, rather than requesting a new token per call. A request
with a missing, malformed, or expired token returns
401 Unauthorized.
Confirm your wiring with whoami
GET /auth/whoami reflects the caller the auth middleware derived from your
token. 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/auth/whoami \
-H "Authorization: Bearer $KEEPABLE_TOKEN" \
-H "Keepable-Version: 2026-05-24"{ "kind": "sender", "id": "ten_acme", "scopes": ["tenant.read", "content.send"] }Scopes
A credential is granted a set of scopes, and the token it mints can only carry
scopes the credential holds. 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. |
forms.read | Read form templates and responses. |
forms.write | Create and delete form templates and responses. |
access.write | Request and respond to access delegation. |
webhooks.write | Manage webhook endpoints and read deliveries. |
A call whose token lacks the required scope returns
403 Forbidden.
mTLS for tier-1 senders
Tier-1 senders (federal agencies, the central bank, and the top-five banks) may additionally be required to present a client certificate at the edge. mTLS is enforced at the gateway and layered on top of the OAuth2 bearer above; it does not replace it. If your organisation is in scope, Partner Engineering provisions the certificate during onboarding.
Never embed a client_secret in a browser, mobile app, or any distributed
client. The client-credentials flow is for back-end services only.
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.