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

# Error Handling

> HTTP status codes, commit statuses, signature errors, and health monitoring for the Vanish Core API.

This page covers everything you need to handle errors gracefully: HTTP codes, commit statuses, signature debugging, and health monitoring.

***

## HTTP Status Codes

| Code  | Meaning                                                                    | Action                                              |
| ----- | -------------------------------------------------------------------------- | --------------------------------------------------- |
| `200` | Success                                                                    | Parse response normally                             |
| `400` | Bad request: invalid body or parameters                                    | Check request schema                                |
| `401` | Unauthorized: invalid or missing API key, or signature verification failed | Check `x-api-key` header and signing format         |
| `500` | Internal server error                                                      | Retry; check [`GET /health`](/api-reference/health) |

<Info>
  If you're seeing an unexpected error, join our [Discord](https://discord.gg/vanishtrade) and the team will help you debug it.
</Info>

***

## Commit Status

The [`/commit`](/api-reference/commit) endpoint returns a `status` field rather than an HTTP error for action-level outcomes. Always check this field after committing.

| Status      | Description                                                                                                                         | Action                                                                      |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `completed` | Action processed successfully: balances have been updated                                                                           | Continue normally                                                           |
| `rejected`  | Deposit failed compliance or risk screening: funds will be refunded to the originating wallet after extended screening is completed | Notify the user; do not re-attempt                                          |
| `pending`   | Waiting for the transaction to land on-chain or undergo compliance screening                                                        | Poll [`/commit`](/api-reference/commit) again shortly with the same `tx_id` |
| `failed`    | The transaction was rejected on-chain: the user's balance is no longer in a pending state                                           | Inform the user; check the on-chain transaction                             |
| `expired`   | The transaction was not confirmed within the required window: the user's balance is no longer in a pending state                    | Inform the user                                                             |

<Info>
  A `rejected`, `failed`, or `expired` status still means the commit succeeded. The balance is no longer in a pending state. No further action is required.
</Info>

***

## Idempotency

