> ## Documentation Index
> Fetch the complete documentation index at: https://turnkey-0e7c1f5b-eric-txl-313-earn-beta-docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Withdraw from a vault

> Withdraw any amount, just the yield, or the entire position from an enabled yield vault.

A withdrawal moves assets from your organization's fee wrapper back to the user's wallet. Amounts are specified in the underlying asset (vault shares are not exposed in the API), or pass `"MAX"` to exit the position entirely.

<Note>
  Earn is in private beta. [Contact us](https://www.turnkey.com/contact-us)
  to enable it for your organization.
</Note>

## Submit the withdrawal

<ParamField body="type" type="enum<string>" required>
  `ACTIVITY_TYPE_EARN_WITHDRAW`
</ParamField>

<ParamField body="timestampMs" type="string" required>
  Timestamp (in milliseconds) of the request, used to verify liveness.
</ParamField>

<ParamField body="organizationId" type="string" required>
  Unique identifier for your organization (or the sub-organization whose wallet is withdrawing).
</ParamField>

<ParamField body="parameters.wrapperAddress" type="string" required>
  Address of the deployed fee wrapper holding the position, from [`earn_positions`](/features/transaction-management/earn/positions).
</ParamField>

<ParamField body="parameters.signWith" type="string" required>
  The wallet account address to withdraw to and sign with.
</ParamField>

<ParamField body="parameters.amountValue" type="string" required>
  Amount of the underlying asset to withdraw, in raw on-chain units (e.g. `"500000"` for 0.50 USDC), or the literal `"MAX"` to withdraw the entire position.
</ParamField>

<ParamField body="parameters.chainCaip2" type="string" required>
  CAIP-2 chain identifier, e.g. `eip155:8453`.
</ParamField>

<ParamField body="parameters.sponsor" type="boolean">
  Whether to sponsor the transaction's gas via Gas Station. Defaults to `false`.
</ParamField>

<CodeGroup>
  ```bash title="cURL" theme={"system"}
  curl --request POST \
    --url https://api.turnkey.com/public/v1/submit/earn_withdraw \
    --header 'Accept: application/json' \
    --header 'Content-Type: application/json' \
    --header "X-Stamp: <string> (see Stamps)" \
    --data '{
      "type": "ACTIVITY_TYPE_EARN_WITHDRAW",
      "timestampMs": "<string> (e.g. 1745474677453)",
      "organizationId": "<ORGANIZATION_ID>",
      "parameters": {
        "wrapperAddress": "<WRAPPER_ADDRESS>",
        "signWith": "<WALLET_ADDRESS>",
        "amountValue": "MAX",
        "chainCaip2": "eip155:8453",
        "sponsor": false
      }
    }'
  ```

  ```javascript title="JavaScript" theme={"system"}
  import { TurnkeyClient } from "@turnkey/http";
  import { ApiKeyStamper } from "@turnkey/api-key-stamper";

  const client = new TurnkeyClient(
    { baseUrl: "https://api.turnkey.com" },
    new ApiKeyStamper({
      apiPublicKey: process.env.TURNKEY_API_PUBLIC_KEY,
      apiPrivateKey: process.env.TURNKEY_API_PRIVATE_KEY,
    }),
  );

  const { activity } = await client.request("/public/v1/submit/earn_withdraw", {
    type: "ACTIVITY_TYPE_EARN_WITHDRAW",
    timestampMs: String(Date.now()),
    organizationId: "<ORGANIZATION_ID>",
    parameters: {
      wrapperAddress: "<WRAPPER_ADDRESS>",
      signWith: "<WALLET_ADDRESS>",
      amountValue: "MAX",
      chainCaip2: "eip155:8453",
      sponsor: false,
    },
  });
  ```
</CodeGroup>

The result contains only a poll handle:

```json theme={"system"}
{
  "activity": {
    "id": "<ACTIVITY_ID>",
    "status": "ACTIVITY_STATUS_COMPLETED",
    "type": "ACTIVITY_TYPE_EARN_WITHDRAW",
    "result": {
      "earnWithdrawResult": {
        "withdrawRequestId": "<WITHDRAW_REQUEST_ID>"
      }
    }
  }
}
```

## Full exit with MAX

`"amountValue": "MAX"` redeems the wallet's exact live share balance in the wrapper, so the position closes completely without leaving dust.

<Note>
  A `MAX` withdrawal resets the position's lifetime accounting: after it
  confirms, `totalDeposited` and `totalWithdrawn` in
  [`earn_positions`](/features/transaction-management/earn/positions) start again from zero
  for that wrapper.
</Note>

Positions in wrappers you have since replaced (after a [fee change](/features/transaction-management/earn/deploy-wrapper#choose-your-fee-configuration)) remain withdrawable. Target the old wrapper's address.

## Claiming yield only

To pay out yield without touching principal, withdraw exactly the yield amount. Compute it from the position's raw fields:

```javascript title="JavaScript" theme={"system"}
const { positions } = await client.request("/public/v1/query/earn_positions", {
  organizationId: "<ORGANIZATION_ID>",
  walletAddress: "<WALLET_ADDRESS>",
});

const p = positions.find(
  (p) => p.wrapperAddress === "<WRAPPER_ADDRESS>",
);

// These are raw on-chain unit strings, so use BigInt rather than floats.
const yieldEarned =
  BigInt(p.currentValue) - BigInt(p.totalDeposited) + BigInt(p.totalWithdrawn);

// Withdraw the yield, leave the principal earning.
await client.request("/public/v1/submit/earn_withdraw", {
  type: "ACTIVITY_TYPE_EARN_WITHDRAW",
  timestampMs: String(Date.now()),
  organizationId: "<ORGANIZATION_ID>",
  parameters: {
    wrapperAddress: "<WRAPPER_ADDRESS>",
    signWith: "<WALLET_ADDRESS>",
    amountValue: yieldEarned.toString(),
    chainCaip2: "eip155:8453",
    sponsor: false,
  },
});
```

## Poll withdrawal status (required)

<Warning>
  As with deposits, a `COMPLETED` activity means the transaction was enqueued
  for broadcast, not that it confirmed. Poll `earn_withdraw_status` until it
  reports `COMPLETED` (included on-chain) or `FAILED`.
</Warning>

<CodeGroup>
  ```bash title="cURL" theme={"system"}
  curl --request POST \
    --url https://api.turnkey.com/public/v1/query/earn_withdraw_status \
    --header 'Accept: application/json' \
    --header 'Content-Type: application/json' \
    --header "X-Stamp: <string> (see Stamps)" \
    --data '{
      "organizationId": "<ORGANIZATION_ID>",
      "withdrawRequestId": "<WITHDRAW_REQUEST_ID>"
    }'
  ```

  ```javascript title="JavaScript" theme={"system"}
  const status = await client.request("/public/v1/query/earn_withdraw_status", {
    organizationId: "<ORGANIZATION_ID>",
    withdrawRequestId: "<WITHDRAW_REQUEST_ID>",
  });
  ```
</CodeGroup>

```json theme={"system"}
{
  "status": "COMPLETED",
  "withdrawTxHash": "<TRANSACTION_HASH>"
}
```

`status` is `PENDING`, `COMPLETED`, or `FAILED`. On `FAILED`, the response includes an `error` field with the reason.

## Gas

Identical to deposits: `sponsor: true` uses Gas Station (Pro plan or higher); otherwise the `signWith` wallet pays gas natively. See [Gas: sponsored vs self-funded](/features/transaction-management/earn/deposit#gas-sponsored-vs-self-funded).

## Next steps

* [Track positions](/features/transaction-management/earn/positions) to verify the position after withdrawing
