> ## Documentation Index
> Fetch the complete documentation index at: https://docs.machines.cash/llms.txt
> Use this file to discover all available pages before exploring further.

# Withdrawals

> Partner withdrawal discovery, range validation, estimation, relay setup, and onchain execution.

<Tip>
  Read endpoints for withdrawals use `deposits.read` scope. Create still requires `withdrawals.write`.
</Tip>

## Overview

* `GET /partner/v1/withdrawals/assets`: list destination assets.
* `POST /partner/v1/withdrawals/range`: get min/max for a destination pair.
* `POST /partner/v1/withdrawals/estimate`: quote destination receive amount for a given amount.
* `POST /partner/v1/withdrawals`: create relay + fetch Rain withdrawal signature payload.

<Warning>
  `POST /partner/v1/withdrawals` uses a breaking request body shape with `source` and `destination` objects.
</Warning>

<Note>
  Withdrawal routes are dynamic (BTC/SOL/EVM and others) and can change. Always call
  <code>assets</code>, <code>range</code>, and <code>estimate</code> before create.
  In sandbox, source is fixed to <code>rUSD</code> on <code>Base Sepolia</code>.
  <code>source.contractId</code> is optional.
</Note>

## Flow

```mermaid theme={null}
sequenceDiagram
  participant Backend as "Your Backend"
  participant MachinesAPI as "Machines API"
  participant Rain as "Rain Contract"
  participant ChangeNOW as "ChangeNOW Relay"
  participant Chain as "Blockchain"

  Backend->>MachinesAPI: GET /partner/v1/withdrawals/assets
  MachinesAPI-->>Backend: assets[]
  Backend->>MachinesAPI: POST /partner/v1/withdrawals/range
  MachinesAPI-->>Backend: range {min/max}
  Backend->>MachinesAPI: POST /partner/v1/withdrawals/estimate
  MachinesAPI-->>Backend: estimate {estimatedToAmount}
  Backend->>MachinesAPI: POST /partner/v1/withdrawals
  MachinesAPI->>ChangeNOW: create exchange (pay-in = Rain recipient)
  MachinesAPI->>Rain: get signed withdrawal payload
  MachinesAPI-->>Backend: status + signature + relay metadata
  Backend->>Chain: sign/broadcast withdrawAsset(...)
```

## API flows (step-by-step)

<Steps>
  <Step title="1) Discover destination assets">
    <b>Endpoint</b>

    : <a href="/api/withdrawals/assets" target="_blank" rel="noreferrer"><code>GET /partner/v1/withdrawals/assets</code></a>

    ```bash theme={null}
    curl -s \
      -H "Authorization: Bearer $PARTNER_SESSION_TOKEN" \
      "https://api.machines.cash/partner/v1/withdrawals/assets?q=hbar&limit=20"
    ```

    You can optionally pass `sourceChainId` and `sourceTokenAddress` as hints, but they are not required.
  </Step>

  <Step title="2) Get min/max for chosen route">
    <b>Endpoint</b>

    : <a href="/api/withdrawals/range" target="_blank" rel="noreferrer"><code>POST /partner/v1/withdrawals/range</code></a>

    ```bash theme={null}
    curl -s \
      -H "Authorization: Bearer $PARTNER_SESSION_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "destination": { "currency": "hbar", "network": "hbar" }
      }' \
      https://api.machines.cash/partner/v1/withdrawals/range
    ```
  </Step>

  <Step title="3) Estimate destination receive amount">
    <b>Endpoint</b>

    : <a href="/api/withdrawals/estimate" target="_blank" rel="noreferrer"><code>POST /partner/v1/withdrawals/estimate</code></a>

    ```bash theme={null}
    curl -s \
      -H "Authorization: Bearer $PARTNER_SESSION_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "destination": { "currency": "hbar", "network": "hbar", "extraId": "optional-tag" },
        "amountCents": 2500
      }' \
      https://api.machines.cash/partner/v1/withdrawals/estimate
    ```
  </Step>

  <Step title="4) Create withdrawal signature payload (breaking schema)">
    <b>Endpoint</b>

    : <a href="/api/withdrawals/create" target="_blank" rel="noreferrer"><code>POST /partner/v1/withdrawals</code></a>

    ```bash theme={null}
    curl -s \
      -H "Authorization: Bearer $PARTNER_SESSION_TOKEN" \
      -H "Idempotency-Key: wdrl-123456" \
      -H "Content-Type: application/json" \
      -d '{
        "amountCents": 2500,
        "source": {
          "contractId": "optional-uuid"
        },
        "destination": {
          "currency": "hbar",
          "network": "hbar",
          "address": "0.0.123456",
          "extraId": "optional-tag"
        },
        "adminAddress": "0x..."
      }' \
      https://api.machines.cash/partner/v1/withdrawals
    ```

    Ready responses include:

    <ul>
      <li><code>execution</code> info for contract call routing.</li>
      <li><code>relay</code> metadata (<code>payinAddress</code>, <code>payinExtraId</code>, <code>payoutAddress</code>, <code>payoutExtraId</code>, route ids).</li>
    </ul>
  </Step>

  <Step title="5) Execute onchain">
    <ul>
      <li><code>from</code> must be the same admin EOA used in the create request (<code>adminAddress</code>).</li>
      <li><code>to</code> must be <code>execution.callTarget</code> (controller or coordinator), not the collateral proxy address.</li>
      <li>Use <code>parameters</code> in order: <code>\[collateralProxy, token, amount, recipient, expiresAt, executorSalt, executorSignature]</code>.</li>
      <li>If <code>execution.callPath = controller\_v1</code>, call 7-arg <code>withdrawAsset(...)</code>.</li>
      <li>If <code>execution.callPath = coordinator\_v2</code>, call 10-arg <code>withdrawAsset(...)</code> and include v2 admin signature values.</li>
      <li>If <code>status</code> is <code>pending</code>, wait <code>retryAfterSeconds</code> and retry the same request with the same idempotency key.</li>
    </ul>
  </Step>
