The model
Two primitives carry the whole API. A plan is a reusable definition of a piece of work; a correspondence is the record of one instance of it. Letters, agreements, approvals, consent drives, and forms are kinds of correspondence, not separate APIs.
Everything in this API is one of two things.
Correspondence is the record of something you started: a letter you sent, an agreement you are collecting signatures on, a file you are routing desk to desk, a consent decision you asked for, a form you published. One resource, one lifecycle, one place to look.
A plan is a reusable definition of that work. Write the questions, the route, the retention, the conditions once, and run it as often as you like. A plan is optional; you can create correspondence directly.
POST /plans → a definition pln_01J8ZQ4T
POST /plans/{id}/runs → an instance of it cor_01J8ZQ4T
POST /correspondence → an instance, no definitionKind is a field, not an endpoint
A correspondence carries a kind, and the kind decides what else the object
means.
kind | What it is | The field that matters |
|---|---|---|
letter | Digital mail: a payslip, an invoice, a notice | body_html, document_ids |
agreement | A signature ceremony | recipients[].role, recipients[].mark |
approval | A file routed desk to desk | route |
consent | A decision on the record | consent |
form | Questions answered from the recipient's inbox | form |
They are not five APIs. GET /correspondence lists all of them, GET /correspondence/{id} reads any of them, and the same participants, minutes,
covenant, and export endpoints hang off every one.
A file accretes, and then it seals
The shape worth understanding before you write any code is what happens after you create a correspondence.
Every act against it appends a minute: delivered, opened, approved, signed, decided, submitted. Minutes are hash-chained, so the ledger can be checked against itself, and each one names who acted and when.
GET /correspondence/cor_01J8ZQ4T/minutesDocuments accrete the same way. A routed approval starts with one attachment and
picks up more as it moves between desks; POST /correspondence/{id}/documents
is how the third desk adds the memo the fourth desk needs.
When the work finishes, the file seals into a covenant: a tamper-evident
record of exactly what was agreed, by whom, in what order. The covenant carries
a receipt code, and anyone holding both the receipt and the file can check them
against each other at keepable.co/verify without an account and without
Keepable handing anything over.
GET /correspondence/cor_01J8ZQ4T/covenantWho a recipient is
Keepable delivers to verified people, not to addresses. A recipient is identified by their NIN or their email; a company by its CAC registration number. The difference between those matters more than it looks:
-
Email is a matching key. If no Keepable account matches it, the send does not happen and never will. Check with
POST /recipient-checksfirst. -
NIN and CAC number are hold keys. If nobody has registered against one yet, the item is retained: sealed, waiting, and delivered the day that person or company verifies. Which is most of the country, and the point of the product.
A CAC number is the one identifier here you can look up rather than be given. CAMA 2020 s.729 obliges a company to print it on every business letter, invoice and receipt it issues, and the Corporate Affairs Commission runs a free public register, so it is usually already on the document you are answering. Include the series prefix:
RC1234567andBN1234567are different entities, and a bare number is rejected rather than guessed at.
There is a third way to address somebody, for when you have neither: a claim code, where Keepable deposits the file against a declared name and you hand over a single-use code on your own channel.
What you will actually build
Authenticate. One organisation-scoped key, its prefix choosing the plane. See Authentication.
Create correspondence, directly or by running a plan. See Correspondence.
React to webhooks rather than polling: a participant acted, a file sealed, a claim was redeemed. See Webhooks.
What is not here
Some of what the sender portal does is not in this API, and that is deliberate rather than an omission waiting to be filled.
Clearing KYB, accepting the Sender Agreement, changing who is on your team,
authoring a campaign, connecting your identity provider, and minting an API key
are acts of a named person with authority, not of a credential. A minute
reading "key kp_live_5179 accepted your terms" is not evidence that anyone
accepted anything. You do those in the portal, once, and then integrate.
Introduction
Keepable is a secure digital mailbox for Nigeria. Deliver documents to verified people, route files desk to desk for approval, run signature ceremonies, ask for consent, and collect answers back, over one API.
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.