Get started

Quick start

From the public sandbox to a paid cross-MDA assessment in under ten minutes. Every example below runs against the live rail.

Sandbox vs. production

Every endpoint cited on this page is reachable today at https://sente-rails.space/v1. The MoMo adapter runs against the MTN Mobile Money sandbox; no real money moves. URA-EFRIS will move from Sandbox to Live the day sandbox credentials land — code paths stay identical.

Before you start

You need three things:

  • A terminal with curl (every example uses it) and optionally jq for pretty-printing JSON responses.
  • A sandbox API key. The public catalogue endpoints work without one; authenticated calls require a Bearer token. Get one in sixty seconds via the self-serve signup at /signup — no card required, ten thousand calls per month, free forever.
  • About ten minutes. The five steps below take roughly two minutes each.

1. List the connected agencies

The catalogue is public — no authentication required. This endpoint returns every MDA on the rail with its current integration status, sector classification, and live endpoint count.

Request · bash
curl https://sente-rails.space/v1/mdas
Response (truncated) · json
{
  "data": [
    {
      "short_code": "GULU",
      "full_name": "Gulu City Authority",
      "mda_type": "City Authority",
      "country": "UG",
      "mode": "A",
      "sector": "Local Government",
      "integration_status": "Sandbox",
      "endpoint_count": 6,
      "target_endpoint_count": 14,
      "display_endpoint_count": 6
    },
    {
      "short_code": "URA",
      "full_name": "Uganda Revenue Authority",
      "sector": "Revenue",
      "integration_status": "Sandbox",
      "endpoint_count": 2,
      "display_endpoint_count": 2
    }
  ]
}

Filter the catalogue by mode (A, B, or C), sector, or integration status using query parameters documented under API standards.

2. Authenticate

Every endpoint outside the public catalogue requires a Bearer token in the X-Sente-Authorization header. Tokens are scoped per integrator and audit-logged on every request.

Two accepted Bearer headers

Both Authorization: Bearer <key> and X-Sente-Authorization: Bearer <key> are accepted on every authenticated endpoint. Use the standard Authorization header for compatibility with the bulk of client libraries; the custom header is retained for cases where another middleware on the network path may strip or rewrite Authorization.

curl
curl https://sente-rails.space/v1/citizens \
  -H "X-Sente-Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxx"

3. Resolve a citizen by NIN

Citizen lookup is the first leg of almost every counter workflow. The rail cascades: local cache first, then NIRA via UGHub if the citizen isn't in the local catalogue yet. The response carries the source badge so you can audit where the verification came from.

Request · bash
curl https://sente-rails.space/v1/citizens/search-by-nin?nin=CM78001234ABCD \
  -H "X-Sente-Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxx"
Response · json
{
  "data": {
    "nin": "CM78001234ABCD",
    "full_name": "John Patrick Mukasa",
    "district": "Gulu",
    "verified": true,
    "source": "local",
    "consent": {
      "data_sharing": true,
      "recorded_on": "2026-05-20T22:57:40Z"
    }
  }
}

Fifteen citizens are pre-seeded in the sandbox. See the cookbook for the full test catalogue and how to drive each workflow.

4. Build a cross-MDA assessment

An assessment is a basket of one or more lines, each pointing at a service on a specific MDA. Cross-MDA in one assessment is a first-class behaviour — it compresses a traditional thirty-minute, three-office business-registration trail into a single counter interaction.

Request · bash
curl -X POST https://sente-rails.space/v1/assessments \
  -H "X-Sente-Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "citizen": "CM78001234ABCD",
    "lines": [
      { "service": "NAME-RESERVE", "mda": "URSB" },
      { "service": "COMPANY-REG",  "mda": "URSB" },
      { "service": "TIN-REG",      "mda": "URA"  },
      { "service": "TL-NEW",       "mda": "GULU" }
    ]
  }'
Response · json
{
  "data": {
    "name": "ASSESS-2026-000123",
    "status": "Assessed",
    "cross_mda": true,
    "total_amount": 350000,
    "currency": "UGX",
    "lines": [
      { "service": "NAME-RESERVE", "mda": "URSB", "amount":  50000, "efris_fdn": "FDN-..." },
      { "service": "COMPANY-REG",  "mda": "URSB", "amount": 250000, "efris_fdn": "FDN-..." },
      { "service": "TIN-REG",      "mda": "URA",  "amount":      0, "efris_fdn": null      },
      { "service": "TL-NEW",       "mda": "GULU", "amount":  50000, "efris_fdn": "FDN-..." }
    ]
  }
}

Always send an Idempotency-Key

Network blips happen at counters. An idempotency key (any UUID) makes retrying safe — a second call with the same key returns the original assessment, not a duplicate. See API standards · Idempotency for the contract.

5. Initiate payment via MoMo sandbox

A payment intent wraps the assessment, fans out to per-MDA splits at the aggregator level, and emits webhooks as the payment progresses. Sente Rails never holds the money — settlement lands directly in the per-MDA aggregator accounts.

  1. Create the intent against the assessment.
  2. Initiate the charge via the selected channel (MoMo here).
  3. Citizen receives a USSD prompt; their PIN confirms the charge.
  4. The rail receives a webhook and propagates per-MDA receipts.
Step 5a · Create the intent · bash
curl -X POST https://sente-rails.space/v1/payment-intents \
  -H "X-Sente-Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "assessment": "ASSESS-2026-000123",
    "payment_channel": "MTN MoMo"
  }'
Step 5b · Initiate the charge · bash
curl -X POST https://sente-rails.space/v1/payment-intents/PI-2026-000045/initiate \
  -H "X-Sente-Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "payer_msisdn": "+256772123456"
  }'

On webhook receipt the intent moves to Confirmed, the per-MDA splits credit their aggregator accounts, and per-MDA receipts propagate to URSB, URA, and Gulu City. Inspect the full state machine:

Step 5c · Trace · bash
curl https://sente-rails.space/v1/payment-intents/PI-2026-000045/trace \
  -H "X-Sente-Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxx"

What to read next

You've seen the spine of the rail. Three threads pull on it:

  • Webhooks — receive payment, registration, and verification events on your side.
  • Security & compliance — auth modes, signing, data classifications, and the seven regulatory frameworks mapped to architecture.
  • Cookbook — four full recipes from everyday collections to cross-agency registration, with citizens you can use, expected payloads, and timing.