Sell Liquidity
Fulfil Magma orders as a seller - accept, reject, and confirm channel openings. Authenticated mutations with full code examples.
Once you've created an offer, buyers can place orders against it. This page covers the three seller mutations that move an order from creation to a confirmed channel: accept, reject, and add transaction.
All three live under market.order.seller.* and require an authenticated API key.
Always check the order's status before sending a mutation. Each operation is only valid in a specific state - sending one at the wrong time will fail. See Order Lifecycle.
Seller's view of the lifecycle
1. Accept an order
Required state: WAITING_FOR_SELLER_APPROVAL.
Provide a BOLT11 HODL invoice. The buyer will pay it, and the payment is held until you confirm channel opening. The invoice must have an expiry of at least 48 hours.
mutation AcceptOrder($input: SellerAcceptOrdersInput!) {
market {
order {
seller {
accept(input: $input) {
success
}
}
}
}
}{
"input": {
"order_id": "ec562479-a4b8-44f4-95b4-150b310832de",
"payment_request": "lnbc125u1p..."
}
}curl -X POST https://magma.amboss.tech/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AMBOSS_API_KEY" \
-d '{
"query": "mutation($input: SellerAcceptOrdersInput!) { market { order { seller { accept(input: $input) { success } } } } }",
"variables": {
"input": {
"order_id": "ec562479-a4b8-44f4-95b4-150b310832de",
"payment_request": "lnbc125u1p..."
}
}
}'import { GraphQLClient, gql } from "graphql-request";
const magma = new GraphQLClient("https://magma.amboss.tech/graphql", {
headers: { Authorization: `Bearer ${process.env.AMBOSS_API_KEY}` },
});
const ACCEPT = gql`
mutation AcceptOrder($input: SellerAcceptOrdersInput!) {
market { order { seller { accept(input: $input) { success } } } }
}
`;
await magma.request(ACCEPT, {
input: {
order_id: orderId,
payment_request: hodlInvoice, // BOLT11 with ≥ 48h expiry
},
});SellerAcceptOrdersInput
| Field | Required | Type | Description |
|---|---|---|---|
order_id | ✅ | String | The order to accept. |
payment_request | ✅ | String | BOLT11 HODL invoice for the order's fees.seller amount. Expiry ≥ 48 hours. |
Generate the invoice as a HODL invoice (preimage held by you, released only on channel confirmation). A regular invoice will settle as soon as the buyer pays, which removes the non-custodial guarantee that backs Magma orders.
2. Reject an order
Required state: WAITING_FOR_SELLER_APPROVAL.
Use this if you can't or won't fulfil the order (insufficient on-chain funds, buyer doesn't meet your standards, etc.). The order moves to SELLER_REJECTED and the offer's locked_amount is released.
mutation RejectOrder($input: SellerRejectOrdersInput!) {
market {
order {
seller {
reject(input: $input) {
success
}
}
}
}
}{ "input": { "order_id": "ec562479-a4b8-44f4-95b4-150b310832de" } }curl -X POST https://magma.amboss.tech/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AMBOSS_API_KEY" \
-d '{
"query": "mutation($input: SellerRejectOrdersInput!) { market { order { seller { reject(input: $input) { success } } } } }",
"variables": { "input": { "order_id": "ec562479-a4b8-44f4-95b4-150b310832de" } }
}'const REJECT = gql`
mutation RejectOrder($input: SellerRejectOrdersInput!) {
market { order { seller { reject(input: $input) { success } } } }
}
`;
await magma.request(REJECT, { input: { order_id: orderId } });3. Confirm channel opening (add transaction)
Required state: WAITING_FOR_CHANNEL_OPEN.
After the buyer pays your HODL invoice, you have a deadline to broadcast the channel funding TX. Once it's in the mempool, call add_transaction to tell Magma which TX is the channel.
The tx_id must include the output index as TRANSACTION_ID:OUTPUT_INDEX. The output identifies which UTXO in the TX is the channel anchor.
mutation AddTransaction($input: SellerAddTransactionInput!) {
market {
order {
seller {
add_transaction(input: $input) {
success
}
}
}
}
}{
"input": {
"order_id": "ec562479-a4b8-44f4-95b4-150b310832de",
"tx_id": "5e8a3f...c4f1:0"
}
}curl -X POST https://magma.amboss.tech/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AMBOSS_API_KEY" \
-d '{
"query": "mutation($input: SellerAddTransactionInput!) { market { order { seller { add_transaction(input: $input) { success } } } } }",
"variables": {
"input": {
"order_id": "ec562479-a4b8-44f4-95b4-150b310832de",
"tx_id": "5e8a3f...c4f1:0"
}
}
}'const ADD_TX = gql`
mutation AddTransaction($input: SellerAddTransactionInput!) {
market { order { seller { add_transaction(input: $input) { success } } } }
}
`;
await magma.request(ADD_TX, {
input: {
order_id: orderId,
tx_id: `${fundingTxId}:${outputIndex}`,
},
});SellerAddTransactionInput
| Field | Required | Type | Description |
|---|---|---|---|
order_id | ✅ | String | The order being fulfilled. |
tx_id | ✅ | String | TRANSACTION_ID:OUTPUT_INDEX of the funding TX. |
Once the funding TX confirms with the agreed capacity, the order moves to VALID_CHANNEL_OPENING and the HODL invoice settles - you get paid. If the channel doesn't match the agreed terms, the order ends in INVALID_CHANNEL_OPENING and the buyer's payment is refunded automatically.
4. List orders you need to act on
The OrderInput.action_needed filter returns just the orders where the seller is expected to do something.
query MySales {
user {
market {
orders {
sales(input: { action_needed: true }, page: { limit: 25, offset: 0 }) {
list {
id
status
timeout
amount { satoshi { sats usd } }
destination { pubkey alias }
}
total
}
}
}
}
}Poll this query at 30–60s intervals or pair it with a UI that auto-refreshes. Each result is either:
WAITING_FOR_SELLER_APPROVAL→ accept or rejectWAITING_FOR_CHANNEL_OPEN→ broadcast TX and calladd_transaction
Reference flow (seller side)
import { GraphQLClient, gql } from "graphql-request";
const magma = new GraphQLClient("https://magma.amboss.tech/graphql", {
headers: { Authorization: `Bearer ${process.env.AMBOSS_API_KEY}` },
});
const MY_SALES = gql`
query MySales {
user { market { orders { sales(input: { action_needed: true }) {
list {
id status destination { pubkey } amount { satoshi { sats } }
}
} } } }
}
`;
const ACCEPT = gql`
mutation Accept($input: SellerAcceptOrdersInput!) {
market { order { seller { accept(input: $input) { success } } } }
}
`;
const ADD_TX = gql`
mutation AddTx($input: SellerAddTransactionInput!) {
market { order { seller { add_transaction(input: $input) { success } } } }
}
`;
while (true) {
const { user } = await magma.request(MY_SALES);
for (const order of user.market.orders.sales.list) {
if (order.status === "WAITING_FOR_SELLER_APPROVAL") {
const invoice = await myNode.createHodlInvoice({
amount: order.amount.satoshi.sats,
expirySec: 48 * 3600,
});
await magma.request(ACCEPT, {
input: { order_id: order.id, payment_request: invoice },
});
}
if (order.status === "WAITING_FOR_CHANNEL_OPEN") {
const { txid, outputIndex } = await myNode.openChannel({
peer: order.destination.pubkey,
sats: order.amount.satoshi.sats,
});
await magma.request(ADD_TX, {
input: { order_id: order.id, tx_id: `${txid}:${outputIndex}` },
});
}
}
await new Promise((r) => setTimeout(r, 30_000));
}Next steps
- Create Offers - post the listings buyers fill
- Order Lifecycle - every status, what triggers each transition
- Tracking Orders - polling pattern (same for sellers)
- Errors - failure modes and refunds