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

# Cancellations

> Complete cancellation walkthrough using the Paxos Labs REST API calldata endpoints

Cancel a pending withdrawal order to recover locked vault shares. Only the original order owner (verified on-chain via `msg.sender`) can cancel.

<Warning>
  Only orders with status `PENDING` can be cancelled. Orders that have already
  been processed (`COMPLETE`) or are mid-refund (`PENDING_REFUND`, `REFUNDED`)
  cannot be cancelled.
</Warning>

## Prerequisites

* A Paxos Labs API key
* An EVM-compatible signer (the same address that submitted the original withdrawal order)
* The `orderIndex` of the pending withdrawal order

## Step 1: Find the Order Index

Query `GET /v2/amplify/withdrawalRequests` to find the `orderIndex` of the order you want to cancel:

```bash theme={null}
curl "https://api.paxoslabs.com/v2/amplify/withdrawalRequests?\
filter=userAddress%3D0x1234567890abcdef1234567890abcdef12345678%20AND%20\
vaultAddress%3D0xbbbb000000000000000000000000000000000001%20AND%20\
status%3DPENDING" \
  -H "x-api-key: pxl_your_key"
```

Each item in the response includes an `orderIndex` field:

```json theme={null}
{
  "withdrawalRequests": [
    {
      "id": "wr_abc123",
      "orderIndex": "42",
      "status": "PENDING",
      "vaultAddress": "0xbbbb...",
      "chainId": 1,
      "userAddress": "0x1234...",
      "orderAmount": "1000000000000000000",
      "wantAssetAddress": "0xA0b8..."
    }
  ],
  "nextPageToken": null
}
```

## Step 2: Prepare Cancel Calldata

`GET /v2/amplify/withdraw/cancel` returns the transaction to cancel a pending withdrawal order.

### Parameters

| Parameter        | Type     | Required | Description                                                                                 |
| ---------------- | -------- | -------- | ------------------------------------------------------------------------------------------- |
| `vaultAddress`   | `string` | Yes      | BoringVault contract address (`0x` + 40 hex chars)                                          |
| `orderIndex`     | `string` | Yes      | Index of the withdrawal order to cancel (decimal string from `withdrawalRequests` response) |
| `chainId`        | `number` | Yes      | EVM chain ID                                                                                |
| `responseFormat` | `string` | No       | `encoded` (default), `full`, or `structured`                                                |

### Response

```json theme={null}
{
  "transaction": {
    "to": "0xdddd000000000000000000000000000000000001",
    "data": "0x5c975abb...",
    "value": "0",
    "abi": [{"type": "function", "name": "cancelOrder", "inputs": [...]}],
    "functionName": "cancelOrder",
    "args": ["42"]
  }
}
```

## Step 3: Sign and Submit

Broadcast the cancellation transaction. The transaction must be sent from the same address that originally submitted the withdrawal order — ownership is enforced on-chain via `msg.sender`.

After confirmation, the locked vault shares are returned to the user's wallet.

## Complete Examples

