Reference

SDKs & samples

Postman collection, curl snippets, and copy-pastable Python + Node code for the five workflows you will hit ninety percent of the time.

Postman collection

The full /v1 surface — twenty-two endpoints across seven modules — ships as a Postman collection in the repository. Import it and run the full workflow set end-to-end inside the Postman runner.

Official SDKs

Native client libraries are in the planning phase. Today every integration uses HTTP directly via curl, your language's built-in HTTP client, or a thin wrapper around it.

LanguageTodayPlanned
Pythonhttpx + the snippets belowQ3 2026 — sente-rails python
Node / TypeScriptfetch + the snippets belowQ3 2026 — @sente-rails/node
JavaOkHttp / java.net.http + snippetsQ4 2026
.NETHttpClient + snippetsQ4 2026
Gonet/http + snippetsDriven by integrator demand
PHPGuzzle + snippetsDriven by integrator demand

What an SDK will add

Typed request and response shapes, automatic retry with idempotency handling, webhook signature verification helpers, and pagination iterators. Until that lands, the snippets below cover the same ground with five lines of glue per call.

Catalogue — list agencies

Public endpoint, no auth. Lists every MDA on the rail with its current integration status and live endpoint count.

curl
curl https://sente-rails.space/v1/mdas?status=Sandbox

Citizen — resolve by NIN

Authenticated. Cascades local cache → NIRA via UGHub. Response carries the source badge so you can audit the verification path.

curl
curl https://sente-rails.space/v1/citizens/search-by-nin?nin=CM78001234ABCD \
  -H "X-Sente-Authorization: Bearer $SENTE_API_KEY"

Assessment — cross-MDA in one call

Authenticated. Always send an Idempotency-Key — see the API standards page for the contract.

curl
curl -X POST https://sente-rails.space/v1/assessments \
  -H "X-Sente-Authorization: Bearer $SENTE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "citizen": "CM78001234ABCD",
    "lines": [
      { "service": "NAME-RESERVE", "mda": "URSB" },
      { "service": "TIN-REG",      "mda": "URA"  },
      { "service": "TL-NEW",       "mda": "GULU" }
    ]
  }'

Payment — initiate via MoMo

Two-step: create the intent, then initiate the charge. The aggregator prompts the citizen on their handset; the rail receives a webhook on confirmation and propagates per-MDA receipts.

curl (both steps) · bash
# 1) create the intent
INTENT=$(curl -sS -X POST https://sente-rails.space/v1/payment-intents \
  -H "X-Sente-Authorization: Bearer $SENTE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "assessment": "ASSESS-2026-000123", "payment_channel": "MTN MoMo" }' \
  | jq -r '.data.name')

# 2) initiate the charge
curl -X POST "https://sente-rails.space/v1/payment-intents/$INTENT/initiate" \
  -H "X-Sente-Authorization: Bearer $SENTE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "payer_msisdn": "+256772123456" }'

# 3) trace state machine end-to-end (poll or wait for webhook)
curl "https://sente-rails.space/v1/payment-intents/$INTENT/trace" \
  -H "X-Sente-Authorization: Bearer $SENTE_API_KEY"

Webhook receiver — verify + acknowledge

Receive payment + cross-MDA propagation events on your side. Verify the HMAC-SHA256 signature against the raw body before parsing. See Webhooks for the full event catalogue.

python (flask)
import hmac, hashlib, os
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ["SENTE_WEBHOOK_SECRET"].encode()

@app.post("/webhooks/sente")
def receive():
    sig = request.headers.get("X-Sente-Signature", "")
    expected = hmac.new(SECRET, request.data, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig):
        abort(401)

    event = request.json
    # event["type"] e.g. "payment_intent.confirmed", "assessment.propagated"
    # event["data"]["object"] holds the resource snapshot
    print(event["type"], event["id"])
    return "", 200