Whish Money
Whish Money
Partner Integration Guide

International Money Transfer

Programmatically send money, quote fees, track transfers, and reconcile statements through the Whish Money network.

This reference describes the HTTP request and response models used to integrate with Whish Money, and the operations available to move funds across the network.

v6.4.9 Platform: Whish Money JSON over HTTPS Author: TecFrac
Authentication
username + key
Production base URL
api.woocash.money
Currencies
USD · LBP

Integration flow

  1. 1
    Quote the fee. Call Get transfer fees to show the customer the total cost before you commit.
  2. 2
    Create the transfer. Call Create a transfer with the recipient details, amount, and a unique Request-id. Set paymentMode to choose how the funds are delivered: cash collection or mobile wallet.
  3. 3
    Track the outcome by mode. Cash collection issues an ltnNumber (status SENT) that the receiver collects at a Whish Money agent; collection moves it to RECEIVED and triggers your callback. Mobile wallet credits the receiver's wallet directly and is final in the Create a transfer response, with no callback.
  4. 4
    Confirm and reconcile. Use Get transfer status to verify the outcome; for cash collection it is also your fallback if a callback did not arrive.
  5. 5
    Cancel if needed. A cash-collection transfer can be reversed with Cancel a transfer while the funds have not yet been collected. Mobile wallet transfers are final and cannot be canceled.

Authentication & headers

Every request is authenticated with two credentials issued by Whish Money, sent as HTTP headers. Unless an endpoint states otherwise, all requests must include the headers below, plus Content-Type: application/json on requests that send a body.

HeaderValueDescription
username-Account username, provided by Whish Money.
key-Secret API key, provided by Whish Money.
Content-Typeapplication/jsonRequired for requests that send a JSON body.
Request-id-Required on every request. Unique numeric request identifier generated by your account (the API accepts numeric values only). See the note below.

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.

Send Request-id on every request, and mint a new one for every new Create a Transfer. It must be numeric (Long), so use epoch milliseconds plus a counter, or your own numeric transaction reference. Store it before you send.

Reuse it on purpose to recover from a timeout. Resend the identical request with the same Request-id: if it never reached us the transfer is created, and if it did, nothing is duplicated and you get transfer.already.done. Then call Get Transfer Status with that requestId to read the outcome. Reusing an id for a genuinely new transfer hits the same guard, and that transfer is never created.

Environments

Whish Money provides two isolated environments. Validate your full integration in Sandbox before requesting Production access.

EnvironmentBase URLPurpose
Sandboxhttps://sandbox.woocash.money/apiTesting, QA, and integration verification.
Productionhttps://api.woocash.moneyLive environment for real users and transactions.

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

Building a request URL

Each endpoint path in this reference begins with /api/woo/…. Append the endpoint path to the environment base URL. Note that the Sandbox base URL already ends in /api, so the full Sandbox URL contains /api/api/….

For the example endpoint POST /api/woo/send/money:

EnvironmentFull URL
Sandboxhttps://sandbox.woocash.money/api/api/woo/send/money
Productionhttps://api.woocash.money/api/woo/send/money

The two environments use different base-URL conventions. Sandbox includes a trailing /api segment while Production does not. Construct URLs by concatenating the base URL and the endpoint path exactly as shown above, rather than assuming both environments share the same prefix. Endpoint sections below list the path only.

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 on the HTTP status code.

FieldTypeDescription
statusBooleantrue = success · false = failure.
codeStringError code when status = false; null on success. See Error codes.
dataObject | ArrayPayload of the operation on success; null on failure. Shape varies per endpoint: an object for most, an array for List payment modes.
dialogObjectOptional user-friendly message (title / message) for display. Usually null on success, but some successful responses (for example a Mobile Wallet top-up) include an informational dialog.
dialog.titleStringTitle of the message (e.g. "Sorry!").
dialog.messageStringHuman-readable description of the outcome, for display to the end user. Usually an error, but some successful responses carry an informational message.
extraObjectReserved for additional context; typically null.
Envelope shape success
Success envelope
{
  "status": true,
  "code": null,
  "dialog": null,
  "extra": null,
  "data": { }
}
Envelope shape failure
Failure envelope
{
  "status": false,
  "code": "api.account.invalid_request",
  "dialog": {
    "title": "Sorry!",
    "message": "api.account.invalid_request"
  },
  "extra": null,
  "data": null
}

Recommended handling: read status first. On true, process data. On false, switch on code for programmatic logic and surface dialog.message to end users. Specialized rules apply to Get Transfer Status and the Create a Transfer / Mobile Wallet responses.

The envelope status is a Boolean and answers only whether the call worked. It is a different field from data.status, the String that carries the transfer's own progress (SENT, RECEIVED, CANCELED_FULL). A completed call reporting a transfer that has not arrived yet is status: true with data.status: "SENT".

Payment modes

Every transfer runs in one of two modes, set by paymentMode on Create a Transfer. They behave differently end to end, so decide which one you integrate before you build.

AspectCash CollectionMobile Wallet
paymentModeCASH_COLLECTIONMOBILE_WALLET
How funds arriveReceiver collects cash from a Whish Money agent using the ltnNumber.Funds are credited directly to the receiver's Whish wallet.
Outcome timingMoves through statuses over time (SENTRECEIVED).Instant and final in the Create a Transfer response.
CallbackYes, on SENTRECEIVED (set callbackUrl). See Callbacks.None. Nothing to notify later.
Pending (code: 500)Escalate to Whish Money to determine the final outcome; treat as Pending meanwhile. See Transaction handling.Same: escalate to Whish Money and treat as Pending. See Transaction handling.
CancellableYes, before collection. See Cancel a Transfer.No. The outcome is already final.

