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.
| Language | Today | Planned |
|---|---|---|
| Python | httpx + the snippets below | Q3 2026 — sente-rails python |
| Node / TypeScript | fetch + the snippets below | Q3 2026 — @sente-rails/node |
| Java | OkHttp / java.net.http + snippets | Q4 2026 |
| .NET | HttpClient + snippets | Q4 2026 |
| Go | net/http + snippets | Driven by integrator demand |
| PHP | Guzzle + snippets | Driven 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 https://sente-rails.space/v1/mdas?status=SandboxCitizen — resolve by NIN
Authenticated. Cascades local cache → NIRA via UGHub. Response carries the source badge so you can audit the verification path.
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 -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.
# 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.
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