Keepable
Foundations

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 definition

Kind is a field, not an endpoint

A correspondence carries a kind, and the kind decides what else the object means.

kindWhat it isThe field that matters
letterDigital mail: a payslip, an invoice, a noticebody_html, document_ids
agreementA signature ceremonyrecipients[].role, recipients[].mark
approvalA file routed desk to deskroute
consentA decision on the recordconsent
formQuestions answered from the recipient's inboxform

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/minutes

Documents 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/covenant

Who 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-checks first.

  • 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: RC1234567 and BN1234567 are 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.

On this page