Guides/Moving from sandbox to production
Updated 2026-08-01
Moving from sandbox to production
With the built-in gateway, going live changes the base URL and the keys. Nothing else.
This covers the default setup, where gateway.adapter in portal.yaml is portal: the portal is its own
production gateway, no extra software required. If your deployment runs Kong instead, the base URL differs but the
keys, tokens and paths work the same way.
What changes
| Sandbox | Production | |
|---|---|---|
| Base URL | /api/sandbox |
/api/live |
| Token endpoint | /api/sandbox/oauth/v1/token |
/api/live/oauth/v1/token |
| Keys | Sandbox key pair | Production key pair |
| Token life | 1 hour | 1 hour |
| Paths | Same | Same |
| Answers | Scripted scenarios | Your real backend |
Every operation keeps the same path and the same request and response shape. A client built against the sandbox needs its base URL and its keys swapped, and nothing else.
Before you start
Go live is a checklist in Console under your app: pass the sandbox test cases, verify your business, accept the
production agreement, wait for an operator's approval, then issue your production keys. The exact steps are
whatever your deployment configured in portal.yaml; see Go live for this one's.
Get a production token
Same shape as the sandbox token request, against the production base URL, with your production key pair:
curl -X POST 'http://localhost:3000/api/live/oauth/v1/token' \
-u "$LANGO_LIVE_KEY:$LANGO_LIVE_SECRET" \
-d 'grant_type=client_credentials'
Call it
curl -X POST 'http://localhost:3000/api/live/collect/v1/push' \
-H "Authorization: Bearer $LANGO_LIVE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "shortCode": "600100", "amount": 1500, "currency": "KES", "phoneNumber": "254712345678", "reference": "INV-2041", "callbackUrl": "https://your-server.example/hooks/lango" }'
This time the request reaches your real backend, listed under gateway.upstreams for this API. Real money moves.
There is no magic-value scenario engine in production: whatever your backend answers is what the caller gets.
What is checked on every call
In order, before your backend ever sees the request:
- The token's signature and expiry.
- The key pair it names is still active. Revoking a key pair, or suspending the app, takes effect on the very next call.
- The app holds an approved subscription to a group that contains this API.
- The plan's per-minute rate and monthly quota. Sandbox and production are counted separately, so testing never uses up production's allowance.
Errors
The shape is the same as the sandbox: { "error": "CODE", "message": "...", "requestId": "..." }.
| Status | Code | Meaning |
|---|---|---|
| 401 | MISSING_TOKEN |
No Authorization: Bearer header. |
| 401 | INVALID_TOKEN |
Unknown, expired, or its key pair was revoked. |
| 403 | NOT_SUBSCRIBED |
No approved subscription to a group with this API. |
| 403 | SUBSCRIPTION_PENDING |
The group needs review and it has not happened yet. |
| 403 | APP_SUSPENDED |
An operator suspended the app. |
| 404 | NO_SUCH_OPERATION |
The method and path match nothing in the catalog. |
| 413 | BODY_TOO_LARGE |
Over the configured request body limit. |
| 429 | RATE_LIMITED |
Over the plan's per-minute rate. |
| 429 | QUOTA_EXCEEDED |
Over the plan's monthly quota for this group. |
| 502 | UPSTREAM_UNAVAILABLE |
Your backend could not be reached. Nothing was processed. |
| 504 | UPSTREAM_TIMEOUT |
Your backend did not answer in time. It may still have processed the request; check before retrying anything that moves money. |
requestId is also returned to you and stored with the call, so support can find the exact row without you sending
a request or response body.
What your backend receives
x-gateway-secret (refuse anything without it), x-lango-app-id, x-lango-organization-id,
x-lango-consumer-key, x-lango-request-id, and x-lango-callback-url for operations that answer by callback.
Your callback signing works the same way in production as in the sandbox: see
Verifying callback signatures.
Next
- Go live for the checklist itself.
- The sandbox page documents every scenario you should have exercised before this point.