What each step buys
Skip preview and you are asking a payer to approve an amount you have not calculated. Skip the destination and there is nothing to spend — a transfer takes a
destinationId, never an account number.
Where things fail
Failures cluster, and knowing which step owns which failure makes them much easier to handle.
The middle row is the one to plan for. Preview does not check the balance, so a preview can succeed and the transfer can still fail with
insufficient_funds a moment later.
Same currency or not
The two paths differ by one object.fxQuoteId when the currencies differ and you get 409 fx_quote_required. Send one that was quoted for a different account, destination, side, or amount and you get a mismatch.
Where the money goes
Three destination shapes, decided when the destination was created, not here:own_account— between your own accounts. Settles internally, usuallycompletedimmediately.sakawave_tag— to any Sakawave account by its public handle, including your customers’ wallets.external— out to a bank or mobile-money account, over a route. This is the one that takes time and can fail at a provider.
destinationType, and carries a snapshot of the destination in destination — so history stays readable even after a recipient is deleted.
Asynchronous by default
An external transfer returnspending. It has been accepted and submitted; settlement happens afterwards.
Do not poll it in a loop. Subscribe to fiat.withdrawal.completed and fiat.withdrawal.failed, and treat GET /v1/transfers/{transferId} as the authority when you need to check — a webhook can arrive out of order, the resource cannot be wrong.
The shape of the API
There is no cancel, no amend, and no approve. A transfer is created and then observed.