Transaction status & handling

Interpret every transfer outcome using the rule below. It applies to both payment modes and lets you resolve most cases automatically, without escalating to Whish Money.

Transaction status interpretation

OutcomeConditionHow to treat it
Successfulstatus: trueConsider the transaction successful.
Failedstatus: false and code is not 500Consider it failed. Handle it automatically based on the returned error code. No escalation to Whish Money is required.
Pendingstatus: false and code is 500 (Internal Server Error)Consider it pending. Escalate and follow up with Whish Money to determine the final outcome.

Receipt issuance

Issue a receipt to the customer only in these cases:

CaseIssue receipt?
Successful (status: true)Yes
Pending (status: false, code: 500)Yes (pending investigation)
Failed (business-validation error)No

Do not issue receipts for failed transactions. Any transaction returned as failed because of one of the final-failure error codes below must not produce a customer receipt.

Handling timeouts & network issues

If you do not receive a response from POST /api/woo/send/money because of a timeout, network interruption, or any communication issue, resend the same request using the exact same Request-id. This is safe:

  • If the original request never reached Whish Money, the transfer is initiated now.
  • If the original request was already received, no duplicate is created and the resend answers transfer.already.done. That is a confirmation, not a failure: it tells you the transfer exists. It does not replay the original payload, so call Get Transfer Status with that requestId to read the outcome.

Resend before you check status. Confirm the original POST /api/woo/send/money was received before relying on Get Transfer Status. If you query the status of a transfer that was never received, the status endpoint returns sending.transfer.invalid.info, which means the referenced transaction does not exist in Whish Money's records (most likely the original request never arrived). Resend the original request with the same Request-id first, then check status only after the transfer is registered.

Error codes to treat as final failures

Automate the following as failed transactions and resolve them on your side. None of these require contacting Whish Money.

Error codeDescription
api.account.invalid_requestInvalid account request.
receiver.emptyReceiver information is missing.
receiver.invalid.phone_numberReceiver phone number is invalid.
receiver.customer.invalid_nameReceiver name is invalid.
receiver.customer.info_invalidReceiver information is invalid.
receiver.customer.name_enlgishReceiver name must be in English.
sender.invalid.phone_numberSender phone number is invalid.
sender.customer.invalid_nameSender name is invalid.
sender.customer.info_invalidSender information is invalid.
sender.customer.name_enlgishSender name must be in English.
transfer.already.doneThis Request-id was already used, so the transfer was not created again. On a resend after a timeout this is the expected, safe answer: call Get Transfer Status with that requestId to read the outcome.
sender.missing_infoSender information is missing.
currency.not_supportedCurrency not supported.
transfer.invalid_amountAmount is negative or sending amount not found in price list.
transfer.reason_emptyReason not provided in the request.
transfer.from_country_not_allowedTransfers from this country are not allowed.
transfer.sourceOfFunds_emptySource of funds not provided in the request.
transfer.not_allowed_countriesTransfer to these countries is not allowed.
transfer.account_reach_balance_limitReached balance limit or insufficient funds.
transfer.exceeds_account_transaction_limitSending amount exceeds the allowed transaction limit.
service.inactiveService is inactive.
service.price_not_activeService price is not active.
transfer.miss_configTransfer configuration is missing.
service.transaction_status_unknownTransaction status is unknown.
service.transaction_failedTransaction failed.
sender.invalid.date.of.birthInvalid date of birth.
transfer.exceeds_account_daily_limitReached daily sending limit.
receiver.whish.account.not.existsWhish account does not exist (Mobile Wallet mode).
sender.kyc.neededSender's KYC is required.
receiver.kyc.neededReceiver's KYC is required.
sender.interview.neededAdditional information required for the sender.
receiver.interview.neededAdditional information required for the receiver.

The code: 500 pending case is the only outcome that warrants follow-up with Whish Money.

Shared models

Objects reused by the request bodies above.

Customer model (sender / receiver)

Represents a party to the transfer. Carries the basic information needed to identify a sender or receiver.

FieldTypeRequiredDescription
firstNameStringRequiredFirst name.
middleNameStringOptionalMiddle name.
lastNameStringRequiredLast name.
phoneStringRequiredPhone number.
extraInformationStringOptionalAdditional information about the customer.
identityIdentityConditionalIdentity details about the customer. Required for the receiver when paymentMode is CASH_COLLECTION, which is the mode that requires dateBirth. Optional for the sender in both payment modes. See Date of birth requirements.
Customer (receiver) sample
"receiver": {
  "firstName": "Alex",
  "middleName": "something",
  "lastName": "hunter",
  "phone": "96170123456",
  "extraInformation": "useful info",
  "identity": {
    "dateBirth": "01-01-1990",
    "nationality": "lebanese"
  }
}

Identity model

Provides additional detail about a customer's identity.

FieldTypeRequiredDescription
nationalityStringOptionalNationality.
dateBirthStringConditionalDate of birth. Format: dd-MM-yyyy. Whether it is required depends on the party and the paymentMode: see Date of birth requirements.
Identity sample
"identity": {
  "dateBirth": "01-01-1990",
  "nationality": "lebanese"
}

Date of birth requirements

identity.dateBirth uses the format dd-MM-yyyy. Whether you must send it depends on the party and on paymentMode:

PartyCASH_COLLECTIONMOBILE_WALLET
Sender
sender.identity.dateBirth
OptionalOptional
Receiver
receiver.identity.dateBirth
RequiredOptional

