Whish Money
Whish Money
Partner Integration Guide

Whish Pay Split

Collect a payment and record how it should be split across destination accounts, by amount or by percentage.

REST over HTTPS with JSON requests and responses. Intended for authorized partners.

v1.0.0Platform: WhishJSON over HTTPSAuthor: TecFrac
Authentication
channel + secret + websiteUrl
Production base URL
https://api.whish.money/itel-service/api
Currencies
USD · LBP

The contract on this page is final; fund redistribution is not live yet. The endpoint, request fields, and response shape below are settled and safe to integrate against now. Actual redistribution of funds to each breakdown identifier, including fee handling, is still being finalized on the Whish side. Until it ships, breakdown and breakdownType are captured and held against the payment, and the full amount is collected from the payer as normal. Whish will confirm when redistribution goes live; no change to your integration is expected at that point.

Integration flow

  1. 1
    Create the split payment. Call Create a split payment with the amount, currency, your externalId, the breakdown, and your callback URLs. You receive a collectUrl.
  2. 2
    Redirect the payer. Send the customer to the collectUrl. On that hosted page the payer authorizes the payment from their Whish balance and confirms with a one-time code (OTP) that Whish delivers to them as an in-app notification in the Whish app. This step happens entirely on the hosted page; your integration never sends or verifies the OTP.
  3. 3
    Receive the callback. When the payment settles, Whish calls your successCallbackUrl or failureCallbackUrl by GET.
  4. 4
    Confirm before fulfilling. Call Get payment status to verify the outcome, then release the order. Poll it as a fallback if no callback arrived.

A success callback settles the payment; a failure callback does not. successCallbackUrl is your confirmation that the order is paid. failureCallbackUrl only tells you an attempt failed, and the customer can still pay the same link, so confirm with Get payment status before acting on it. See Callbacks.

Authentication & headers

Every request must include the HTTP headers below, plus Content-Type: application/json on requests that send a body.

HeaderDescription
channelValue provided by Whish.
secretValue provided by Whish.
websiteUrlSite URL or app name. Use the exact value Whish issued with your keys.
User-AgentIdentifies your app. Use your own details, not Whish's. Format: AppName/version (website; contact-email). Example: AcmeStore/2.1 (https://acme.example; dev@acme.example).

Keep your credentials secret. They authorize account actions and money movement, so send them only over HTTPS, store them in a secure secrets manager, and never expose them in client-side code, logs, or version control. Rotate them immediately if you suspect a leak.

Environments

EnvironmentBase URLPurpose
Sandboxhttps://partner.api.sbx.whish.money/itel-service/apiTesting, QA, integration verification.
Productionhttps://api.whish.money/itel-service/apiLive environment for real transactions.

The cURL examples on this page target Sandbox. To go live, swap the host for the Production base URL above.

Response format

All endpoints return HTTP 200 with Content-Type: application/json and a shared envelope. Always branch on the status and code fields in the body, never on the HTTP status code.

FieldTypeDescription
statusBooleantrue when the call was accepted and processed. false when it was not; read code for the reason.
codeStringnull whenever status is true. When status is false, an operation-specific error code; the value 500 is special (see below). This field only ever reports errors, never a payment's progress.
dialogObjectOptional title / message to display to the user.
dataObjectResponse payload for the operation; shape varies per endpoint.
actions / extraObjectReserved; typically null.
retrievedBooleanPresent on idempotent operations. true means this response replays an already-processed request (the original was not run again); false or absent means it ran now.

The two fields move together, so read them as a pair. There are only these combinations:

statuscodeTreat as
truenullSucceeded. Process data.
false500Pending. The call did not return an outcome, so the result is unknown rather than failed. Reconcile with Get payment status; do not mark it failed.
falseany other codeFailed; read code (and any dialog message) to see why.

A status: true is never accompanied by a code, and a code never reports how far a payment has progressed. The payment's own lifecycle is reported on data.collectStatus by Get payment status.

Get account balance

Returns the real balance of the account. Pass currency as a query-string parameter on the request URL to choose which balance to return.

GET/payment/account/balance?currency={currency}
ParameterTypeRequiredDescription
currencyStringRequiredCurrency to return the balance for: USD or LBP. Sent in the query string, for example ?currency=USD.

Each channel supports two currencies (USD and LBP). Use currency to pick which balance to return.

Example request · cURL
cURL · Sandbox
curl 'https://partner.api.sbx.whish.money/itel-service/api/payment/account/balance?currency=USD' \
  -H 'channel: YOUR_CHANNEL' \
  -H 'secret: YOUR_SECRET' \
  -H 'websiteUrl: https://yoursite.com' \
  -H 'User-Agent: AcmeStore/2.1 (https://acme.example; dev@acme.example)'
