Skip to main content
Prerequisite: have a session token. See Authentication.

Flow

API flows (step‑by‑step)

1

1) Request a withdrawal signature

Endpoint: POST /withdrawals
Headers: Authorization: Bearer <SESSION_TOKEN>, Content-Type: application/json
Body:
curl -s \
  -H 'Authorization: Bearer <SESSION_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"amountCents": 5000, "tokenAddress": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "recipientAddress":"0xabc...", "chainId":8453 }' \
  https://dev-api.machines.cash/partner/v1/withdrawals
Ready response:
{
  "ok": true,
  "data": {
    "status": "ready",
    "retryAfterSeconds": null,
    "parameters": [
      "0xCollateralProxy",
      "0xToken",
      "5000",
      "0xRecipient",
      1716400000,
      "0xExecutorSalt",
      "0xExecutorSignature"
    ],
    "signature": { "data": "0x...", "salt": "0x..." },
    "expiresAt": "2026-01-01T00:00:00Z"
  },
  "summary": "withdrawal signature",
  "errors": []
}
If you receive status: “pending”, wait retryAfterSeconds and call again.
2

2) Execute onchain

Broadcast a transaction calling withdrawAsset with the returned parameters.Parameter mapping
  • Coordinator (v2) signature: withdrawAsset(collateralProxy, token, amount, recipient, expiresAt, executorSalt, executorSignature, adminSalt[], adminSignature[], directTransfer)
  • All values except the admin arrays come from the API response. You generate the admin arrays locally using your admin wallet.
Option A — Coordinator (v2)
  • Preferred when you have the Coordinator address for the user’s contract.
  • Add an admin EIP‑712 signature for the user’s admin address (must be linked).
import { Contract, Signature, ethers, randomBytes } from "ethers";

const COORDINATOR_ABI = [
  "function withdrawAsset(address collateralProxy,address token,uint256 amount,address recipient,uint256 expiresAt,bytes32 executorSalt,bytes executorSignature,bytes32[] adminSalt,bytes[] adminSignature,bool directTransfer)"
];

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

// 1) Read admin nonce from the user's collateral contract
const nonce = await new Contract(collateralProxy, ["function adminNonce() view returns (uint256)"], signer).adminNonce();

// 2) Create admin EIP‑712 signature (domain name "Collateral", version "2")
const adminSaltBytes = randomBytes(32);
const domain = { name: "Collateral", version: "2", chainId, verifyingContract: collateralProxy, salt: ethers.hexlify(adminSaltBytes) };
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 adminSigRaw = await signer.signTypedData(domain, types, message);
const adminSig = Signature.from(adminSigRaw).serialized;

// 3) Execute on the Coordinator (v2)
const coord = new Contract(coordinatorAddress, COORDINATOR_ABI, signer);
const tx = await coord.withdrawAsset(collateralProxy, token, amount, recipient, expiresAt, executorSalt, executorSignature, [adminSaltBytes], [adminSig], true);
await tx.wait();
Coordinator address comes from the user’s collateral contract. If you don’t persist this, contact us and we’ll provide the addresses for your integration.
3

3) Confirm and handle errors

  • Wait for the transaction receipt and surface the hash to your user.
  • If the signature expires (expiresAt), request a new payload.
  • If you receive 403 when requesting a signature, ensure the adminAddress is linked to the user.