Because receiver.identity.dateBirth is required for CASH_COLLECTION, a Cash Collection transfer must carry a receiver.identity object. sender.identity is optional in both payment modes: send it only when you hold the data.

Get transfer fees

Retrieve the fees for an amount to be sent, given the amount, currency, and payment mode.

POST/api/woo/send/money/fees
Example request · cURL
cURL · Sandbox
curl -X POST 'https://sandbox.woocash.money/api/api/woo/send/money/fees' \
  -H 'username: YOUR_USERNAME' \
  -H 'key: YOUR_KEY' \
  -H 'Request-id: 1720598400123' \
  -H 'Content-Type: application/json' \
  -d '{
  "fromCountry": 1,
  "toCountry": 1,
  "currencyId": 2,
  "amount": 100,
  "deductFees": false,
  "paymentMode": "CASH_COLLECTION",
  "phoneNumber": "96170123456"
}'

Request body

FieldTypeRequiredDescription
fromCountryLongRequiredSending country id. Read the ids available to you from List sending countries rather than hard-coding them.
toCountryLongRequiredReceiving country id. Always Lebanon (1): this API delivers funds in Lebanon, so only the sending country varies.
currencyIdLongRequiredCurrency id (USD = 2, LBP = 1).
paymentModeStringOptionalEnum: CASH_COLLECTION, MOBILE_WALLET. Defaults to CASH_COLLECTION when omitted.
amountdoubleRequiredSending amount in the chosen currency.
deductFeesBooleanOptionalIf true, fees are deducted from the sending amount, so the receiver collects less than amount. If false, fees are charged on top of amount. Defaults to false when omitted.
phoneNumberStringOptionalRecipient phone number the fees are quoted for.
Request
Request body
{
  "fromCountry": 1,
  "toCountry": 1,
  "currencyId": 2,
  "amount": 100,
  "deductFees": false,
  "paymentMode": "CASH_COLLECTION",
  "phoneNumber": "96170123456"
}

Response fields (in data)

The cost the user will pay to send the amount.

Field (in data)TypeDescription
feesDoubleFees in the sending currency.
amountDoubleAmount to be sent.
totalAmountDoubleSum of amount and fees.
Response · 200 success
application/json
{
  "status": true,
  "code": null,
  "dialog": null,
  "extra": null,
  "data": {
    "fees": 1,
    "amount": 100,
    "totalAmount": 101
  }
}
Response · 200 failure
application/json
{
  "status": false,
  "code": "transfer.invalid_amount",
  "dialog": {
    "title": "Sorry!",
    "message": "Transfer Amount Outside Range"
  },
  "extra": null,
  "data": null
}

Create a transfer

Create a transfer through Whish Money, specifying the amount, currency, countries, and parties.

POST/api/woo/send/money
Example request · cURL
cURL · Sandbox
curl -X POST 'https://sandbox.woocash.money/api/api/woo/send/money' \
  -H 'username: YOUR_USERNAME' \
  -H 'key: YOUR_KEY' \
  -H 'Request-id: 1720598400123' \
  -H 'Content-Type: application/json' \
  -d '{
  "paymentMode": "CASH_COLLECTION",
  "fromCountry": 1,
  "toCountry": 1,
  "currencyId": 2,
  "amount": 100,
  "sender": {
    "firstName": "personName",
    "lastName": "LastName",
    "phone": "96170654321"
  },
  "receiver": {
    "firstName": "personName",
    "lastName": "LastName",
    "phone": "96170123456",
    "identity": { "dateBirth": "01-01-1990", "nationality": "lebanese" }
  },
  "reason": "Donations & Gifts",
  "sourceOfFunds": "Income from Own Business",
  "callbackUrl": "https://merchant.example/whish/callback"
}'

Request-id must be unique for each transfer. See Authentication & headers.

Request body

FieldTypeRequiredDescription
paymentModeStringOptionalEnum: CASH_COLLECTION, MOBILE_WALLET. Defaults to CASH_COLLECTION when omitted.
fromCountryLongRequiredSending country id. Read the ids available to you from List sending countries rather than hard-coding them.
toCountryLongRequiredReceiving country id. Always Lebanon (1): this API delivers funds in Lebanon, so only the sending country varies.
currencyIdLongRequiredCurrency id (USD = 2, LBP = 1).
amountdoubleRequiredSending amount in the chosen currency.
senderCustomerRequiredInformation about the sender.
receiverCustomerRequiredInformation about the receiver.
deductFeesBooleanOptionalIf true, fees are deducted from the sending amount, so the receiver collects less than amount. If false, fees are charged on top of amount. Defaults to false when omitted.
reasonStringRequiredReason for the transfer. See Reason values.
sourceOfFundsStringRequiredSource of funds (e.g. Salary, Sale of Property or Goods). See Source of Funds.
callbackUrlStringOptionalCash Collection only. URL Whish Money calls when the transaction changes from SENT to RECEIVED. Not used for Mobile Wallet. See Callbacks.

Both sender and receiver are required. Each is a Customer object; a request missing either party is rejected. For CASH_COLLECTION, receiver.identity.dateBirth is required as well; sender.identity is optional in both payment modes. See Date of birth requirements.

Request
Request body
{
  "paymentMode": "CASH_COLLECTION",
  "fromCountry": 1,
  "toCountry": 1,
  "currencyId": 2,
  "amount": 100,
  "sender": {
    "firstName": "personName",
    "middleName": "m",
    "lastName": "LastName",
    "phone": "96170654321",
    "extraInformation": "extraInformation",
    "identity": {
      "dateBirth": "01-01-1990",
      "nationality": "lebanese"
    }
  },
  "receiver": {
    "firstName": "personName",
    "middleName": "m",
    "lastName": "LastName",
    "phone": "96170123456",
    "extraInformation": "extraInformation",
    "identity": {
      "dateBirth": "01-01-1990",
      "nationality": "lebanese"
    }
  },
  "deductFees": false,
  "reason": "Donations & Gifts",
  "sourceOfFunds": "Income from Own Business",
  "callbackUrl": "https://merchant.example/whish/callback"
}