</Steps>

## Onchain execution details

For `coordinator_v2`, you must generate an additional admin signature before calling the contract.

```ts theme={null}
// response from POST /partner/v1/withdrawals
const { data } = withdrawalResponse;
if (data.status !== "ready" || !data.parameters || !data.execution?.callTarget) {
  throw new Error("withdrawal signature is not ready");
}

const [collateralProxy, token, amount, recipient, expiresAt, executorSalt, executorSignature] =
  data.parameters;

// tx sender must match adminAddress used in create request
const adminAddress = "0x..."; // same value you sent as adminAddress

if (data.execution.callPath === "controller_v1") {
  // to = data.execution.callTarget
  await controller.withdrawAsset(
    collateralProxy,
    token,
    amount,
    recipient,
    expiresAt,
    executorSalt,
    executorSignature,
  );
} else if (data.execution.callPath === "coordinator_v2") {
  // 1) Read nonce from collateral proxy
  const nonce = await collateral.adminNonce();

  // 2) Build typed data
  const adminSalt = randomBytes(32);
  const domain = {
    name: "Collateral",
    version: "2",
    chainId: data.execution.chainId,
    verifyingContract: collateralProxy,
    salt: hexlify(adminSalt),
  };
  const types = {
    Withdraw: [
      { name: "user", type: "address" },
      { name: "asset", type: "address" },
      { name: "amount", type: "uint256" },
      { name: "recipient", type: "address" },
      { name: "nonce", type: "uint256" },
    ],
  };
  const message = {
    user: adminAddress,
    asset: token,
    amount: BigInt(amount),
    recipient,
    nonce,
  };
  const adminSignature = await signer.signTypedData(domain, types, message);

  // 3) Call coordinator (to = data.execution.callTarget)
  await coordinator.withdrawAsset(
    collateralProxy,
    token,
    amount,
    recipient,
    expiresAt,
    executorSalt,
    executorSignature,
    [adminSalt],
    [adminSignature],
    true,
  );
}
```

## Relay metadata fields

<ResponseField name="relay.changeNowId" type="string">
  Provider exchange id for relay tracking.
</ResponseField>

<ResponseField name="relay.payinAddress" type="string">
  Address Rain withdrawal sends funds to.
</ResponseField>

<ResponseField name="relay.payinExtraId" type="string">
  Optional memo/tag required on relay pay-in side.
</ResponseField>

<ResponseField name="relay.payoutAddress" type="string">
  Final destination address configured on the relay.
</ResponseField>

<ResponseField name="relay.payoutExtraId" type="string">
  Optional destination memo/tag configured for payout.
</ResponseField>