<Tabs>
  <Tab title="Node.js">
    ```ts theme={null}
    import { createWalletClient, createPublicClient, http } from "viem";
    import { mainnet } from "viem/chains";
    import { privateKeyToAccount } from "viem/accounts";

    const API_KEY = process.env.AMPLIFY_API_KEY!;
    const PRIVATE_KEY = process.env.PRIVATE_KEY as `0x${string}`;
    const BASE = "https://api.paxoslabs.com";
    const HEADERS = { "x-api-key": API_KEY };

    const VAULT_ADDRESS = "0xbbbb000000000000000000000000000000000001";
    const CHAIN_ID = 1;

    async function main() {
      const account = privateKeyToAccount(PRIVATE_KEY);
      const walletClient = createWalletClient({
        account,
        chain: mainnet,
        transport: http(),
      });
      const publicClient = createPublicClient({
        chain: mainnet,
        transport: http(),
      });

      // Step 1: Find pending withdrawal orders
      console.log("Fetching pending withdrawal orders...");
      const statusUrl = new URL(`${BASE}/v2/amplify/withdrawalRequests`);
      statusUrl.searchParams.set(
        "filter",
        `userAddress=${account.address} AND vaultAddress=${VAULT_ADDRESS} AND status=PENDING`
      );
      const statusResp = await fetch(statusUrl, { headers: HEADERS }).then(
        (r) => r.json()
      );
      const orders = statusResp.withdrawalRequests;
      if (orders.length === 0) {
        console.log("No pending orders to cancel.");
        return;
      }
      const orderIndex = orders[0].orderIndex;
      console.log(`Cancelling order index: ${orderIndex}`);

      // Step 2: Get cancel calldata
      const cancelUrl = new URL(`${BASE}/v2/amplify/withdraw/cancel`);
      cancelUrl.searchParams.set("vaultAddress", VAULT_ADDRESS);
      cancelUrl.searchParams.set("orderIndex", orderIndex);
      cancelUrl.searchParams.set("chainId", String(CHAIN_ID));

      const cancelResp = await fetch(cancelUrl, { headers: HEADERS }).then(
        (r) => r.json()
      );
      const tx = cancelResp.transaction;

      // Step 3: Sign and submit
      console.log("Submitting cancellation...");
      const hash = await walletClient.sendTransaction({
        to: tx.to as `0x${string}`,
        data: tx.data as `0x${string}`,
        value: BigInt(tx.value),
        chain: mainnet,
        account,
      });
      const receipt = await publicClient.waitForTransactionReceipt({ hash });
      console.log(
        `Cancellation confirmed in block ${receipt.blockNumber}: ${hash}`
      );
    }

    main().catch(console.error);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    import requests
    from web3 import Web3

    API_KEY = os.environ["AMPLIFY_API_KEY"]
    PRIVATE_KEY = os.environ["PRIVATE_KEY"]
    BASE = "https://api.paxoslabs.com"
    HEADERS = {"x-api-key": API_KEY}

    VAULT_ADDRESS = "0xbbbb000000000000000000000000000000000001"
    CHAIN_ID = 1

    w3 = Web3(Web3.HTTPProvider("https://eth.llamarpc.com"))
    account = w3.eth.account.from_key(PRIVATE_KEY)

    # Step 1: Find pending withdrawal orders
    print("Fetching pending withdrawal orders...")
    status_resp = requests.get(f"{BASE}/v2/amplify/withdrawalRequests", headers=HEADERS, params={
        "filter": f"userAddress={account.address} AND vaultAddress={VAULT_ADDRESS} AND status=PENDING",
    }).json()

    orders = status_resp["withdrawalRequests"]
    if not orders:
        print("No pending orders to cancel.")
        exit()

    order_index = orders[0]["orderIndex"]
    print(f"Cancelling order index: {order_index}")

    # Step 2: Get cancel calldata
    cancel_resp = requests.get(f"{BASE}/v2/amplify/withdraw/cancel", headers=HEADERS, params={
        "vaultAddress": VAULT_ADDRESS,
        "orderIndex": order_index,
        "chainId": CHAIN_ID,
    }).json()
    tx = cancel_resp["transaction"]

    # Step 3: Sign and submit
    print("Submitting cancellation...")
    cancel_tx = {
        "to": Web3.to_checksum_address(tx["to"]),
        "data": tx["data"],
        "value": int(tx["value"]),
        "gas": 200_000,
        "nonce": w3.eth.get_transaction_count(account.address),
        "chainId": CHAIN_ID,
    }
    signed = account.sign_transaction(cancel_tx)
    tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    print(f"Cancellation confirmed in block {receipt['blockNumber']}: {tx_hash.hex()}")
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    package main

    import (
        "context"
        "encoding/json"
        "fmt"
        "io"
        "math/big"
        "net/http"
        "net/url"
        "os"

        "github.com/ethereum/go-ethereum/common"
        "github.com/ethereum/go-ethereum/core/types"
        "github.com/ethereum/go-ethereum/crypto"
        "github.com/ethereum/go-ethereum/ethclient"
    )

    const (
        baseURL      = "https://api.paxoslabs.com"
        vaultAddress = "0xbbbb000000000000000000000000000000000001"
        chainID      = 1
    )

    func apiGet(path string, params url.Values) (map[string]interface{}, error) {
        u := fmt.Sprintf("%s%s?%s", baseURL, path, params.Encode())
        req, _ := http.NewRequest("GET", u, nil)
        req.Header.Set("x-api-key", os.Getenv("AMPLIFY_API_KEY"))
        resp, err := http.DefaultClient.Do(req)
        if err != nil { return nil, err }
        defer resp.Body.Close()
        body, _ := io.ReadAll(resp.Body)
        var result map[string]interface{}
        json.Unmarshal(body, &result)
        return result, nil
    }

    func main() {
        pk, _ := crypto.HexToECDSA(os.Getenv("PRIVATE_KEY"))
        fromAddr := crypto.PubkeyToAddress(pk.PublicKey)
        client, _ := ethclient.Dial("https://eth.llamarpc.com")
        ctx := context.Background()
        signer := types.NewEIP155Signer(big.NewInt(chainID))

        // Step 1: Find pending withdrawal orders
        fmt.Println("Fetching pending withdrawal orders...")
        statusResp, _ := apiGet("/v2/amplify/withdrawalRequests", url.Values{
            "filter": {fmt.Sprintf("userAddress=%s AND vaultAddress=%s AND status=PENDING",
                fromAddr.Hex(), vaultAddress)},
        })
        orders := statusResp["withdrawalRequests"].([]interface{})
        if len(orders) == 0 {
            fmt.Println("No pending orders to cancel.")
            return
        }
        order := orders[0].(map[string]interface{})
        orderIndex := order["orderIndex"].(string)
        fmt.Printf("Cancelling order index: %s\n", orderIndex)

        // Step 2: Get cancel calldata
        fmt.Println("Fetching cancel calldata...")
        cancelResp, _ := apiGet("/v2/amplify/withdraw/cancel", url.Values{
            "vaultAddress": {vaultAddress},
            "orderIndex":   {orderIndex},
            "chainId":      {fmt.Sprint(chainID)},
        })
        txData := cancelResp["transaction"].(map[string]interface{})

        // Step 3: Sign and submit
        fmt.Println("Submitting cancellation...")
        nonce, _ := client.PendingNonceAt(ctx, fromAddr)
        gasPrice, _ := client.SuggestGasPrice(ctx)
        cancelTx := types.NewTransaction(nonce,
            common.HexToAddress(txData["to"].(string)),
            big.NewInt(0), 200000, gasPrice,
            common.FromHex(txData["data"].(string)))
        signedCancel, _ := types.SignTx(cancelTx, signer, pk)
        client.SendTransaction(ctx, signedCancel)
        fmt.Printf("Cancellation submitted: %s\n", signedCancel.Hash().Hex())
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    import org.web3j.crypto.Credentials;
    import org.web3j.crypto.RawTransaction;
    import org.web3j.crypto.TransactionEncoder;
    import org.web3j.protocol.Web3j;
    import org.web3j.protocol.http.HttpService;
    import org.web3j.utils.Numeric;

    import java.math.BigInteger;
    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import com.google.gson.JsonArray;
    import com.google.gson.JsonObject;
    import com.google.gson.JsonParser;

    public class AmplifyCancelWithdraw {

        static final String BASE = "https://api.paxoslabs.com";
        static final String API_KEY = System.getenv("AMPLIFY_API_KEY");
        static final String VAULT_ADDRESS = "0xbbbb000000000000000000000000000000000001";
        static final int CHAIN_ID = 1;

        static HttpClient httpClient = HttpClient.newHttpClient();

        static JsonObject apiGet(String path) throws Exception {
            HttpRequest req = HttpRequest.newBuilder()
                .uri(URI.create(BASE + path))
                .header("x-api-key", API_KEY)
                .GET().build();
            HttpResponse<String> resp = httpClient.send(req,
                HttpResponse.BodyHandlers.ofString());
            return JsonParser.parseString(resp.body()).getAsJsonObject();
        }

        public static void main(String[] args) throws Exception {
            Credentials credentials = Credentials.create(System.getenv("PRIVATE_KEY"));
            String userAddress = credentials.getAddress();
            Web3j web3 = Web3j.build(new HttpService("https://eth.llamarpc.com"));

            // Step 1: Find pending withdrawal orders
            System.out.println("Fetching pending withdrawal orders...");
            String filter = String.format(
                "userAddress=%s AND vaultAddress=%s AND status=PENDING",
                userAddress, VAULT_ADDRESS);
            String statusPath = "/v2/amplify/withdrawalRequests?filter=" +
                java.net.URLEncoder.encode(filter, "UTF-8");
            JsonObject statusResp = apiGet(statusPath);
            JsonArray orders = statusResp.getAsJsonArray("withdrawalRequests");

            if (orders.size() == 0) {
                System.out.println("No pending orders to cancel.");
                return;
            }

            String orderIndex = orders.get(0).getAsJsonObject()
                .get("orderIndex").getAsString();
            System.out.println("Cancelling order index: " + orderIndex);

            // Step 2: Get cancel calldata
            System.out.println("Fetching cancel calldata...");
            String cancelPath = String.format(
                "/v2/amplify/withdraw/cancel?vaultAddress=%s&orderIndex=%s&chainId=%d",
                VAULT_ADDRESS, orderIndex, CHAIN_ID);
            JsonObject cancelResp = apiGet(cancelPath);
            JsonObject tx = cancelResp.getAsJsonObject("transaction");

            // Step 3: Sign and submit
            System.out.println("Submitting cancellation...");
            BigInteger nonce = web3.ethGetTransactionCount(userAddress,
                org.web3j.protocol.core.DefaultBlockParameterName.PENDING)
                .send().getTransactionCount();
            BigInteger gasPrice = web3.ethGasPrice().send().getGasPrice();

            RawTransaction cancelTx = RawTransaction.createTransaction(
                nonce, gasPrice, BigInteger.valueOf(200000),
                tx.get("to").getAsString(), BigInteger.ZERO,
                tx.get("data").getAsString());
            byte[] signedCancel = TransactionEncoder.signMessage(
                cancelTx, CHAIN_ID, credentials);
            String txHash = web3.ethSendRawTransaction(
                Numeric.toHexString(signedCancel)).send().getTransactionHash();
            System.out.println("Cancellation submitted: " + txHash);
        }
    }
    ```
  </Tab>
</Tabs>

## Error Responses

| Status | Meaning                                                      |
| ------ | ------------------------------------------------------------ |
| 400    | Invalid parameters (bad address, missing `orderIndex`, etc.) |
| 404    | No account found for the given `vaultAddress` + `chainId`    |