Response fields (in data)

Returned when the transfer was successfully created.

Field (in data)TypeDescription
ltnNumberStringIn Cash Collection, the LTN number the receiver uses to collect the transfer from Whish Money cash agents. In Mobile Wallet it is returned as a reference only: funds are credited directly, so there is nothing to collect with it.
transferIdStringReference of the transfer.
chargesDoubleFees applied to the transfer.

Mobile Wallet response interpretation

CaseConditionTreat as
Successstatus = trueSuccess
Pendingstatus = false and code = 500Pending
Failurestatus = false and code ≠ 500Failure

The value 500 is the Pending signal in both payment modes: on status: false, compare code against 500 before treating the send as failed. Every other code is a string error identifier and, per the rule above, a final failure.

Response · 200 success
application/json
{
  "status": true,
  "code": null,
  "dialog": null,
  "extra": null,
  "data": {
    "ltnNumber": "9262-6531-4941",
    "transferId": "2020125708",
    "charges": 1.0
  }
}
Response · 200 failure
application/json
{
  "status": false,
  "code": "transfer.reason_empty",
  "dialog": {
    "title": "Sorry!",
    "message": "Transfer Reason is Missing"
  },
  "extra": null,
  "data": null
}
Mobile Wallet success
Mobile Wallet top-up success
{
  "status": true,
  "code": null,
  "dialog": {
    "title": "Success",
    "message": "Your Account Has Been Successfully Topup"
  },
  "extra": null,
  "data": {
    "ltnNumber": "MW223871662783",
    "transferId": "AA 2500000104",
    "currency": null,
    "charges": 1.0,
    "collectionAmount": 100.0,
    "balance": 0.0,
    "transactionId": 130482
  }
}

Mobile Wallet response fields (in data)

In addition to ltnNumber, transferId, and charges, a Mobile Wallet send returns:

Field (in data)TypeDescription
collectionAmountDoubleAmount credited to the receiver's wallet, in the receiving currency's minor-free units as returned.
balanceDoubleYour account balance after the send.
transactionIdLongNumeric wallet transaction identifier. Distinct from transferId (the transfer reference string).
currencyStringCurrency of the credited amount; may be null.

Cancel a transfer

Cancel a transfer that has already been sent, by its LTN number.

Cancellation is available for Cash Collection transfers only. Mobile Wallet transfers cannot be canceled via API, because the funds are delivered instantly at send time. A cancel request for a Mobile Wallet transfer is rejected with transfer.cannot_be_canceled.

POST/api/woo/cancel/send/money
Example request · cURL
cURL · Sandbox
curl -X POST 'https://sandbox.woocash.money/api/api/woo/cancel/send/money' \
  -H 'username: YOUR_USERNAME' \
  -H 'key: YOUR_KEY' \
  -H 'Request-id: 1720598400123' \
  -H 'Content-Type: application/json' \
  -d '{ "ltnNumber": "9262-6531-4941" }'

Request body

FieldTypeRequiredDescription
ltnNumberStringRequiredLTN number of the transfer to cancel.
reasonStringOptionalReason for cancellation. Recorded on your account activity log for audit; it is not stored against the cancellation itself and is not returned by any endpoint.
Request
Request body
{
  "ltnNumber": "9262-6531-4941",
  "reason": "Customer changed their mind"
}

Response fields (in data)

Field (in data)TypeDescription
ltnNumberStringLTN number of the transfer.
transferIdStringReference of the transfer.
statusStringResulting status of the transfer, CANCELED_FULL on success. Note the spelling: this and the callback use CANCELED_FULL, while the same cancellation shows as CANCELLED in List account activity.
receiverIdentityObjectReceiver identity details when present; null for a cancellation.
Response · 200 success
application/json
{
  "status": true,
  "code": null,
  "dialog": null,
  "extra": null,
  "data": {
    "ltnNumber": "9262-6531-4941",
    "transferId": "2020125708",
    "status": "CANCELED_FULL",
    "receiverIdentity": null
  }
}
Response · 200 failure
application/json
{
  "status": false,
  "code": "transfer.not_exists",
  "dialog": {
    "title": "Sorry!",
    "message": "Transfer Not Found"
  },
  "extra": null,
  "data": null
}

Get transfer status

Retrieve the current status of a transfer.

POST/api/woo/send/money/status
Example request · cURL
cURL · Sandbox
curl -X POST 'https://sandbox.woocash.money/api/api/woo/send/money/status' \
  -H 'username: YOUR_USERNAME' \
  -H 'key: YOUR_KEY' \
  -H 'Request-id: 1720598400123' \
  -H 'Content-Type: application/json' \
  -d '{ "transferId": "2020125708", "ltnNumber": "9262-6531-4941", "requestId": 1720598400123 }'

Request body

FieldTypeRequiredDescription
transferIdStringOptionalId of the transfer.
ltnNumberStringOptionalLTN number of the transfer.
requestIdLongOptionalThe Request-id header value you sent with the original Create a Transfer request. You can pass it here to look up any transaction by your own reference, for either payment mode.
Request
Request body
{
  "transferId": "2020125708",
  "ltnNumber": "9262-6531-4941",
  "requestId": 1720598400123
}

Response fields (in data)

