Withdraw Funds

Initiate a Rails Yield withdrawal — set a withdrawal target, derive the master-account password hash, and track funds becoming available as channels unwind.

Withdrawals in Rails Yield are not instant. Bitcoin is committed to Lightning channels and Magma leases; to release it, channels must close or leases must unwind. You signal intent by raising a withdrawal target, and automation drains liquidity back to on-chain balance over time.

Sequence

Password hash

The withdrawal mutation requires the master-account password_hash, derived client-side from the account password using Argon2id (master key + password as salt). The hash is sent to the server, never the plaintext password. See Security for the derivation details.

This is not just authentication; it's the entropy that gates withdrawal. There is no recovery path if the password is lost. Store and derive it securely.

Mutation

mutation IncreaseWithdrawalTarget($input: IncreaseWithdrawalInput!) {
  node {
    withdrawal {
      increase_withdrawal_target(input: $input) {
        amount
      }
    }
  }
}
curl -X POST https://rails.amboss.tech/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AMBOSS_DASHBOARD_TOKEN" \
  -d '{
    "query": "mutation($input: IncreaseWithdrawalInput!) { node { withdrawal { increase_withdrawal_target(input: $input) { amount } } } }",
    "variables": {
      "input": {
        "node_id": "<NODE_ID>",
        "amount_sats": "5000000",
        "password_hash": "<argon2id-derived hash>"
      }
    }
  }'
const { node } = await client.request(INCREASE_WITHDRAWAL_TARGET, {
  input: {
    node_id: process.env.NODE_ID,
    amount_sats: "5000000",
    password_hash: passwordHash,
  },
});
console.log("new target:", node.withdrawal.increase_withdrawal_target.amount);

IncreaseWithdrawalInput

FieldTypeDescription
node_idString!UUID of the node to withdraw from.
amount_satsString!Total target as a string-encoded integer. The new target is current + amount_sats.
password_hashStringMaster-account Argon2id hash. Required for production teams.

amount_sats increases the running target rather than setting it absolutely. To "withdraw an additional 1 BTC", pass 100000000. The response amount is the new total target.

Tracking progress

Poll withdrawal_target and funds.available until they converge:

query WithdrawalProgress($nodeId: String!, $input: LiquidityProviderInput) {
  node {
    deployed_node(node_id: $nodeId) {
      withdrawal_target
      funds_available_at
    }
  }
  user {
    liquidity_provider(input: $input) {
      funds {
        available { sats usd }
        reserved  { sats usd }
        total     { sats usd }
      }
    }
  }
}
FieldWhat it tells you
withdrawal_targetThe total sats requested for withdrawal.
funds.available.satsSats currently on-chain and withdrawable (not waiting on a channel close).
funds.reserved.satsSats still locked in channels.
funds_available_atISO timestamp of the projected full unwind (latest channel lease close + a force-close buffer).

Move funds to your wallet

Once funds.available.sats covers the target, send the Bitcoin out using the LND macaroon directly - Rails does not handle the on-chain send. From the dashboard, the "Withdraw" dialog walks through this; from the API:

curl --header "Grpc-Metadata-macaroon: $MACAROON_HEX" \
  -X POST $LND_REST/v1/transactions \
  -d '{"addr":"bc1q...","amount":"5000000","sat_per_vbyte":"5"}'

The Rails-issued macaroon does not grant on-chain spend. For Fully Managed nodes, withdrawals route through Rails' withdrawal pipeline (triggered by the increase_withdrawal_target mutation); for Third Party Hosted you spend with your own admin macaroon.

Common scenarios

Partial withdraw

Set amount_sats to less than the total balance. Automation will close just enough channels to cover the request, preserving routing capacity.

Max withdraw

Set amount_sats to funds.total.sats - funds.available.sats (i.e. the reserved portion). All channels will close.

Cancelling a target

There is no direct "decrease" mutation; the target is monotonically increasing. Practically, contact support before a large change-of-mind to avoid unnecessary channel closures.

Error reference

ErrorCause
Incorrect passwordpassword_hash does not match the team's stored Argon2id hash.
Node not foundnode_id is not owned by your team.
Insufficient fundsTarget exceeds total balance.

Next steps

  • Security - password hash derivation in detail
  • Monitor - poll funds and projection
  • LND API - move funds on-chain once available