Response · 200 success
application/json (USD)
{
  "status": true,
  "code": null,
  "dialog": null,
  "actions": null,
  "extra": null,
  "data": {
    "balance": 10439.0
  }
}

Response fields

FieldTypeDescription
balanceDoubleAccount balance in the requested currency.

Create a split payment

Creates a payment and records how the collected amount should be split across destination accounts. Returns the hosted URL where the payer completes the payment from their Whish balance.

POST/payment/whish/split
Example request · cURL
cURL · Sandbox
curl -X POST 'https://partner.api.sbx.whish.money/itel-service/api/payment/whish/split' \
  -H 'channel: YOUR_CHANNEL' \
  -H 'secret: YOUR_SECRET' \
  -H 'websiteUrl: https://yoursite.com' \
  -H 'User-Agent: AcmeStore/2.1 (https://acme.example; dev@acme.example)' \
  -H 'Content-Type: application/json' \
  -d '{
  "amount": 100,
  "currency": "USD",
  "externalId": "your-unique-id-001",
  "invoice": "Order #1",
  "successCallbackUrl": "https://merchant.example/api/payment/success",
  "failureCallbackUrl": "https://merchant.example/api/payment/failure",
  "successRedirectUrl": "https://merchant.example/thank-you",
  "failureRedirectUrl": "https://merchant.example/payment-error",
  "breakdownType": "amount",
  "breakdown": {
    "destination-1": 60,
    "destination-2": 40
  }
}'

Request body

FieldTypeRequiredDescription
amountNumberRequiredAmount to collect from the payer. Send it as a JSON number; a double-quoted string such as "100" is also accepted. When breakdownType is amount, this should equal the sum of the breakdown values.
currencyStringRequiredCurrency: USD or LBP.
externalIdString or NumberRequiredYour unique idempotency key for this payment, and the transaction reference. Re-sending a value already used replays the original result instead of creating a second payment.
breakdownObjectRequiredMap of identifier: value describing how the payment should be split. See The breakdown object.
breakdownTypeStringOptionalHow to read the breakdown values: amount (default) or percentage. See The breakdown object.
invoiceStringOptionalFree-text reference shown on the payment page.
successCallbackUrlStringRequiredGET callback URL Whish calls when a payment succeeds. This is your primary confirmation signal, so it is required.
failureCallbackUrlStringRequiredGET callback URL Whish calls when a payment attempt fails. The link stays payable, so this is not a settled outcome.
successRedirectUrlStringRequiredWhere the payer's browser is sent after a successful payment.
failureRedirectUrlStringRequiredWhere the payer's browser is sent after a failed payment.
Request
Request body
{
  "amount": 100,
  "currency": "USD",
  "externalId": "your-unique-id-001",
  "invoice": "Order #1",
  "successCallbackUrl": "https://merchant.example/api/payment/success",
  "failureCallbackUrl": "https://merchant.example/api/payment/failure",
  "successRedirectUrl": "https://merchant.example/thank-you",
  "failureRedirectUrl": "https://merchant.example/payment-error",
  "breakdownType": "amount",
  "breakdown": {
    "destination-1": 60,
    "destination-2": 40
  }
}
Response · 200 success
application/json
{
  "status": true,
  "code": null,
  "data": {
    "collectUrl": "https://pay.whish.money/invoice/pay/?q=abc123"
  }
}

Response fields

FieldTypeDescription
data.collectUrlStringHosted Whish payment page URL. Send the payer here to complete the payment from their Whish balance.

Callback and redirect URLs must be publicly reachable. Sending localhost (or any loopback or non-public host) in any of the four callback and redirect URLs above is not allowed: the request is rejected with HTTP 403 Forbidden. Use a public URL.

status: true here means the request succeeded and a collectUrl was generated. It does not mean the customer has paid. The payment outcome is separate: it is delivered to your callback URLs and can be checked with Get payment status.

In sandbox the payer receives no OTP in the Whish app, so the hosted page is driven with fixed test values instead. See Sandbox scenarios.

Get payment status

Returns the status of a payment. Call it with the externalId you sent when creating the payment. Use it to reconcile when a callback did not arrive, and to tell a settled outcome from an open one: a failure callback leaves the status pending while the link is still payable, so this endpoint is what confirms a payment finally failed.

POST/payment/collect/status
Example request · cURL
cURL · Sandbox
curl -X POST 'https://partner.api.sbx.whish.money/itel-service/api/payment/collect/status' \
  -H 'channel: YOUR_CHANNEL' \
  -H 'secret: YOUR_SECRET' \
  -H 'websiteUrl: https://yoursite.com' \
  -H 'User-Agent: AcmeStore/2.1 (https://acme.example; dev@acme.example)' \
  -H 'Content-Type: application/json' \
  -d '{ "currency": "USD", "externalId": "your-unique-id-001" }'