Field (in data)TypeDescription
ltnNumberStringLTN number of the transfer.
transferIdStringReference of the transfer.
statusStringStatus of the transfer (e.g. SENT, RECEIVED, CANCELED_FULL).
receiverIdentityObjectReceiver identity details (see nested fields).
receiverIdentity.numberStringIdentity number.
receiverIdentity.typeStringIdentity type (e.g. Lebanese ID).
receiverIdentity.frontImageStringURL of the identity front image.
receiverIdentity.backImageStringURL of the identity back image.
Response · 200 success
application/json
{
  "status": true,
  "code": null,
  "dialog": null,
  "extra": null,
  "data": {
    "ltnNumber": "9262-6531-4941",
    "transferId": "2020125708",
    "status": "RECEIVED",
    "receiverIdentity": {
      "number": "1990xxxxxx",
      "type": "Lebanese ID",
      "frontImage": "https://cdn.woocash.money/id/2020125708-front.jpg",
      "backImage": "https://cdn.woocash.money/id/2020125708-back.jpg"
    }
  }
}

receiverIdentity is populated once the receiver has been identified (typically at collection); it may be null at earlier statuses such as SENT.

Response · 200 failure
application/json
{
  "status": false,
  "code": "transfer.not_exists",
  "dialog": {
    "title": "Sorry!",
    "message": "Transfer Not Found"
  },
  "extra": null,
  "data": null
}

Response handling

If status = true → treat the request as successful and read data.status for the actual transaction status (e.g. SENT, RECEIVED, CANCELED_FULL).

If status = false → branch on the error code:

sending.transfer.invalid.info → the referenced transfer does not exist in Whish Money's records. Usually this means the original Create a Transfer request never arrived. Resend it with the same Request-id before checking status again. See Handling timeouts & network issues.
transfer.not_exists → treat as Failed.
• Any other code → apply the transaction status interpretation (code: 500 is Pending and warrants follow-up; other codes are final failures).

List sending countries

Retrieve the countries your account may send from, and their id values. Pass the one you want to Get transfer fees and Create a transfer as fromCountry. toCountry is always Lebanon, because this API delivers funds in Lebanon.

Take country ids from this endpoint, not from these examples. In production your list is the set agreed with Whish Money for your account; in sandbox only Lebanon is enabled unless you ask for more. Call it again against production when you go live to confirm what your account has.

GET/api/woo/sending/countries
Example request · cURL
cURL · Sandbox
curl 'https://sandbox.woocash.money/api/api/woo/sending/countries' \
  -H 'username: YOUR_USERNAME' \
  -H 'key: YOUR_KEY' \
  -H 'Request-id: 1720598400123'

Request body: None.

Response fields (in data)

Field (in data.list[])TypeDescription
idLongCountry id.
nameStringCountry name.
codeStringISO country code (e.g. LB).
defaultCurrencyIdLongDefault currency id for the country, or null.
dateCreationLongCreation timestamp (epoch milliseconds).
Response · 200 success
application/json
{
  "status": true,
  "code": null,
  "dialog": null,
  "extra": null,
  "data": {
    "list": [
      {
        "id": 1,
        "name": "LEBANON",
        "code": "LB",
        "defaultCurrencyId": 2,
        "dateCreation": 1649173044000
      },
      {
        "id": 2,
        "name": "Afghanistan",
        "code": "AF",
        "defaultCurrencyId": null,
        "dateCreation": 1649173050000
      }
    ]
  }
}

List account activity

Retrieve transfers sent or received within a given period.

POST/api/woo/activity
Example request · cURL
cURL · Sandbox
curl -X POST 'https://sandbox.woocash.money/api/api/woo/activity' \
  -H 'username: YOUR_USERNAME' \
  -H 'key: YOUR_KEY' \
  -H 'Request-id: 1720598400123' \
  -H 'Content-Type: application/json' \
  -d '{ "fromDate": "2026-01-01 00:00:00", "toDate": "2040-12-31 23:59:59", "currencyId": 2 }'

Request body

FieldTypeRequiredDescription
fromDateStringRequiredStart date. Format: yyyy-MM-dd HH:mm:ss.
toDateStringRequiredEnd date. Format: yyyy-MM-dd HH:mm:ss.
currencyIdLongOptionalCurrency id (USD = 2, LBP = 1). Send null to include both.
Request
Request body
{
  "fromDate": "2026-01-01 00:00:00",
  "toDate": "2040-12-31 23:59:59",
  "currencyId": 2
}

Response fields (in data)

Field (in data.list[])TypeDescription
idStringTransfer id.
statusStringStatus of the transfer at this point: SENT, RECEIVED, or CANCELLED. This list is an event log, so a cancelled transfer appears twice, once SENT and again CANCELLED, sharing the same id and requestId. Note that a cancellation reads as CANCELLED here but as CANCELED_FULL on Cancel a transfer and in the callback.
amountDoubleAmount of the transfer.
feeDoubleFee of the transfer.
totalDoubleSum of amount and fee. The same quantity Get transfer fees returns as totalAmount.
currencyStringCurrency code.
dateStringTransaction date. Format: yyyy-MM-dd HH:mm:ss.
requestIdLongThe Request-id you sent when creating the transfer, echoed here so you can match this line to your own record.
Response · 200 success
application/json
{
  "status": true,
  "code": null,
  "dialog": null,
  "extra": null,
  "data": {
    "list": [
      {
        "id": "efi 2600000006",
        "status": "SENT",
        "amount": 100.0,
        "fee": 1.0,
        "total": 101.0,
        "currency": "USD",
        "date": "2026-07-21 12:25:14",
        "requestId": 1720598400123
      },
      {
        "id": "efi 2600000006",
        "status": "CANCELLED",
        "amount": 100.0,
        "fee": 1.0,
        "total": 101.0,
        "currency": "USD",
        "date": "2026-07-21 12:28:30",
        "requestId": 1720598400123
      },
      {
        "id": "efi 2600000007",
        "status": "SENT",
        "amount": 10.0,
        "fee": 0.1,
        "total": 10.1,
        "currency": "USD",
        "date": "2026-07-21 12:30:20",
        "requestId": 1720598400124
      }
    ]
  }
}
Response · 200 failure
application/json
{
  "status": false,
  "code": "transfer.invalid_amount",
  "dialog": {
    "title": "Sorry!",
    "message": "Error"
  },
  "extra": null,
  "data": null
}

