Appearance
Top-ups
Create a Taura top-up for a phone number and read the result from the returned status.
Top-up statuses
| Status | What your integration should do |
|---|---|
pending | Wait and check the top-up again before updating the customer order. Repeating the same ref returns this top-up and does not send it again. |
completed | Mark the customer order as successful. |
failed | Read failure_reason. Use a new ref only if the customer wants another attempt. |
under_review | Do not submit a duplicate. Keep the uuid and ref, and contact Xash support if needed. |
Top-up fields
| Field | Type | Notes |
|---|---|---|
uuid | string | Taura Xashpay top-up identifier. |
ref | string | Your reference for the customer request. |
recipient | string | Recipient phone number. |
units | number | Top-up amount in UNITS. |
status | string | pending, completed, failed, or under_review. |
wallet_transaction_uuid | string|null | Related wallet transaction identifier. |
vendor_reference | string|null | Completion reference when available. |
failure_reason | string|null | Reason supplied for an unsuccessful or reviewed top-up. |
created_at | string|null | ISO 8601 timestamp. |
POST/api/v1/taura/topups
Create a top-up.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
recipient | string | Yes | Phone number in E.164 format, such as +263775696233. Maximum 16 characters. |
units | number | Yes | Positive amount in UNITS. |
ref | string | Yes | Your unique reference. Maximum 120 characters. |
Use the same ref for the same request
If you retry the same customer request, send the same ref. The API returns the existing top-up instead of creating another one. Use a new ref only for a new customer request.
Example request
bash
curl --request POST "https://taura.xashpay.com/api/v1/taura/topups" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer TOKEN" \
--data '{
"recipient": "+263775696233",
"units": 25.00,
"ref": "ORDER-10045"
}'Response: 201
json
{
"topup": {
"uuid": "2236170a-85fb-4354-ab0b-941871ecfab0",
"ref": "ORDER-10045",
"recipient": "+263775696233",
"units": 25.0,
"status": "completed",
"wallet_transaction_uuid": "9cbce09d-0d13-4cad-a7aa-537f51f5ef23",
"vendor_reference": "TXN-10045",
"failure_reason": null,
"created_at": "2026-08-31T08:20:00+00:00"
}
}Always branch on status. Do not treat HTTP 201 alone as confirmation that the top-up completed.
Request errors
- HTTP
422means a request field is invalid or the wallet balance cannot cover the amount. - HTTP
429means the rate limit was reached. Wait forRetry-After, then retry with the sameref.
See Errors for the response format.
GET/api/v1/taura/topups
List your top-ups, newest first.
Query parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
per_page | integer | No | Default 20. |
page | integer | No | Page number, starting at 1. |
Example request
bash
curl "https://taura.xashpay.com/api/v1/taura/topups?per_page=20" \
--header "Accept: application/json" \
--header "Authorization: Bearer TOKEN"The response contains a topups array. Each item uses the fields listed in Top-up fields. Request the next page with ?page=2. An empty array means that page has no records.
GET/api/v1/taura/topups/lookup
Look up one of your top-ups by the ref you sent when you created it. This endpoint only reads. It never sends a top-up, retries one, or changes your wallet.
Query parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
ref | string | Yes | The ref of the top-up. Maximum 120 characters. |
include_balance | string | No | true or 1 adds the recipient's account check. false, 0 or leaving it out keeps the response below. |
include_retry_authorization | string | No | true or 1 adds the top-up's latest retry authorization. |
Example request
bash
curl --get "https://taura.xashpay.com/api/v1/taura/topups/lookup" \
--data-urlencode "ref=ORDER-10045" \
--header "Accept: application/json" \
--header "Authorization: Bearer TOKEN"Response: 200
json
{
"topup": {
"uuid": "2236170a-85fb-4354-ab0b-941871ecfab0",
"ref": "ORDER-10045",
"recipient": "+263775696233",
"units": "25.00000000",
"status": "completed",
"vendor_reference": "TXN-10045",
"failure_reason": null,
"created_at": "2026-08-31T08:20:00+00:00",
"completed_at": null
}
}The fields match Top-up fields, with these differences:
unitsis a string with exactly 8 decimal places, the precision stored for the top-up. For example,"0.19230760"or"25.00000000". It is not rounded to 2 decimal places as it is in the other top-up responses, so you can compare it with the amount you sent.wallet_transaction_uuidis not included.completed_atis added. It is alwaysnullfor now because completion times are not recorded yet.
If you have no top-up with that ref, the response is still HTTP 200:
json
{
"topup": null
}null means no record is stored for that ref. It does not prove that no recharge was sent, and neither does status: failed. Contact Xash support before sending the same request again under a new ref.
Recipient account check
Send include_balance=true to get the recipient's Taura account and balance next to the top-up. The recipient always comes from the stored top-up, never from the request. Nothing is sent, retried, settled or refunded.
bash
curl --get "https://taura.xashpay.com/api/v1/taura/topups/lookup" \
--data-urlencode "ref=ORDER-10045" \
--data-urlencode "include_balance=true" \
--header "Accept: application/json" \
--header "Authorization: Bearer TOKEN"json
{
"topup": {
"uuid": "2236170a-85fb-4354-ab0b-941871ecfab0",
"ref": "ORDER-10045",
"recipient": "+263775696233",
"units": "25.00000000",
"status": "under_review",
"vendor_reference": null,
"failure_reason": "Deadline Exceeded",
"created_at": "2026-08-31T08:20:00+00:00",
"completed_at": null
},
"account_check": {
"outcome": "exists",
"recipient": "+263775696233",
"monetary_balance": "1.65",
"monetary_balance_unit": "$",
"currency": null,
"voice_balance": "0.00",
"voice_balance_unit": "minutes",
"checked_at": "2026-08-31T09:02:11+00:00",
"freshness": "fresh",
"failure_code": null
}
}| Field | Type | Notes |
|---|---|---|
outcome | string | exists: Taura recognised the account. not_found: Taura reported no such account. unavailable: no answer could be read. |
recipient | string | The top-up's recipient. |
monetary_balance | string|null | Balance as Taura prints it, for example "1.65". null unless outcome is exists. |
monetary_balance_unit | string|null | The symbol Taura prints before the balance, currently "$". |
currency | string|null | ISO currency code once Taura confirms one. Always null for now: the $ symbol is not proof of a currency. |
voice_balance | string|null | Voice balance, for example "0.00". null unless outcome is exists. |
voice_balance_unit | string|null | "minutes" when voice_balance is set. |
checked_at | string|null | ISO 8601 time Taura was asked. null when no lookup was made. |
freshness | string | fresh: looked up for this request. cached: a check of this top-up from the last 60 seconds is returned instead. none: no lookup was made (see failure_code). |
failure_code | string|null | Why there is no balance: timeout, rate_limited, unauthorized, server_error, http_error, request_failed, malformed_response, missing_balance, unrecognised_response, account_not_found, lookup_rate_limited (your balance-check limit) or lookup_busy (Taura's shared limit). |
If the account check fails or times out, the response is still HTTP 200 with the full topup, and account_check has outcome: "unavailable" and null balances:
json
{
"topup": { "ref": "ORDER-10045", "status": "under_review", "...": "..." },
"account_check": {
"outcome": "unavailable",
"recipient": "+263775696233",
"monetary_balance": null,
"monetary_balance_unit": null,
"currency": null,
"voice_balance": null,
"voice_balance_unit": null,
"checked_at": "2026-08-31T09:02:19+00:00",
"freshness": "fresh",
"failure_code": "timeout"
}
}A null balance never means zero. A real zero balance is "0.00" with outcome: "exists".
A balance is not proof of delivery
A zero balance can follow spending, and a positive balance can come from another recharge. Do not decide that an earlier top-up did or did not deliver from the balance alone.
Balance checks have their own limit of 30 per minute per vendor, inside the lookup limit. Over it, the top-up is still returned and account_check has failure_code: "lookup_rate_limited". Each check is recorded against the top-up.
Retry authorization
An under-review top-up has an unknown delivery outcome, so it is never sent again automatically. A Taura Xashpay admin can allow one more send, either after Taura confirms the earlier attempt was not delivered, or by accepting that the recipient may receive the units twice. Send include_retry_authorization=true to see that permission:
json
{
"topup": { "ref": "ORDER-10045", "status": "under_review", "...": "..." },
"retry_authorization": {
"id": "8f0f3f8e-5a2b-4d5b-9a55-0f6c1c2d7e10",
"decision": "duplicate_risk_accepted",
"status": "active",
"ref": "ORDER-10045",
"recipient": "+263775696233",
"units": "25.00000000",
"uncertain_attempt": 1,
"granted_after_attempt": 1,
"current_send_attempt": 1,
"granted_at": "2026-08-31T10:15:00+00:00",
"consumed_at": null,
"consumed_send_attempt": null,
"voided_at": null,
"send_queued": false
}
}retry_authorization is null when none was ever granted.
| Field | Type | Notes |
|---|---|---|
id | string | Name this in retry_authorization when you retry. |
decision | string | confirmed_not_delivered: Taura confirmed the uncertain attempt was not delivered. duplicate_risk_accepted: the earlier attempt may have delivered and a second delivery was accepted. These are different decisions; do not treat one as the other. |
status | string | active: one retry naming id may send. consumed: a retry already used it. void: the top-up changed (an outcome, another attempt or a decision) and it no longer applies. |
ref, recipient, units | string | Exactly what it covers. Only act on it if all three match your record. |
uncertain_attempt | integer|null | The send attempt whose outcome was unknown when it was granted. |
granted_after_attempt | integer | The latest send attempt when it was granted. The retry it allows is the next one. |
current_send_attempt | integer | The top-up's latest send attempt now. |
consumed_at, consumed_send_attempt | string|null, integer|null | When and as which attempt it was used. |
send_queued | boolean | Always false. Taura Xashpay only grants permission; it never queues or sends the retry for you. |
To use an active authorization, call the retry-only endpoint POST /api/v1/taura/topups/retry with the original details and name it. The retry still checks the recipient's account first and sends nothing if the check fails; the authorization then stays active for your next retry. It is used by the first retry that sends, and a second retry naming it is refused with retry_blocked: "retry_authorization_invalid":
json
{
"recipient": "+263775696233",
"units": 25,
"ref": "ORDER-10045",
"retry_authorization": "8f0f3f8e-5a2b-4d5b-9a55-0f6c1c2d7e10"
}If that retry's outcome is unknown again, the top-up is blocked again and needs a new decision.
Request errors
- HTTP
422meansrefis missing, blank, or longer than 120 characters, or aninclude_option is nottrue,false,1or0. - HTTP
429means the lookup rate limit was reached. Wait forRetry-After, then try again.