Errors
Common errors returned by the Magma API and how to handle them, including auth failures, payment errors, and order cancellation reasons.
How to read errors from the Magma API, and what to do about the ones you're most likely to hit.
Error shape
The Magma API is GraphQL. Errors come back in the standard errors array on the response:
{
"errors": [
{
"message": "Unauthorized token",
"extensions": { "code": "UNAUTHORIZED" }
}
],
"data": null
}The message is the user-facing description. Some errors include a machine-readable code in extensions. Surface message in your UI and key your retry logic off the code when present.
A 200 OK HTTP status doesn't mean the operation succeeded. GraphQL returns 200 even on partial failures. Always check errors before reading data.
Authentication errors
| Message | When it happens | What to do |
|---|---|---|
Unauthorized token | API key or session key missing, expired, or revoked. | Mint a new key at account.amboss.tech/settings/api-keys. For session keys, the original liquidity.buy response is the only place the key was returned - create a new session. |
Forbidden | The key is valid but doesn't own the resource you're trying to read or mutate (e.g. another account's offer). | Check that the key is for the right Amboss account. |
See Authentication for the credential matrix.
Validation errors
| Message | When it happens | What to do |
|---|---|---|
Invalid ID | A UUID-shaped argument (order_id, offer_id, …) is malformed or not a UUID at all. | Validate IDs before sending; never embed user input in an ID field without checks. |
connection_uri must be in <pubkey>@<host>:<port> format | The connection_uri you passed to liquidity.buy isn't in the expected shape. | Use lncli getinfo (LND) or lightning-cli getinfo (CLN) and pick a public socket. Tor (*.onion:9735) works. |
| Min amount violated | usd_cents below 500 (the $5 minimum), or outside an offer's min_amount/max_amount. | Bump the amount, or query market.liquidity.liquidity_per_usd to size your order against the offer's bounds. |
| GraphQL argument errors | Required field missing, wrong type, or enum value not allowed. | Compare your variables against the schema. The errors array points at the offending field. |
Payment errors (MarketOrderPaymentStatus)
Every order carries a payment_status alongside its status. The non-success values are terminal - once you see one of these, the payment side of the order is dead.
payment_status | Meaning | Recovery |
|---|---|---|
PENDING_PAYMENT | Invoice issued, waiting for the buyer to pay. | No action - keep polling. The Lightning invoice has its own expiry; if you wait past it you'll roll to one of the failure states below. |
SUCCESSFUL_PAYMENT | Buyer paid the HODL invoice. | None - proceed to channel opening. |
HODL_INVOICE_TIMEOUT | Buyer didn't pay before the seller's HODL invoice expired. | The order is dead. Create a new one. Ensure the seller's invoice expiry is at least 48 hours when you provide it. |
SELLER_INVOICE_EXPIRED | The seller's invoice expired before payment landed. | Same as above - new order, longer expiry. |
PAYMENT_FAILED | The buyer's payment attempt failed at the wallet or routing layer. | Check the buyer's logs and retry from the buyer side. The order is still open if the invoice hasn't expired. |
PAYMENT_REJECTED_BY_DESTINATION | The seller's node rejected the HTLC. | Almost always a seller-side problem (peer connectivity, channel state). Cancel and re-order from a different offer. |
INVALID_PAYMENT_SECRET | Preimage mismatch - the HTLC didn't carry the expected secret. | Treat as a payment failure. Create a new order. |
Order cancellation reasons
Pass an OrderCancellationReason when calling market.order.cancel. The choice is the audit trail for why the order didn't complete:
| Reason | Use when |
|---|---|
CHANNEL_SIZE_OUT_OF_BOUNDS | The buyer's request fell outside the offer's min_amount / max_amount after pricing. |
UNABLE_TO_CONNECT_TO_NODE | Seller couldn't reach the buyer's node over Lightning to open the channel. |
UNABLE_TO_PAY | Buyer's wallet couldn't pay the HODL invoice (no routing path, insufficient outbound, etc.). |
mutation CancelOrder($input: CancelOrderInput!) {
market {
order {
cancel(input: $input) {
success
}
}
}
}{
"input": {
"order_id": "ec562479-a4b8-44f4-95b4-150b310832de",
"cancellation_reason": "UNABLE_TO_CONNECT_TO_NODE"
}
}Terminal order statuses (MarketOrderStatus)
Some status values mean the order is permanently failed. Stop polling and surface the error when you see any of these:
| Status | Cause |
|---|---|
SELLER_REJECTED | Seller declined the order. |
SELLER_FAILED_TO_REACT | Seller didn't accept or reject before timing out. |
BUYER_REJECTED | Buyer cancelled before paying. |
BUYER_FAILED_TO_PAY | Buyer didn't pay the HODL invoice in time. |
SELLER_FAILED_TO_OPEN_CHANNEL | Seller accepted but never broadcast a funding transaction. |
SELLER_FAILED_TO_SEND_SWAP | Seller couldn't complete the swap leg. |
INVALID_CHANNEL_OPENING | A funding transaction was broadcast but didn't match the agreed terms. |
ADMIN_CLOSED | Manually closed by Amboss support (rare). |
See Order Lifecycle for the full state diagram.
Server errors
| Symptom | Likely cause | What to do |
|---|---|---|
500 HTTP status with no GraphQL body | Internal database or processing failure on our side. | Retry with exponential backoff. If it persists, open a ticket. |
404 from REST (/api/lsp/v1/...) endpoints | Order ID doesn't exist or isn't on the LSP account. | Verify the order_id you stored; it must come from a previous create_order on the same type. |
| Network timeout | Your client gave up before we responded. | Retry. The GraphQL endpoint is idempotent for read queries; mutations may not be - use idempotency_key patterns or check the resulting order state before re-issuing. |
Retry strategy
- Read queries (
market.*,user.market.orders.get_order, etc.) are safe to retry freely. Exponential backoff, max ~5 attempts. liquidity.buyandliquidity.create_subscriptionare not idempotent. Don't retry on timeout without first readinguser.transactions.transaction_listto confirm whether the previous call already created an order.- Seller mutations (
accept,reject,add_transaction) only make sense from one status. If a retry hits a state where the operation no longer applies, the API will reject it - re-fetch the order, look atstatus, and branch accordingly.
Next steps
- Order Lifecycle - what each status means and where the transitions are
- Tracking Orders - how to detect terminal states efficiently
- Authentication - credential troubleshooting