Create Offers

Post liquidity for sale on the Magma marketplace - create, update, and toggle offers with conditions and onchain priority.

To sell liquidity on Magma you publish an offer: a listing that says how much capacity you'll provide, at what price, with what guarantees, and to which buyers. Once published, buyers can fill it through liquidity.buy or market.order.create.

This page covers creating and managing offers. For accepting and fulfilling the orders that land against them, see Sell Liquidity.

Selling requires an Amboss API key and at least one connected node. Connect your node first at account.amboss.tech/settings/nodes or programmatically via account.node.connect.


1. Create an offer

The market.offer.create mutation takes a CreateOfferInput and returns the new offer_id.

mutation CreateOffer($input: CreateOfferInput!) {
  market {
    offer {
      create(input: $input) {
        offer_id
      }
    }
  }
}
{
  "input": {
    "pubkey": "03abc...xyz",
    "total_size": 10000000,
    "min_size": 500000,
    "max_size": 5000000,
    "min_block_length": 4320,
    "fee_rate": 500,
    "base_fee": 1,
    "fee_rate_cap": 1000,
    "base_fee_cap": 100,
    "onchain_priority": "MEDIUM",
    "onchain_multiplier": 2,
    "conditions": [
      {
        "condition": "NODE_CAPACITY",
        "operator": "GREATER_THAN_OR_EQUAL_TO",
        "value": "10000000"
      }
    ]
  }
}
curl -X POST https://magma.amboss.tech/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AMBOSS_API_KEY" \
  -d '{
    "query": "mutation($input: CreateOfferInput!) { market { offer { create(input: $input) { offer_id } } } }",
    "variables": {
      "input": {
        "pubkey": "03abc...xyz",
        "total_size": 10000000,
        "min_size": 500000,
        "max_size": 5000000,
        "min_block_length": 4320,
        "fee_rate": 500,
        "base_fee": 1,
        "fee_rate_cap": 1000,
        "base_fee_cap": 100,
        "onchain_priority": "MEDIUM",
        "onchain_multiplier": 2
      }
    }
  }'
import { GraphQLClient, gql } from "graphql-request";

const magma = new GraphQLClient("https://magma.amboss.tech/graphql", {
  headers: { Authorization: `Bearer ${process.env.AMBOSS_API_KEY}` },
});

const CREATE_OFFER = gql`
  mutation CreateOffer($input: CreateOfferInput!) {
    market { offer { create(input: $input) { offer_id } } }
  }
`;

const { market } = await magma.request(CREATE_OFFER, {
  input: {
    pubkey: process.env.MY_NODE_PUBKEY,
    total_size: 10_000_000,
    min_size: 500_000,
    max_size: 5_000_000,
    min_block_length: 4320,        // ~30 days
    fee_rate: 500,                 // 500 ppm routing fee
    base_fee: 1,                   // 1 sat base
    fee_rate_cap: 1000,
    base_fee_cap: 100,
    onchain_priority: "MEDIUM",
    onchain_multiplier: 2,
  },
});

console.log("Offer ID:", market.offer.create.offer_id);

CreateOfferInput fields

FieldRequiredTypeDescription
pubkeyStringThe pubkey of your connected node providing the liquidity.
total_sizeFloatTotal satoshis you're advertising on this offer.
min_sizeFloatMinimum sats per order. Defaults to the marketplace minimum.
max_sizeFloatMaximum sats per order. Defaults to total_size.
min_block_lengthFloatMinimum blocks the channel must stay open. Common values: 4320 (30 days), 8640 (60 days), 12960 (90 days).
fee_rateFloatYour routing fee rate, in ppm.
base_feeFloatYour routing base fee, in sats.
fee_rate_capFloatMaximum ppm you promise not to exceed for min_block_length.
base_fee_capFloatMaximum base fee (sats) you promise not to exceed for min_block_length.
onchain_priorityOnchainPriorityLOW, MEDIUM, or HIGH. Selects the fee tier for the funding TX.
onchain_multiplierFloat1–5x multiplier on the funding TX fee. Higher = faster confirmation.
conditions[OfferConditionsInput!]Buyer eligibility rules (see below).