Request body

FieldTypeRequiredDescription
currencyStringRequiredCurrency: USD or LBP.
externalIdString or NumberRequiredThe externalId you sent when creating the split payment.
Request
Request body
{
  "currency": "USD",
  "externalId": "your-unique-id-001"
}
Response · 200 success
application/json
{
  "status": true,
  "code": null,
  "dialog": null,
  "extra": null,
  "data": {
    "collectStatus": "success",
    "payerPhoneNumber": "96170123456"
  }
}

Response fields

FieldTypeDescription
collectStatusStringPayment status. pending: the link is still payable, including after a failed attempt, so this is not a failure. success: paid, and the link is now expired. failed: the link expired without being paid. refunded: a successful payment was refunded. unknown: the final state could not be determined. Only success and failed are settled outcomes.
payerPhoneNumberStringPhone number that performed the payment.

The status reports the collect from the payer, which is the whole amount. It does not report per-destination distribution: redistribution to each breakdown identifier is not live yet. See the note at the top of this page.

The breakdown object

breakdown is an object of identifier: value pairs. The identifier is any string you choose to label a destination: a name, a phone number, or an account id. Several identifiers may point at the same underlying destination. How the value is read depends on breakdownType.

breakdownTypeHow breakdown values are readExpected total
amount (default)Each value is a literal amount in the collect's currency.The values should sum to amount.
percentageEach value is a percentage (0-100) of the payment.The values should sum to 100.

Amount mode (default)

Use this when you already know the exact figure each destination should receive. Omitting breakdownType selects it.

Request fragment
"breakdownType": "amount",
"breakdown": {
  "destination-1": 60,
  "destination-2": 40
}

With breakdownType: "amount", amount should equal the sum of the breakdown values. In the example above, amount is 100.

Percentage mode

Use this when you do not know each destination's exact figure up front, for example because a fee is deducted on the merchant's side and you would rather split whatever actually lands than split what you predicted. Set breakdownType to percentage and send the values as percentages instead of amounts.

Request fragment
"breakdownType": "percentage",
"breakdown": {
  "destination-1": 60,
  "destination-2": 40
}

Percentages should sum to 100 across the breakdown. The payer is still charged amount; the percentages describe how the collected payment is divided, not what is collected.

Callbacks

Callbacks tell you the outcome of a payment attempt. Provide successCallbackUrl and failureCallbackUrl when you create the split payment; Whish calls them when an attempt on the payment link succeeds or fails.

A failure callback is not the end of the payment. The payment link stays payable until it is either paid successfully or expires, so a customer whose attempt failed can simply try again on the same link. That is why Get payment status keeps reporting pending after a failure callback: the payment can still succeed. A successful payment expires the link immediately and moves the status to success; only a link that expires without a successful payment settles as failed. So treat a failure callback as this attempt failed, never as this order failed, and do not cancel the order on it.

  • Each callback is an HTTP GET with no request body.
  • You get one callback per attempt, so a link paid on the second try produces a failure callback followed by a success callback.
  • Whish adds no identifying parameters of its own, so include your own reference in the callback URL (for example ?externalId=your-unique-id-001&order=A-1234); Whish preserves and forwards them unchanged.
  • Because the callback is an unauthenticated GET, treat it as a signal to verify, not as proof. Confirm the outcome with Get payment status before releasing the order.
  • Respond with HTTP 200 to acknowledge receipt.
  • A missing callback (for example a transient network failure) should be reconciled by polling Get payment status, not treated as a failure.
  • The callback carries no breakdown information: it reports the outcome of the collect from the payer.
Success callback URL
GET
https://merchant.example/api/payment/success?externalId=your-unique-id-001&order=A-1234

Whish calls your callback URLs from a fixed set of source IPs. If your callback endpoint restricts inbound traffic, allowlist them; see IP whitelisting for the current list.

IP whitelisting

Whish calls your successCallbackUrl and failureCallbackUrl from a fixed set of source IPs. If your callback endpoint restricts inbound traffic, allowlist them so callbacks are not blocked. The current list is maintained in one place: Callback source IPs in the Reliability & Idempotency guide.

Sandbox scenarios

Sandbox does not deliver the OTP that the payer would normally receive in the Whish app. Drive the hosted payment page with the fixed values below instead.

