Create Card Payment
POST /api/v1/payments with type: card
POST /api/v1/payments with type: "card".
Idempotency-Key is required — requests without it return 400 IDEMPOTENCY_KEY_REQUIRED. Idempotency.
Returns 201 with status: "pending" and a payment_url for hosted card checkout, or 200 when replaying an existing Idempotency-Key.
Required fields
| Field | Notes |
|---|---|
amount, currency, type, customer, phone | type must be "card". Currencies: TZS, USD, KES, UGX |
customer | firstname, lastname, email, address, city, state, postcode, country |
redirect_url | Where the customer is sent after successful checkout |
cancel_url | Where the customer is sent if they cancel checkout |
Optional fields
| Field | Notes |
|---|---|
network | Mobile network operator |
reference, metadata | Duplicate live reference → 409 DUPLICATE_REFERENCE |
webhook_url, callback_url | Override merchant defaults |
Response
| Field | Value |
|---|---|
status | pending |
payment_url | Hosted checkout URL — redirect the customer’s browser here |
qr_code | Omitted |
payment_token | May be set by the provider |
payment_url. Trust the webhook over the redirect — the redirect is a UX cue; webhooks are the source of truth.
Unfinished payments expire roughly 30 minutes after creation (PayGrid TTL). The provider-hosted page may allow up to 60 minutes, but PayGrid marks the payment expired at ~30 min.
Errors
| HTTP | error_code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | Missing or invalid amount, currency, type, phone, or customer basics (firstname, lastname, email) |
| 400 | PAYMENT_FAILED | Missing redirect_url, cancel_url, or a billing address field (address, city, state, postcode, country) |
Authorizations
API key obtained from the merchant dashboard. When using the 'Try It' feature, enter only the key—the 'Bearer ' prefix is added automatically. For direct API calls, the 'Authorization: Bearer ' format is mandatory.
Headers
Required. A unique value (max 255 characters) per payment attempt, scoped to your merchant account. Requests without it are rejected with 400 IDEMPOTENCY_KEY_REQUIRED. Reuse the same value when retrying — the retry returns 200 with the original payment instead of creating a duplicate. Safe under concurrent retries: the losing request replays the winner's payment.
255"123e4567-e89b-12d3-a456-426614174000"
Body
- Option 1
- Option 2
- Option 3
Amount the customer is charged. Minimum 500 TZS when currency is TZS.
TZS, USD, KES, UGX Mobile money USSD push payment.
"mobile"Customer phone number. For TZS, accepted formats: 0712345678, 712345678, 255712345678, +255712345678 — normalized to 255XXXXXXXXX and must be a valid Tanzanian mobile number (2556/2557 prefix). Invalid numbers return 400 VALIDATION_ERROR.
Optional — the mobile network operator is auto-detected from the phone number. Aliases accepted: mpesa (vodacom), mixx (tigo). Invalid values return 400 VALIDATION_ERROR.
vodacom, tigo, airtel, halotel, ttcl Your internal reference. A reference with an existing live (pending/processing/completed) payment is rejected with 409 DUPLICATE_REFERENCE.
Per-payment webhook URL. Overrides merchant webhook_url when set.
Per-payment callback URL for terminal status. Overrides merchant callback_url when set.
Response
Idempotent replay — a payment already exists for this Idempotency-Key. The existing payment is returned and no new charge is created.
Standard success envelope wrapping all API responses.
success HTTP status code echoed in the body
Payment record returned by GET /payments/{id}, GET /payments, POST /payments/{id}/refresh, and idempotent replays. reference is YOUR merchant reference; the upstream payment identifier is external_id.
Pagination metadata on list endpoints

