Keepable docs
Sender API

Content types in depth

The content types the send endpoint accepts, their stable vs preview status, and the structured attributes each carries. Grounded in the Nigerian market.

Every content delivery names a content_type. It does two things: it tells the recipient app how to frame the item (icon, grouping, the by-type filter), and it selects which attributes schema applies.

The envelope is identical for every type. content_type is not a discriminator that changes the required envelope fields; it selects the optional attributes shape. Adding a new content type is an additive change; it never disturbs the envelope or existing types.

Stable vs preview

The vocabulary is split by release status:

StatusTypes
Stableletter, payslip, invoice, statement
Previewcredit_note, booking, receipt, form, survey_invitation, campaign, registered_letter
  • Stable types are production-ready; their semantics and attributes are covered by the deprecation policy.
  • Preview types are visible and usable, but their semantics and attributes shape may still change. Don't depend on them in production yet.

The status is also machine-readable in the spec as x-keepable-status, so tooling can surface it.

An unknown content_type is rejected with 422. The recipient's by-type filter relies on the value, so the set is closed and validated.

Why these are the stable four

The stable set is the highest-frequency, identity-anchored mail in the Nigerian market:

  • payslip: issuing payslips is a statutory payroll record (the Labour Act, the Personal Income Tax Act, the Pension Reform Act), so every formal employer produces them monthly.
  • invoice: FIRS National E-Invoicing is mandatory for large taxpayers from August 2025 and extends to smaller VAT-registered businesses through 2026. A cleared e-invoice can travel to the inbox carrying its clearance reference (irn).
  • statement: monthly bank statements are a customer right, and a signed, identity-anchored channel is a direct answer to the "fake credit alert" fraud the CBN repeatedly warns about. Pension (RSA) statements fit here too.
  • letter: the generic baseline for anything without a more specific type.

Attributes by type

attributes carries the structured, machine-readable fields the recipient app renders into a rich view. Four types have a typed schema today; every other type accepts a free-form attributes object.

Put anything the recipient must see or act on (an amount, a due date, a location) in attributes or a part, never in metadata, which Keepable never reads or renders.

payslip (stable)

FieldRequiredNotes
pay_periodyesThe period covered, e.g. "2026-03" (YYYY-MM).
net_payyesTake-home pay as a decimal string. Never a float.
gross_paynoGross pay as a decimal string.
currencyyesISO 4217 code, e.g. NGN.
{ "pay_period": "2026-03", "gross_pay": "320000.00", "net_pay": "250000.00", "currency": "NGN" }

invoice (stable)

FieldRequiredNotes
amountyesTotal due as a decimal string.
currencyyesISO 4217 code.
due_dateyesISO 8601 date (YYYY-MM-DD).
invoice_numbernoYour invoice reference.
irnnoFIRS Invoice Reference Number from e-invoicing clearance, where applicable.
{ "amount": "125000.00", "currency": "NGN", "due_date": "2026-06-30", "invoice_number": "INV-88213", "irn": "IRN-7F3A9C20-2026" }

statement (stable)

FieldRequiredNotes
period_startyesISO 8601 date.
period_endyesISO 8601 date.
currencyyesISO 4217 code.
opening_balancenoDecimal string.
closing_balancenoDecimal string.
account_referencenoThe account or RSA reference the statement covers.
{ "period_start": "2026-03-01", "period_end": "2026-03-31", "currency": "NGN", "opening_balance": "10000.00", "closing_balance": "42500.50" }

booking (preview)

FieldRequiredNotes
starts_atyesRFC 3339 date-time.
locationyesWhere the appointment is.
ends_atnoRFC 3339 date-time.
referencenoYour booking reference.
{ "starts_at": "2026-04-02T10:30:00Z", "ends_at": "2026-04-02T11:00:00Z", "location": "Lagos Island branch", "reference": "BK-4471" }

Money, dates, currencies

Across every typed schema: money is a decimal string ("125000.00", never a float), dates are ISO 8601 (YYYY-MM-DD), date-times are RFC 3339, and currencies are ISO 4217 codes (NGN). Violations are rejected 422.

Adding a content type

New types graduate from preview to stable as their semantics settle, and new preview types appear as the market calls for them (receipt and registered_letter are reserved this way as forward-looking preview types). Because a type is just a vocabulary entry plus an optional attributes schema, adding one never changes the envelope or any existing type, so your integration keeps working untouched.

When you need a signature

If the recipient must sign rather than just read, that is not a content type at all: use an agreement, which returns a signed, tamper-evident covenant.