[`/commit`](/api-reference/commit) is idempotent. If you call it twice with the same `tx_id`, the second call returns `already_processed: true` and the same status as the first call. This is safe - use it to confirm commit state after a crash or retry.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const commit = await vanish('/commit', {
    method: 'POST',
    body: JSON.stringify({ tx_id }),
  });

  if (commit.already_processed) {
    // Already committed - use commit.status to determine what happened
    console.log('Previously resolved:', commit.status);
  } else {
    console.log('Freshly committed:', commit.status, commit.balance_changes);
  }
  ```

  ```rust Rust theme={null}
  let commit: CommitResponse = client
      .post(format!("{}/commit", VANISH_URL))
      .header("x-api-key", &api_key)
      .json(&json!({ "tx_id": tx_id }))
      .send()
      .await?
      .json()
      .await?;

  if commit.already_processed {
      println!("Previously resolved: {}", commit.status);
  } else {
      println!("Freshly committed: {} {:?}", commit.status, commit.balance_changes);
  }
  ```
</CodeGroup>

***

## Recovering Pending Commits

If a transaction is submitted but [`/commit`](/api-reference/commit) is never called - due to a crash, timeout, or network issue - the user's balance remains in a pending state.

Use [`POST /account/pending`](/api-reference/account/get_pending_actions) on startup to detect and recover these states:

<CodeGroup>
  ```typescript TypeScript theme={null}
  async function recoverPendingCommits(userKeypair: Keypair) {
    const timestamp = Date.now().toString();
    const signature = readSignature(timestamp, userKeypair);

    const pending = await vanish('/account/pending', {
      method: 'POST',
      body: JSON.stringify({
        user_address: userKeypair.publicKey.toBase58(),
        timestamp,
        signature,
      }),
    });

    for (const action of pending) {
      const commit = await vanish('/commit', {
        method: 'POST',
        body: JSON.stringify({ tx_id: action.tx_id }),
      });
      // already_processed: true means it was already committed - safe to skip
      if (!commit.already_processed) {
        console.log(`Resolved ${action.action_type}:`, commit.status);
      }
    }
  }
  ```

  ```rust Rust theme={null}
  async fn recover_pending_commits(
      client: &Client,
      api_key: &str,
      user_keypair: &Keypair,
  ) -> Result<(), reqwest::Error> {
      let timestamp = chrono::Utc::now().timestamp_millis().to_string();
      let signature = read_signature(&timestamp, user_keypair);

      let body = serde_json::json!({
          "user_address": user_keypair.pubkey().to_string(),
          "timestamp":    &timestamp,
          "signature":    signature,
      });

      let pending = vanish(client, api_key, "/account/pending", Some(&body)).await?;

      for action in pending.as_array().unwrap_or(&vec![]) {
          let commit_body = serde_json::json!({ "tx_id": action["tx_id"] });
          let commit = vanish(client, api_key, "/commit", Some(&commit_body)).await?;

          if !commit["already_processed"].as_bool().unwrap_or(false) {
              println!("Resolved {}: {}", action["action_type"], commit["status"]);
          }
      }

      Ok(())
  }
  ```
</CodeGroup>

***

## Handling 401 - Signature Errors

The most common cause of a `401` on signed endpoints is a malformed signature message. Check:

1. **Message format** - the ToS header line and `Details:` line must match exactly, including the newlines.
2. **Timestamp** - must be Unix time in **milliseconds**, not seconds. Stale timestamps are rejected.
3. **Signing key** - must be the Ed25519 secret key of `user_address`, not any other key.
4. **Encoding** - the signature must be base64-encoded, not base58 or hex.

<Warning>
  The most common mistake is passing seconds instead of milliseconds for the timestamp. Use `Date.now()` in TypeScript or `timestamp_millis()` in Rust - not `Date.now() / 1000` or `timestamp()`.
</Warning>

<CodeGroup>
  ```typescript TypeScript theme={null}
  import * as nacl from 'tweetnacl';
  import { Keypair } from '@solana/web3.js';

  // Correct signing pattern
  const timestamp = Date.now().toString(); // milliseconds

  const message = [
    "By signing, I hereby agree to Vanish's Terms of Service and agree to be bound by them (docs.vanish.trade/legal/TOS)",
    "",
    `Details: read:${timestamp}`,
  ].join('\n');

  const sig       = nacl.sign.detached(new TextEncoder().encode(message), keypair.secretKey);
  const signature = Buffer.from(sig).toString('base64');
  ```

  ```rust Rust theme={null}
  use solana_sdk::signature::{Keypair, Signer};
  use base64::{engine::general_purpose, Engine as _};

  // Correct signing pattern
  let timestamp = chrono::Utc::now().timestamp_millis().to_string(); // milliseconds

  let message = format!(
      "By signing, I hereby agree to Vanish's Terms of Service and agree to be bound by them (docs.vanish.trade/legal/TOS)\n\nDetails: read:{timestamp}"
  );

  let sig       = keypair.sign_message(message.as_bytes());
  let signature = general_purpose::STANDARD.encode(sig.as_ref());
  ```
</CodeGroup>

***

## Health Check

Use [`GET /health`](/api-reference/health) to verify API availability or plug it into your monitoring stack for uptime alerts. It requires no signed request - just your API key.

<CodeGroup>
  ```typescript TypeScript theme={null}
  async function checkHealth(): Promise<boolean> {
    const res = await fetch('https://core-api.vanish.trade/health', {
      headers: { 'x-api-key': process.env.VANISH_API_KEY! },
    });
    return res.ok; // true if 200 "healthy"
  }
  ```

  ```rust Rust theme={null}
  async fn check_health(client: &Client, api_key: &str) -> bool {
      client
          .get(format!("{}/health", VANISH_URL))
          .header("x-api-key", api_key)
          .send()
          .await
          .map(|r| r.status().is_success())
          .unwrap_or(false)
  }
  ```
</CodeGroup>

***

<Info>
  Experiencing an issue not covered here? Join us on [Discord](https://discord.gg/vanishtrade) - our engineers are available to help.
</Info>