Get account statement

Retrieve the account statement within a given period, grouped per currency.

POST/api/woo/statement
Example request · cURL
cURL · Sandbox
curl -X POST 'https://sandbox.woocash.money/api/api/woo/statement' \
  -H 'username: YOUR_USERNAME' \
  -H 'key: YOUR_KEY' \
  -H 'Request-id: 1720598400123' \
  -H 'Content-Type: application/json' \
  -d '{ "fromDate": "2026-01-01 00:00:00", "toDate": "2040-12-31 23:59:59", "currencyId": 2 }'

Request body

FieldTypeRequiredDescription
fromDateStringRequiredStart date. Format: yyyy-MM-dd HH:mm:ss.
toDateStringRequiredEnd date. Format: yyyy-MM-dd HH:mm:ss.
currencyIdLongOptionalCurrency id (USD = 2, LBP = 1). Send null to include both.
Request
Request body
{
  "fromDate": "2026-01-01 00:00:00",
  "toDate": "2040-12-31 23:59:59",
  "currencyId": 2
}

Response fields (in data)

Returns the statement grouped per currency.

FieldTypeDescription
data.list[]ArrayOne entry per currency.
…transactions[]ArrayTransactions for the currency.
…transactions[].idStringTransaction id.
…transactions[].typeStringTransaction type. TRANSFER is a send you made; TOPUP is account funding. Treat this as an open set: other values may appear.
…transactions[].debitDoubleAmount that raised your balance, for example a TOPUP. 0.0 when the entry did not add funds.
…transactions[].creditDoubleAmount that reduced your balance, for example a TRANSFER you sent (the amount plus its fee). 0.0 when the entry did not draw funds. Note the direction: balance = previous balance + debit − credit.
…transactions[].balanceDoubleBalance after the transaction.
…transactions[].currencyStringCurrency code.
…transactions[].dateStringTransaction date. Format: yyyy-MM-dd HH:mm:ss.
…transactions[].requestIdLongThe Request-id you sent when creating the transfer, echoed here so you can reconcile this line against your own record. null for entries with no originating request of yours, such as a TOPUP.
…currentBalanceObjectCurrent balance for the currency.
…currentBalance.currencyStringCurrency code.
…currentBalance.balanceDoubleBalance amount.
…currentBalance.dateStringFormat: yyyy-MM-dd HH:mm:ss.
Response · 200 success
application/json
{
  "status": true,
  "code": null,
  "dialog": null,
  "extra": null,
  "data": {
    "list": [
      {
        "transactions": [
          {
            "id": "",
            "type": "TOPUP",
            "debit": 1000.0,
            "credit": 0.0,
            "balance": 1000.0,
            "currency": "USD",
            "date": "2026-07-20 15:14:00",
            "requestId": null
          },
          {
            "id": "efi 2600000006",
            "type": "TRANSFER",
            "debit": 0.0,
            "credit": 101.0,
            "balance": 899.0,
            "currency": "USD",
            "date": "2026-07-21 12:25:14",
            "requestId": 1720598400123
          },
          {
            "id": "efi 2600000007",
            "type": "TRANSFER",
            "debit": 0.0,
            "credit": 10.1,
            "balance": 888.9,
            "currency": "USD",
            "date": "2026-07-21 12:30:20",
            "requestId": 1720598400124
          }
        ],
        "currentBalance": {
          "currency": "USD",
          "balance": 888.9,
          "date": "2026-07-21 12:35:00"
        }
      }
    ]
  }
}

List payment modes

Retrieve the payment modes supported by your account.

GET/api/woo/send/money/modes
Example request · cURL
cURL · Sandbox
curl 'https://sandbox.woocash.money/api/api/woo/send/money/modes' \
  -H 'username: YOUR_USERNAME' \
  -H 'key: YOUR_KEY' \
  -H 'Request-id: 1720598400123'

Request body: None.

Response · 200 success
application/json
{
  "status": true,
  "code": null,
  "dialog": null,
  "extra": null,
  "data": ["CASH_COLLECTION", "MOBILE_WALLET"]
}

Callbacks

callbackUrl applies to Cash Collection mode only. Mobile Wallet does not use callbacks: its outcome is determined instantly by the Create a Transfer response, which either succeeds or fails with no intermediate statuses to track, so there is nothing to notify later. See Payment modes.

In Cash Collection mode a transfer moves through several statuses (SENT, RECEIVED, CANCELED_FULL), so Whish Money needs a way to tell you when the transaction changes. When you provide a callbackUrl in the Create a Transfer request, Whish Money calls it automatically on one transition only: SENT to RECEIVED. That is the only callback you receive without asking for it. No other status produces a callback, including CANCELED_FULL, so do not wait on one for a cancellation: confirm it with Get transfer status, which is authoritative for every status.

Callback source IPs

