Bills
Fetch and pay real-time bills (utilities, telecom, tuition, and more) with an OTP-verified fetch flow.
REST over HTTPS with JSON requests and responses. Intended for authorized distributors.
Two-step flow: Fetch Bill returns the amount to pay (some billers require a provider OTP first), then Pay Bill settles it using the exact billId and baseAmount returned by fetch.
Authentication & headers
Every request must include the HTTP headers below, plus Content-Type: application/json on requests that send a body.
| Header | Description |
|---|---|
sessionId | Value provided by Whish. |
token | Value provided by Whish. |
language | Always set to en. |
User-Agent | Identifies your application to Whish. Send your own app name, version, website, and support contact, not Whish's details. Format: AppName/version (website; contact-email). Example: AcmeStore/2.1 (https://acme.example; dev@acme.example). |
Environments
| Environment | Base URL | Purpose |
|---|---|---|
| Sandbox | https://api.sandbox.whish.money/itel-service/api | Testing, QA, integration verification. |
| Production | https://api.whish.money/itel-service/api | Live environment for real transactions. |
Response format
All endpoints return HTTP 200 with Content-Type: application/json and a shared envelope. Always branch on the status field in the body, not the HTTP status code.
| Field | Type | Description |
|---|---|---|
status | Boolean | true = success · false = failure. |
code | String | Operation-specific failure code; null on success. |
dialog | Object | Optional title / message to display to the user. |
data | Object | Response payload for the operation; shape varies per endpoint. |
actions / extra | Object | Reserved; typically null. |
Account balance
Returns the real and void balances of the account. The void balance represents voided items (sold but unused, held for later).
{
"status": true,
"code": null,
"dialog": null,
"extra": null,
"data": {
"balanceDetails": {
"balance": -217.718,
"voidBalance": 0.0
}
}
}Response fields
| Field | Type | Description |
|---|---|---|
balance | Double | The real balance of the account. |
voidBalance | Double | The void balance of the account. |
Fetch bill
Fetches a bill. For most denominations this is a two-step, OTP-verified flow; selected denominations skip OTP.
Request body
| Field | Type | Description |
|---|---|---|
denominationId | int | Identifier of the product/biller. |
target | String | Bill target (e.g. subscriber phone number). |
verificationId | int | OTP session ID from Step 1; include only in Step 2 with otp. |
otp | int | One-time password sent by the carrier; required in Step 2. |
Standard flow: OTP first
Treat code = bill.requires_provider_otp as an expected challenge, not an error. Persist verificationId = data and prompt the user for the OTP. Sandbox fixed values: verificationId = 6789, otp = 1234.
{
"denominationId": 53001,
"target": "971555555555"
}{
"status": false,
"code": "bill.requires_provider_otp",
"dialog": {
"title": "Verification required",
"message": "Please enter the OTP sent by the provider."
},
"actions": null,
"extra": null,
"data": 6789
}{
"denominationId": 53001,
"target": "971555555555",
"verificationId": 6789,
"otp": 1234
}{
"status": true,
"code": null,
"dialog": null,
"actions": null,
"extra": null,
"data": {
"billId": 1220,
"target": "0555555555",
"baseAmount": 19.0,
"additionalAmount": -0.0,
"totalAmount": 19.0,
"allowPayAll": true,
"minAmount": 19.0,
"maxAmount": 19.0,
"amountIncrement": 0.0
}
}Alternative flow: no OTP (selected denominations)
{
"denominationId": 54001,
"target": "971555555555"
}{
"status": true,
"code": null,
"dialog": null,
"extra": null,
"data": {
"billId": 1234,
"target": "971555555555",
"baseAmount": 30.40,
"additionalAmount": 1.60,
"totalAmount": 32.00,
"allowPayAll": true,
"minAmount": null,
"maxAmount": null,
"amountIncrement": null
}
}Pay bill
Pays the bill retrieved by Fetch Bill. Always pass the exact billId and baseAmount (as amount) returned by fetch.
Request body
| Field | Type | Description |
|---|---|---|
externalId | Long | Client-generated unique ID per payment attempt (idempotency). |
billId | Long | The billId returned by /bill/fetch. |
amount | Double | Must equal baseAmount from /bill/fetch. |
{
"externalId": 9876543210,
"billId": 1220,
"amount": 19.0
}{
"status": true,
"code": null,
"dialog": null,
"extra": null
}{
"status": false,
"code": "payment.failed",
"dialog": {
"title": "Error",
"message": "Payment failed"
},
"extra": null
}Error codes
| Code | Description |
|---|---|
bill.requires_provider_otp | OTP challenge (expected on fetch, not a failure). |
bill.fetch_failed | Bill fetch failed. |
payment.failed | Payment failed. |
Sandbox scenarios
| Denomination ID | Behavior |
|---|---|
53001 | Standard fetch & payment (requires provider OTP). |
54001 | Fetch & payment (no OTP). |
| Test target | Expected outcome |
|---|---|
971555555555 | Success path (fetch and pay). |
971555555554 | Fetch fails (bill.fetch_failed). |
971555555553 | Fetch succeeds; payment fails (payment.failed). |
Fixed sandbox OTP values: verificationId = 6789, otp = 1234.
Best practices
- Always include
sessionId,token,languageheaders. - Treat
bill.requires_provider_otpas a two-step flow, not an error; persistverificationId. - Use the exact
billIdandbaseAmountfrom fetch when paying. - Make
externalIdunique per payment attempt (client-side idempotency). - Normalize amounts to two decimals for display; preserve full precision internally.
- Handle transient failures with safe retries; on payment retry use a new
externalIdunless told otherwise.
Postman & tools
Import the ready-made Postman collection for this API, set your credentials as collection variables (or attach a sandbox / production environment), and call every endpoint without writing code.
⤓ Download collectionAll downloads & environments
In Postman: Import → select the file → choose the Sandbox or Production environment → fill in your credentials. Requests use {{baseUrl}} and the credential variables provided.