Skip to main content

HTTP Status Codes

CodeMeaningAction
200SuccessParse response normally
400Bad request - invalid body or parametersCheck request schema
401Unauthorized - invalid or missing API key, or signature verification failedCheck x-api-key header and signing format
500Internal server errorRetry with backoff; check /health
If you’re seeing an unexpected error, join our Discord and the team will help you debug it.

Commit Status

The /commit endpoint returns a status field rather than an HTTP error for action-level outcomes. Always check this field after committing.
StatusDescriptionAction
completedAction processed successfully - balances have been updatedContinue normally
rejectedDeposit failed compliance or risk screening - funds will be refunded to the originating wallet after extended screening is completedNotify the user
pendingWaiting for the transaction to land on-chain or undergo compliance screeningCheck back shortly
failedThe transaction was rejected on-chain - the user’s balance is no longer held in a pending stateInform the user; check the on-chain transaction
expiredThe transaction was not confirmed within the required window - the user’s balance is no longer held in a pending stateInform the user
A rejected, failed, or expired status still means the commit succeeded. The balance is no longer held in a pending state. No further action is required.

Idempotency

/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.
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);
}

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.
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');

Retry Strategy

For 500 errors or network timeouts, use exponential backoff:
async function vanishWithRetry(
  path: string,
  options?: RequestInit,
  maxAttempts = 3
) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await vanish(path, options);
    } catch (err: any) {
      if (attempt === maxAttempts) throw err;

      // Don't retry on client errors (4xx)
      if (err.message.includes('400') || err.message.includes('401')) throw err;

      const delay = Math.pow(2, attempt) * 500; // 1s, 2s, 4s
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

Health Check

Use GET /health to verify API availability before critical flows or for uptime monitoring:
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"
}
Integrate this into your monitoring stack to get alerts on API downtime. The health endpoint does not require a signed request.