Whish Money calls your callbackUrl from a fixed set of source IPs (Whish Money → you). 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.

Cash Collection transfer statuses

StatusMeaning
SENTThe transfer has been sent and is available for collection.
RECEIVEDThe receiver has collected the transfer.
CANCELED_FULLThe transfer has been fully canceled.

Callback request

Whish Money sends a POST with Content-Type: application/json to your callbackUrl, with two additional headers:

HeaderTypeDescription
timestampLongTime the callback was sent, in epoch milliseconds.
signatureStringBase64-encoded signature of the request, sent as the bare value with no prefix. The signing scheme is not published yet; ask Whish Money before relying on it.

Callback body

FieldTypeDescription
dateStringHuman-readable time of the event.
externalIdLongThe numeric Request-id you sent on Create a Transfer, echoed back exactly as you sent it so you can match this callback to your record. Sent as a JSON number, not a quoted string.
transferIdStringIdentifier of the transfer this callback is about.
statusStringThe transfer status this callback reports, for example SENT, RECEIVED, or CANCELED_FULL.

The callback body is a flat object. It does not use the status / code / data envelope that API responses use, so status here is the String transfer status directly, not the Boolean call result. It reports the same value you would read from data.status on Get transfer status.

Callback · headers
POST (headers)
Content-Type: application/json
timestamp: 1772094998000
signature: RzqRXZBTc/XiX1wYAXa6zIKcUNOkIiCwflfkcJCRXJWI+4d95eIZBdI8eRZwsshTi8HubfJTzkHQ/ApbDxcw+Q==
Callback · body
application/json
{
  "date": "Thu Feb 26 10:36:38 EET 2026",
  "externalId": 1720598400123,
  "transferId": "TEST 2600000014",
  "status": "RECEIVED"
}

Respond with HTTP 200 to acknowledge receipt. Treat the callback as a notification rather than proof: confirm anything you act on against Get Transfer Status, which is authoritative.

A callback reports the transfer's current status rather than announcing something new, so a repeat is not a new event. Reading status and applying it leaves you where you already were, which is why polling is safe. The one thing to guard is a side effect you only want to happen once, such as crediting an order or releasing goods: trigger those on the change of status you record, not on the arrival of a POST.

Reference data

Currencies

CurrencyId
USD2
LBP1

Countries

Receiving is always Lebanon (id = 1, code LB), because this API delivers funds in Lebanon. The sending countries enabled for your account come from List Sending Countries; pull them from there rather than hard-coding a list.

Payment modes

ValueDescription
CASH_COLLECTIONReceiver collects cash from a Whish Money agent using the ltnNumber.
MOBILE_WALLETFunds are credited directly to a Whish mobile wallet account.

Source of Funds (SoF)

ValueDescription
SalaryIncome earned from employment, wages, or regular payroll.
Income from Own BusinessRevenue generated from operating a personal or self-owned business.
Income from InvestmentsReturns or profits from investments such as stocks, bonds, dividends, or interest.
RentIncome received from renting out property, land, or other assets.
InheritanceFunds received from the estate of a deceased individual.
SavingsMoney accumulated and withdrawn from personal savings or bank accounts.
Sale of Property or GoodsProceeds from selling physical property, assets, or personal goods.
LoanMoney borrowed from financial institutions, individuals, or other entities.
Lottery / GamblingFunds from lottery winnings, betting, or other gambling activities.
OtherAny source of funds not covered by the categories above.

Reason

ValueDescription
Donations & GiftsVoluntary donations or personal gifts to individuals or organizations.
EducationEducational expenses such as tuition, fees, books, or training programs.
Family SupportFinancial assistance to family members for living or personal needs.
Medical TreatmentHealthcare services, treatments, medication, or hospital fees.
Purchase of GoodsBuying goods, products, or merchandise.
Travel ExpensesCosts associated with travel spending.
Insurance Plan / ClaimInsurance premiums, or funds received/used as part of an insurance claim.
Retirement / PensionContributions to or withdrawals from retirement or pension funds.
OtherAny reason not covered by the categories above.

Send reason and sourceOfFunds as the exact values listed in the tables above (for example reason: "Donations & Gifts", sourceOfFunds: "Income from Own Business").

Error codes

When status = false, the code field carries one of the identifiers below. Surface dialog.message to end users and branch your logic on code. Error codes are literal API values and are reproduced exactly, including their original spelling.

