Resolve a swap
/swap/emergencyAlso available as POST /swap/resolve. Takes a decision on a swap that cannot simply execute. Two situations need you:
RATE_CHANGED— a fixed-rate deposit differs from the ordered amount by more than the 0.5 % tolerance (issue.typeisLESSorMORE). The status response carriesproposed: what the user would get for the actually deposited amount at the current market rate, with the fixed-rate fee unchanged. ChooseEXCHANGEorREFUND.REFUNDINGwithissue.needsRefundAddress= true — the deposit will be sent back (below the pair minimum, more than we can pay out, or the user chose a refund) but no refund address is known. SendREFUNDwith anaddress.
Choices:
EXCHANGE— continue the swap: the deposited amount is exchanged at the current market rate (proposed.rate) and paid out. The swap moves toEXCHANGINGimmediately. Only onRATE_CHANGED.REFUND— send the deposit back to the refund address, minus the refund fee (1 % of the deposit; the on-chain fee of the refund is paid by Bitania). The swap isREFUNDINGuntil the refund is sent, thenREFUNDEDwithrefund.tx. Passaddressif none was given at creation; an address that is already set is not changed through the API.
Deposits below the pair minimum or larger than our available balance never wait for you: they are refunded automatically as soon as a refund address is known, which is why we recommend always passing refundAddress on /swap/create. Partners with auto_execute_on_mismatch enabled never see RATE_CHANGED either: their fixed-rate mismatches execute at the current rate automatically (slippage on the swap shows the difference).
Manual review: a swap in FAILED is being looked at by Bitania operations (for example a payout that failed on-chain); EXCHANGE and REFUND are refused with an "under manual review" message and the swap resolves to DONE or REFUNDED without your action. Keep polling and show the user a neutral "processing" state.
Authentication: HMAC-signed (v2) request — X-API-KEY, X-API-SIGN, X-API-TIMESTAMP, X-API-NONCE headers. See the Swap Aggregator Guide.
Authorization (dual-mode): same as /swap/status — the swap must belong to your key, or you include its token.
Request
Request Body (required)
Content-Type: application/json
{
id: string, required — Swap id
choice: string, required, enum: EXCHANGE|REFUND — Action choice
address: string — Refund address (required for REFUND when no refund address is set; validated for the source chain)
token: string — Optional per-swap token from /swap/create; authorizes the call from a different API key
}
Response 200
The updated swap: EXCHANGING after EXCHANGE, REFUNDING (issue.choice REFUND, issue.needsRefundAddress false) after REFUND
{
code: integer, e.g. 0 — 0 = success, 1 = error
msg: string, e.g. "" — Error message (empty on success)
data: object — Swap order (returned by create, status, advance and emergency)
{
id: string, e.g. "k7m3px9qw2" — Swap id: 10 characters (dry-run swaps: "DR" + 8). Use it for status and emergency calls.
token: string, e.g. "uYx9j2k3Lm4nOp5qRs6tUw" — Per-swap access token (22 characters). Lets another API key, or a shared link, read the status. Treat it as a secret.
type: string, enum: float|fixed — Rate type
status: string, enum: NEW|PENDING|CONFIRMING|RATE_CHANGED|EXCHANGING|SENDING|REFUNDING|DONE|EXPIRED|REFUNDED|FAILED — Current status (see Get swap status)
time: object — UNIX timestamps in seconds
{
reg: integer — Swap creation time
start: integer|null — When the deposit was first detected
finish: integer|null — When the swap ended (DONE or REFUNDED)
update: integer — Last change
expiration: integer — End of the payment window
left: integer|null — Seconds left in the payment window; null once a deposit was seen or the swap ended
}
from: object — What the user sends
{
code: string, e.g. "TRX" — Currency code
coin: string, e.g. "TRX" — Coin ticker
network: string, e.g. "TRX" — Chain
name: string, e.g. "Tron" — Display name
amount: string, e.g. "1000" — Amount the user is expected to send (string, full precision)
amountDeposited: string, e.g. "0" — Sum of the confirmed deposits so far
address: string — Deposit address to show the user
reqConfirmations: integer, e.g. 1 — Confirmations we wait for before crediting a deposit
tx: object|null — The deposit transaction, once detected
{
id: string — Transaction hash
amount: string — Deposited amount
fee: string|null — Network fee (not known for deposits)
feeCurrency: string|null — Currency of the fee
timeReg: integer — When we first saw the transaction
timeBlock: integer|null — When it reached the required confirmations
confirmations: integer — Current confirmation count
}
}
to: object — What the user receives
{
code, coin, network, name — as in from
amount: string, e.g. "339.15" — Amount we pay out: the quote until the deposit is confirmed, the final amount afterwards
address: string — Destination address
tx: object|null — The payout transaction, once sent: id, amount, fee (the network fee we paid), feeCurrency, timeReg, confirmations (refreshed while you poll, until the coin's own threshold), txKey (Monero only: the transaction key that proves the payment to the recipient's address; null elsewhere)
}
refund: object
{
address: string|null — Refund address (from the create call, or from an emergency REFUND call)
tx: object|null — The refund transaction, once sent
}
issue: object — Why the swap is not simply executing, and what was decided
{
type: string|null, enum: LESS|MORE|BELOW_MINIMUM|NO_LIQUIDITY — LESS / MORE: a fixed-rate deposit under / over the ordered amount (RATE_CHANGED). BELOW_MINIMUM: the deposit is under the pair minimum. NO_LIQUIDITY: the payout is more than we hold. Both of the latter are refunded automatically.
reason: string|null — Human-readable explanation while the swap is RATE_CHANGED, REFUNDING or FAILED
needsRefundAddress: boolean — true when we want to refund but no refund address is known: send one with POST /swap/emergency (choice REFUND)
choice: string, enum: NONE|EXCHANGE|REFUND — What was decided: EXCHANGE (executed at the new rate), REFUND (being / been refunded)
}
rate: string, e.g. "0.34" — Rate at creation: to-coin per 1 from-coin
rateActual: string|null — Rate actually applied; set when the swap is DONE
fee: object — Our service fee: { percent: number, e.g. 0.25 — amount: string, in the to-coin }
slippage: number|null — Applied rate vs quoted rate, in percent; set when the deposit is credited (0 for a fixed-rate swap honoured as quoted)
proposed: object|null — While RATE_CHANGED: the re-quote the user accepts with EXCHANGE: { rate, amount }
dryrun: boolean — true for simulated swaps
url: string|null — Public status page of the swap on bitania.com (null for dry-run swaps)
}
}
Response 400
Validation or business error, for example: EXCHANGE is only possible while the swap is RATE_CHANGED, REFUND is only possible while the swap is RATE_CHANGED, or REFUNDING without a refund address, A refund address is required, The refund address is not a valid TRX address, A refund address is already set for this swap; contact support to change it, This swap is under manual review; Bitania support resolves it, Dry-run swaps never need a decision
{
code: integer, e.g. 1 — Error code (1)
msg: string, e.g. "Missing required fields: from, to, amount" — Error message
data: null
}
Response 401
Authentication failed: unknown or disabled key, IP not whitelisted (Invalid credentials), or a bad signature, stale timestamp or reused nonce (Invalid signature)
{
code: integer, e.g. 1
msg: string, e.g. "Invalid credentials"
data: null
}
Response 429
Rate limit of this endpoint exhausted; wait retry_after seconds (also sent as the Retry-After header)
{
code: integer, e.g. 1
msg: string, e.g. "Rate limit exceeded. Please retry after 12 seconds."
data: null
retry_after: integer, e.g. 12
}