Keepable
Foundations

Authentication

One organisation-scoped API key, sent as a bearer token. Its prefix chooses the data plane, and its scopes decide which operations it may call.

The Sender API authenticates with an API key: an opaque bearer credential for 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.

POST /sender/v2/correspondence HTTP/1.1
Host: api.keepable.co
Authorization: Bearer kp_live_aZ8mQqWRtY2I4eK7hPnLrXv...
Idempotency-Key: 9f1c8e2a-7b3d-4f10-9a2e-6c5b4d3e2f1a
Content-Type: application/json

A key is scoped to your organisation. There is no tenant id in the path; the key identifies who you are.

The prefix is the environment

kp_sandbox_ keys act on the free synthetic plane. kp_live_ keys act on real recipients and debit your prepaid wallet. Same host, same paths, same contract: the only thing that changes is which key you send. See Environments.

Getting a key

Keys are minted in the portal, not over this API. Minting is what creates access, so it is an act of a person with authority in your organisation rather than of a credential that already exists, and there is no bootstrap regress to solve.

Sandbox, in about a minute. Sign up with your organisation's name and a work email, then mint a kp_sandbox_ key: no contract and no KYB. Start at Sandbox.

Live, once approved. After your organisation accepts the Sender Agreement, clears KYB, and funds its wallet, mint a kp_live_ key from the dashboard. See Going live.

Getting into the portal to do that is a passkey, a one-time email link, or your work account once an administrator has connected your work directory (see Work connections). There is no password to store, rotate, or leak, and none of those three is an API credential: a session signs a person in, a key signs a server in, and neither substitutes for the other.

What you can do over the API is manage the keys you already hold: list them, read their metadata, rotate a secret you think has leaked, and revoke one. Those are incident response, they belong in automation, and none of them widens anything.

POST https://api.keepable.co/sender/v2/api-keys/key_01J8ZQ4T/rotate
Authorization: Bearer {{KEEPABLE_TOKEN}}
Content-Type: application/json

{ "grace_seconds": 3600 }

A secret is returned only when the key is created or rotated. Keepable stores a hash, never the secret. List responses redact it to the prefix. If you lose one, rotate rather than mint.

Confirm your wiring

GET /me reflects the caller the auth middleware derived from your key. It touches no business state, so it is the cheapest way to check a bearer is wired correctly.

GET https://api.keepable.co/sender/v2/me
Authorization: Bearer {{KEEPABLE_TOKEN}}
{
  "organization_id": "org_01J8ZQ4T",
  "organization_name": "Anchor Microfinance Bank",
  "plane": "live",
  "tier": "standard",
  "approved": true,
  "scopes": ["correspondence.write", "responses.read", "webhooks.read"]
}

Scopes

A key carries a set of scopes fixed at mint, and every operation names the one it needs. A call whose key lacks it is refused with 403.

ScopeGrants
org.readThe organisation profile and its work connections.
members.readMembers and routing groups: the desks a route can address.
directory.readSearch the connected work directory, live.
storage.readBrowse and read the connected work storage, live.
documents.readDocuments already in Keepable.
documents.writeUpload a document, or pull one from work storage.
plans.readRead plans.
plans.writeAuthor, update, and archive plans.
correspondence.readFiles, participants, minutes, covenants, exports.
correspondence.writeSend, route, run a plan, attach, remind, close, withdraw.
recipients.checkAsk whether one identifier is deliverable.
responses.readCollected answers, with sensitive values masked.
responses.sensitiveUnmask what came back: an answer, an export, a document.
responses.eraseErase one participant's answers, for an erasure request.
claims.readClaim codes on a tokenized send.
claims.writeVoid and reissue claim codes.
metrics.readDelivery and workflow rollups.
wallet.readPrepaid balance and ledger.
invoices.readInvoices and their PDFs.
webhooks.readEndpoints and delivery history.
webhooks.writeCreate, change, test, and re-secret endpoints.
audit.readYour organisation's audit log.
apikeys.readKey metadata.
apikeys.writeRotate and revoke keys.

Ask for the least each integration needs. Four of these repay a second look, because in each case two acts that look alike turned out to have different consequences:

  • responses.sensitive is separate from responses.read. Reading an answer and unmasking it are different acts. A key minted to pull form answers into a CRM should not thereby be able to read every applicant's BVN.
  • recipients.check is separate from correspondence.read. It is the cheap identity oracle here, and a send-only integration has no need of one.
  • directory.read and storage.read are separate from members.read and documents.read. Those are people and files already in Keepable; these reach live into your own directory and drive.
  • webhooks.read is separate from webhooks.write. Watching deliveries should not carry the right to re-point an endpoint at another host.
  • responses.erase is granted by nothing else. Deleting what somebody answered is not a stronger form of reading it, so it is not implied by responses.read or by responses.sensitive.

Two things a scope is not:

A scope is not reader rights. A restricted correspondence is scoped to its participants and named readers. No key scope overrides that.

Some request shapes need a second scope. Collecting an export made with include_sensitive, or retrieving a document that came back as a response, needs responses.sensitive on top of the scope that reaches the operation at all. The operation's own description says when.

mTLS for tier-1 senders

Tier-1 senders (the central bank, CBN-licensed banks, federal agencies) present a client certificate in addition to the key. It is verified at the gateway and layered on top, not instead: a tier-1 grade keeps sending disabled until the certificate is live, and there is no weaker interim path. Partner Engineering provisions it during onboarding.

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

On this page