The extension API
Every route an installed extension is allowed to call — plus the admin-only management surface used to install, configure and reload extensions. For the architecture behind this API, see how extensions work.
Overview
The API is versioned in the URL and organised into two surfaces: the extension surface (/v1/ext/…), scoped to a single installed extension, and the management surface (/v1/extensions/…), gated to the admin:extensions:manage privilege.
Base URL https://api.tabibu.health Extension API /v1/ext/... Management API /v1/extensions/... Sandbox https://sandbox.tabibu.health
Breaking changes ship as a new major version with a twelve-month overlap. Additive changes (new fields, new endpoints) do not bump the version.
Authentication
Every request carries a bearer token scoped to one extension and one facility. Sandbox keys are prefixed sk_sandbox_, live keys sk_live_. Keys are issued once, at install time, and can be rotated without downtime.
curl https://api.tabibu.health/v1/ext/billing/invoices/inv_4Kp2 \ -H "Authorization: Bearer sk_live_51Hk..." \ -H "Tabibu-Version: 2026-06-01"
Errors
Errors return a 4xx or 5xx status with a JSON body under an error key.
{
"error": {
"type": "validation_error",
"message": "amount must be greater than 0",
"param": "amount"
}
}| Status | Type | Meaning |
|---|---|---|
| 400 | invalid_request | Malformed JSON or a required field is missing. |
| 401 | unauthenticated | The API key is missing, expired or revoked. |
| 403 | insufficient_scope | The key's privileges don't cover this route. |
| 404 | not_found | The resource doesn't exist, or isn't visible to this key. |
| 409 | idempotency_conflict | The same Idempotency-Key was sent with a different body. |
| 422 | validation_error | The request parsed, but a value failed validation. |
| 429 | rate_limited | Too many requests — see Retry-After. |
| 500 | internal_error | Something failed on our side. Safe to retry. |
Idempotency
RabbitMQ delivery is at-least-once, so a write may be retried after a redeliver. Send an Idempotency-Key header on every POST — Tabibu returns the original response for a repeated key instead of processing it twice. Keys are held for 24 hours.
Idempotency-Key: mpesa_ws_CO_070926183451_4821
Rate limits
120 requests per minute per extension key, refilled continuously. A throttled request returns 429 rate_limited with a Retry-After header, in seconds.
Pagination
List endpoints are cursor-paginated. Pass limit (default 20, max 100) and starting_after with the last ID from the previous page.
GET /v1/extensions?limit=20&starting_after=ext_mpesa_payments
{
"data": [ ... ],
"has_more": false
}Extension management
/v1/extensionsadmin:extensions:viewList installed extensions with status, version and last-updated time.
| Field | Type | Description |
|---|---|---|
| category | string | Filter by category, e.g. payment, insurance. |
| enabled | boolean | Filter to enabled or disabled extensions only. |
Request
curl https://api.tabibu.health/v1/extensions?category=payment \ -H "Authorization: Bearer sk_admin_..."
Response
{
"data": [
{
"name": "mpesa-payments",
"version": "1.2.0",
"category": "payment",
"is_enabled": true,
"status": "running"
}
],
"has_more": false
}/v1/extensionsadmin:extensions:manageInstall an extension from a registry manifest URL, or a local manifest for development.
| Field | Type | Description |
|---|---|---|
| manifest_url | string, required | Registry URL to the manifest.toml for the version to install. |
| config | object | Values for the fields declared under [extension.config]. |
Request
curl -X POST https://api.tabibu.health/v1/extensions \ -H "Authorization: Bearer sk_admin_..." \ -d manifest_url="https://registry.tabibu.dev/mpesa-payments/1.2.0" \ -d config[MPESA_SHORTCODE]="174379"
Response
{
"name": "mpesa-payments",
"version": "1.2.0",
"status": "installing",
"api_key_id": "key_9Fn2..."
}/v1/extensions/:name/configadmin:extensions:manageUpdate configuration values. Secret fields can be replaced but are never read back — the response only confirms is_configured.
Request
curl -X PUT https://api.tabibu.health/v1/extensions/mpesa-payments/config \ -H "Authorization: Bearer sk_admin_..." \ -d MPESA_ENV="production"
Response
{
"MPESA_SHORTCODE": { "value": "174379" },
"MPESA_PASSKEY": { "is_configured": true },
"MPESA_ENV": { "value": "production" }
}/v1/extensions/:name/reloadadmin:extensions:manageZero-downtime hot reload. A new container starts and begins consuming before the old one drains and exits — no message is lost.
Request
curl -X POST https://api.tabibu.health/v1/extensions/mpesa-payments/reload \ -H "Authorization: Bearer sk_admin_..."
Response
{ "name": "mpesa-payments", "status": "reloading" }/v1/extensions/:name/ui-tokenrequired_privilegeIssue a short-lived, scoped token for an extension's WebView. Rejected with 403 if the requesting user lacks the manifest's required_privilege.
Request
curl https://api.tabibu.health/v1/extensions/mpesa-payments/ui-token \ -H "Authorization: Bearer <user_session_token>"
Response
{ "token": "wvt_eyJhbGciOi...", "expires_in": 900 }Billing extension API
/v1/ext/billing/invoices/:idext:billing:readRetrieve an invoice to process a payment against — amount, currency, patient reference and current status.
Request
curl https://api.tabibu.health/v1/ext/billing/invoices/inv_4Kp2 \ -H "Authorization: Bearer sk_live_51Hk..."
Response
{
"id": "inv_4Kp2",
"patient_id": "pat_9K2mXw",
"amount": 1500.0,
"currency": "KES",
"status": "pending_payment"
}/v1/ext/billing/payments/:id/recordext:billing:writeRecord a completed payment against an invoice. Tabibu marks the invoice paid and publishes billing.payment_completed.
| Field | Type | Description |
|---|---|---|
| reference | string, required | The provider's transaction reference, e.g. an M-Pesa receipt number. |
| amount | number, required | Amount actually received, in the invoice's currency. |
| method | string | How it was paid — mpesa, card, cash. |
Request
curl -X POST https://api.tabibu.health/v1/ext/billing/payments/inv_4Kp2/record \ -H "Authorization: Bearer sk_live_51Hk..." \ -H "Idempotency-Key: mpesa_ws_CO_070926183451_4821" \ -d reference="QGR7XXYZ1" \ -d amount=1500.0 \ -d method="mpesa"
Response
{ "id": "inv_4Kp2", "status": "paid", "reference": "QGR7XXYZ1" }/v1/ext/billing/payments/:id/failext:billing:writeRecord a failed or cancelled payment attempt, so the invoice returns to pending_payment with a reason on file.
| Field | Type | Description |
|---|---|---|
| reason | string, required | Short machine-readable reason, e.g. insufficient_funds, cancelled. |
Request
curl -X POST https://api.tabibu.health/v1/ext/billing/payments/inv_4Kp2/fail \ -H "Authorization: Bearer sk_live_51Hk..." \ -d reason="cancelled"
Response
{ "id": "inv_4Kp2", "status": "pending_payment", "last_failure": "cancelled" }Control
/v1/ext/control/drain-completeext:control:writeCalled by the SDK when an extension has finished draining in-flight work after SIGTERM. Tells Tabibu it's safe to stop the container immediately, instead of waiting out the grace period.
Request
curl -X POST https://api.tabibu.health/v1/ext/control/drain-complete \ -H "Authorization: Bearer sk_live_51Hk..."
Response
{ "acknowledged": true }