Field on the hosted pageTest value
Phone96170123456
OTP111111
ScenarioHow to triggerExpected outcome
Successful paymentPay with the test phone and OTP above.collectStatus becomes success.
Failed attemptEnter any OTP other than 111111.The failure callback fires, and collectStatus stays pending because the link is still payable. Retry on the same link to see it then succeed.
ExpiryLeave the link unpaid until it expires.collectStatus settles as failed.
Amount breakdownSend breakdownType: "amount" with values summing to amount.The payment is created and the breakdown is recorded against it.
Percentage breakdownSend breakdownType: "percentage" with values summing to 100.The payment is created and the breakdown is recorded against it.

Sandbox behaves exactly like production here: a failed attempt does not end the payment, and the status only settles on a successful payment or on expiry. See Callbacks before you code against a failure callback. Sandbox does not redistribute funds to the breakdown identifiers, because that step is not live in either environment yet.

Error codes

Every code below is returned with status: false. Match the string exactly and branch on it, never on the dialog message text, which is written for display and can change.

Re-sending an already-used externalId never re-executes the payment: the recorded result is replayed verbatim, including the original error code (safe retry, no double charge).

Example failure
application/json
{
  "status": false,
  "code": "400",
  "dialog": {
    "title": "Error",
    "message": "Invalid request"
  },
  "actions": null,
  "extra": null,
  "data": null
}

Payment codes

CodeMeaningRecommended handling
currency.not_supportedOnly LBP and USD are accepted on this route.Send a supported currency.
collect.wrong_denominationThe account has no collect denomination configured.Contact Whish (account setup).
collect.wrong_phone_formatThe customer phone number could not be normalized.Fix the phone number format (E.164).
collect.wrong_email_formatAn email address supplied for the payment failed validation.Correct the address, or omit it.
refresh.denominationsService denomination not available or configured server-side.Retry later; contact Whish if persistent.
credit_card_payment.account_verification_requiredMerchant account is not KYC or financially verified for this operation.Complete account verification with Whish.
whish_pay.missing_pricing_idNo pricing configured for the merchant's Whish Pay profile.Contact Whish.
whish_pay.code_generation_failedPayment-code generation for the payment URL failed.Retry; escalate if persistent.
external_id.already_usedThe externalId is linked to another amount or another session.Use a new externalId for a genuinely new payment.
purchase.confirmThe quoted price changed since the request was prepared (new price in extra.price).Re-quote and resubmit with the new price.
failPricing or amount computation failed (reason in the dialog).Check the dialog message; verify amount and currency.

Common codes (all payment & billing endpoints)

Authentication, session, and request validation:

CodeMeaningRecommended handling
auth.session_not_existMissing or invalid session token.Re-authenticate, then retry.
auth.session_expiredSession token expired.Re-authenticate, then retry.
400Required parameters or headers are missing, or the body failed field-level validation. A malformed breakdown is rejected here.Fix the request payload; do not retry unchanged.
timestamp.invalidRequest timestamp is outside the allowed clock-skew window.Sync the client clock (NTP), then retry.
request.invalid / device.invalid_osDevice or channel-level validation rejected the request.Verify your integration headers; contact Whish if persistent.
500Unhandled server-side exception.Retry with backoff using the same externalId to avoid double execution; report if persistent.
itel.unknown_errorGeneric fallback for unexpected errors or server misconfiguration.Retry with backoff; escalate if persistent.
emoji.not_supportedPayload contained characters the database rejects (for example emoji).Strip unsupported characters, then retry.
deprecationThe endpoint version is deprecated.Migrate to the current endpoint version.

Account, balance, and limits:

CodeMeaningRecommended handling
account.terminatedThe calling account has been terminated.Contact Whish (not recoverable client-side).
account.dealer.terminatedThe parent dealer account is terminated.Contact Whish.
sales.denomination_not_activeThe requested service/denomination is disabled.Verify the denomination id; retry later or contact Whish.
sales.schema_not_activePricing schema not configured/active for this account and service.Contact Whish (configuration issue).
sales.exceeded_daily_limitDaily amount/count limit reached.Wait for the next day, or request a limit increase.
sales.exceeded_monthly_limitMonthly limit reached.Wait, or request a limit increase.
sales.lost_transactions_existEarlier transaction results were not acknowledged.Query the status of prior transactions (replay their externalIds) before submitting new ones.
fatalInternal failure while registering the externalId (defensive path; not expected in practice).Retry with the same externalId; escalate if persistent.

Version history

Notable changes to this API and its reference, newest first. The current version is the one shown at the top of this page.

VersionDateChange
v1.0.02026-08-20Initial release of Whish Pay Split (POST /payment/whish/split) with breakdown and breakdownType. Request and response contract final; fund redistribution to each breakdown identifier is still being finalized.

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. The OpenAPI 3.1 specification is also available to generate client SDKs or validate requests.

Download collectionOpenAPI specAll 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.