Response

{
  "data": {
    "market": {
      "offer": {
        "create": {
          "offer_id": "off_01HX9YQK7P8MVZ3FN4G2RWS6CD"
        }
      }
    }
  }
}

2. Restrict buyers with conditions

conditions is an optional list. Every condition must pass for a buyer's node to qualify. Each entry is a (condition, operator, value) triple.

conditionChecksValue type
NODE_CAPACITYBuyer's total capacity, in sats.Integer string
NODE_CHANNELSNumber of channels the buyer has.Integer string
NODE_SOCKETSBuyer's socket strings.Substring (use CONTAINS)
PARALLEL_CHANNELSWhether the buyer already has a channel with you."true" / "false"
TERMINAL_WEB_RANKBuyer's Amboss reputation rank.Integer string

Operators: EQUAL_TO, NOT_EQUAL_TO, GREATER_THAN, GREATER_THAN_OR_EQUAL_TO, LESS_THAN, LESS_THAN_OR_EQUAL_TO, CONTAINS, DOES_NOT_CONTAIN.

Example: "well-connected nodes with public sockets only"

[
  { "condition": "NODE_CAPACITY", "operator": "GREATER_THAN_OR_EQUAL_TO", "value": "1000000000" },
  { "condition": "NODE_CHANNELS", "operator": "GREATER_THAN_OR_EQUAL_TO", "value": "20" },
  { "condition": "NODE_SOCKETS",  "operator": "CONTAINS",                  "value": "ipv4" }
]

See Orders and Offers for the full schema.


3. Update an offer

market.offer.update accepts the same fields as create, except pubkey (an offer's node is immutable). Everything is optional - pass only what you're changing.

mutation UpdateOffer($input: UpdateOfferInput!) {
  market {
    offer {
      update(input: $input) {
        success
      }
    }
  }
}
{
  "input": {
    "offer_id": "off_01HX9YQK7P8MVZ3FN4G2RWS6CD",
    "fee_rate": 750,
    "max_size": 3000000
  }
}
const UPDATE_OFFER = gql`
  mutation UpdateOffer($input: UpdateOfferInput!) {
    market { offer { update(input: $input) { success } } }
  }
`;

await magma.request(UPDATE_OFFER, {
  input: {
    offer_id: offerId,
    fee_rate: 750,
    max_size: 3_000_000,
  },
});

Updates only affect new orders. Already-accepted orders keep the fees, caps, and lock duration that were locked in at acceptance time (see promises on MarketOrder).


4. Toggle an offer on or off

market.offer.toggle flips an offer between ENABLED and DISABLED. A disabled offer rejects new orders but lets in-flight orders complete.

mutation ToggleOffer($input: ToggleOfferInput!) {
  market {
    offer {
      toggle(input: $input) {
        status
      }
    }
  }
}
{ "input": { "offer_id": "off_01HX9YQK7P8MVZ3FN4G2RWS6CD" } }
{ "data": { "market": { "offer": { "toggle": { "status": "DISABLED" } } } } }

status will be ENABLED, DISABLED, or ADMIN_DISABLED (the last one is a hard stop set by Amboss support - you can't toggle it back).


5. List your offers

query ListMyOffers {
  user {
    market {
      offers {
        offers(page: { limit: 20, offset: 0 }) {
          list {
            id
            status
            total_size
            locked_size
            filled_orders
            created_at
          }
          total
        }
      }
    }
  }
}

Onchain fee tier

onchain_priority and onchain_multiplier together control how much you'll spend on the funding TX when an order is accepted:

onchain_priorityMaps to
LOWMempool low-priority tier (slowest, cheapest).
MEDIUMDefault.
HIGHHigh-priority tier (fastest).

The multiplier is a scalar applied on top, capped at 5x. Useful if you want to always confirm fast regardless of mempool state.

Funding TX fees are paid by the seller out of the channel's commitment. Setting these too high reduces your margin per order; too low risks WAITING_FOR_ON_CHAIN_CONFIRMATION stalling during congestion.


Next steps