CodeDescriptionEndpoint(s)
api.account.invalid_requestInvalid account request.All
amount.not_allowedThe amount is less than the price or is negative.Fees
voucher_amount.not_allowedA voucher on your account could not be applied to this amount.Fees
transfer.invalid_amountAmount not within the price list / negative.Fees, Send, Activity
receiver.emptyReceiver information is missing.Send
receiver.invalid.phone_numberReceiver phone number is invalid.Send
receiver.customer.invalid_nameReceiver name is invalid.Send
receiver.customer.info_invalidReceiver information is invalid.Send
receiver.customer.name_enlgishReceiver name must be in English.Send
receiver.customer.under_ageReceiver is underage.Send
receiver.customer.date_of_birth_invalidReceiver identity.dateBirth is missing, not in dd-MM-yyyy, or in the future. Only checked when you send receiver.identity.Send
receiver.whish.account.not.existsWhish account does not exist (Mobile Wallet).Send
receiver.unknown.errorThe wallet credit failed for an unspecified reason (Mobile Wallet).Send
api.fetching.price_errorPricing could not be retrieved for the transfer (Mobile Wallet).Send
sender.invalid.phone_numberSender phone number is invalid.Send
sender.invalid.phone.numbersender.phone was empty. Note the spelling: this code uses dots throughout, unlike sender.invalid.phone_number above, which is a different check.Send
sender.customer.invalid_nameSender name is invalid.Send
sender.customer.info_invalidSender information is invalid.Send
sender.customer.name_enlgishSender name must be in English.Send
sender.customer.date_of_birth_invalidSender identity.dateBirth is missing, not in dd-MM-yyyy, or in the future. Only checked when you send sender.identity.Send
sender.missing_infoSender information is missing.Send
sender.invalid.date.of.birthInvalid date of birth.Send
sender.kyc.neededSender's KYC is required.Send
receiver.kyc.neededReceiver's KYC is required.Send
sender.interview.neededAdditional information required for the sender.Send
receiver.interview.neededAdditional information required for the receiver.Send
currency.not_supportedCurrency not supported.Send
transfer.reason_emptyReason not provided.Send
transfer.sourceOfFunds_emptySource of funds not provided.Send
transfer.from_country_not_allowedTransfers from this country are not allowed.Send
transfer.not_allowed_countriesTransfer to these countries is not allowed.Send
transfer.account_reach_balance_limitReached balance limit / insufficient funds.Send
transfer.exceeds_account_transaction_limitAmount exceeds per-transfer limit.Send
transfer.exceeds_account_daily_limitReached daily sending limit.Send
transfer.exceeds_customer_daily_limitThe receiver has reached their daily receiving limit. Distinct from the account limits above: this one is about the receiving customer, not your account.Send
transfer.exceeds_customer_yearly_limitThe receiver has reached their yearly receiving limit.Send
transfer.already.doneThis Request-id was already used, so no duplicate was created. Expected when resending after a timeout: read the outcome with Get Transfer Status using that requestId.Send
transfer.miss_configTransfer configuration is missing.Send
service.inactiveService is inactive.Send
service.price_not_activeService price is not active.Send
service.transaction_status_unknownTransaction status is unknown.Send
service.transaction_failedTransaction failed.Send
transfer.not_existsTransfer does not exist / id not found.Cancel, Status
transfer.cannot_be_canceledThe transfer is not cancellable: it was already canceled or received, or it is a Mobile Wallet transfer, which is final at send time and cannot be canceled.Cancel
sending.transfer.invalid.infoSending transfer info is invalid.Status
errorGeneric error.Statement, Modes

code may carry the numeric value 500 to indicate a Pending transaction. This is common for Mobile Wallet sends; for Cash Collection it is rare and only happens on an internal server error. Treat it as pending in either mode. See Mobile Wallet response interpretation.

Best practices

  1. Test in Sandbox first. Verify every flow against sandbox.woocash.money before requesting Production access, and remember the differing base-URL conventions between environments.
  2. Quote before you send. Call Get Transfer Fees to confirm fees and total, then send with the same amount and currency.
  3. Use a new Request-id for each new transfer, and reuse that id to retry it. Generate a unique numeric value (Long, for example epoch milliseconds plus a counter) per transfer and store it before you send. After a timeout, resend the identical request with the same id: either the transfer is created, or you get transfer.already.done and read the outcome via requestId. Retrying with a fresh id is what creates duplicates.
  4. Branch on status, not HTTP code. All responses return HTTP 200; the outcome lives in the body.
  5. Mobile Wallet is instant. Its outcome is final in the Create a Transfer response (no callback). Map status = false + code = 500 to Pending and reconcile later.
  6. Callbacks are Cash Collection only. Configure callbackUrl for Cash Collection and update state when the transfer moves from SENT to RECEIVED. Confirm any status you act on with Get Transfer Status, which is authoritative.
  7. Escalate only the pending case. code: 500 is the single outcome that warrants follow-up with Whish Money (treat as Pending). Every other non-success code is a final failure you resolve automatically. On Get Transfer Status, sending.transfer.invalid.info or transfer.not_exists means the transfer was never registered, so resend the original request with the same Request-id before querying again.
  8. Use English names. Sender and receiver names must be in English, or the send is rejected (*.name_enlgish).
  9. Store references. Persist the Request-id you sent, plus transferId and ltnNumber from the response. Once your integration is live, quote the Request-id when you raise a transaction with Whish Money: it is your own reference, you hold it even when a send times out with no response, and it is the id support will ask for. Keep ltnNumber too: it is what Cancel a transfer needs, and it is one of the ways to look up status.
  10. Secure your credentials. Keep username / key server-side and out of logs and version control.
  11. Respect limits. Design for per-transfer, daily, and balance limits, and surface dialog.message to end users when they are hit.
  12. Reconcile regularly. Use List Account Activity and Get Account Statement to reconcile your ledger against Whish Money.

Sandbox scenarios

Cash Collection

ScenarioInputExpected result
English nameValidPASS
Arabic nameInvalidFAIL

Mobile Wallet

ScenarioInputExpected result
Payment to wallet96170123456PASS
Payment to wallet96170123123FAIL

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
v6.4.92026-08-21Date of birth requirements updated for sender and receiver.
v6.4.82026-07-15Documentation refinements: sandbox cURL examples, callback reference, and reference clarifications.
v6.4.72026-02-16Added some error codes.
v6.4.62026-02-09Minor updates and improvements.
v6.4.52026-02-02Minor updates and improvements.
v6.4.42026-01-28DeductFees renamed to deductFees; deductFromAmount replaced by deductFees.

Postman & tools

Import the ready-made Postman collection for this API, set your username and key as collection variables (or attach a Sandbox / Production environment), and call every endpoint without writing code. Request-id auto-generates a unique numeric value per send.

Download collection OpenAPI spec All 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.