NAV
API Documentation V3.0.1

Introduction

Welcome to the Cape Crypto API Documentation. You can use our full API suite to integrate with us.

Create and cancel orders, view your account balance, stream orderbook and account updates over websocket, and perform all other actions you can perform from the apps.

🚀 Looking for trading features that we haven't added yet? We'll add them for you! Reach out to us

Hummingbot icon Looking for a Hummingbot connector for Cape Crypto? We have one! Reach out to us

Getting started

The API suite consists of public and private endpoints. Private endpoints require your requests to be signed for authentication. In order to sign your requests, you will need to create and API key and secret, and have 2FA enabled:

  1. Sign into your account Cape Crypto
  2. Head to the Profile page by clicking the profile icon at the top right
  3. Enable 2FA
  4. Create a new API key
  5. Save your new API key and Secret and do not share it with anyone

Use your API key and secret to sign your requests for all private endpoints. (Public endpoints do not require authentication)

All requests unless otherwise noted (EG for sub-account / customer management) use the following base URLs:

For Sub-account and customer management (noted in the routes below):

Authentication

Authentication:

// generateHeaders.js

const crypto = require("crypto");
const apiKey = "123456...";
const secret = "ABCDEF...";

module.exports = async (subAccountUid = {}) => {
  const timeStamp = new Date().getTime();
  const queryString = `${timeStamp}${apikey}`;
  const signature = crypto
    .createHmac("sha256", secret)
    .update(queryString)
    .digest("hex");

  return {
    "Content-Type": "application/json;charset=utf-8",
    "X-Auth-Apikey": apikey,
    "X-Auth-Nonce": timeStamp,
    "X-Auth-Signature": signature,
    "X-Auth-Signature": signature,
    // Only for sub-account / customer requests:
    "X-Auth-Subaccount-UID": subAccountUid,
  };
};

To securely access the private endpoints, each request requires authentication headers.

Generate a new signature for each new request, and add these headers to the request.

  1. X-Auth-Nonce: Current timestamp in milliseconds UTC time.
  2. X-Auth-Apikey: Your API key
  3. X-Auth-Signature: HMAC-SHA256 encrypted string of the combined timestamp, apikey and secret (see JS example)

Acting on Behalf of Sub-Accounts or Customers

When making requests on behalf of a sub-account or customer, include an additional optional header:

  1. X-Auth-Subaccount-Uid: The UID of the sub-account or customer you want to act as

This allows the parent account (or merchant/aggregator) to make authenticated requests as if they were the sub-account or customer. The regular authentication headers (API key, nonce, signature) must still be included.

Example using Authentication

POST Create order

// generateHeaders.js

const crypto = require("crypto");
const apiKey = "123456...";
const secret = "ABCDEF...";

module.exports = async () => {
  const timeStamp = new Date().getTime();
  const queryString = `${timeStamp}${apikey}`;
  const signature = crypto
    .createHmac("sha256", secret)
    .update(queryString)
    .digest("hex");

  return {
    "Content-Type": "application/json;charset=utf-8",
    "X-Auth-Apikey": apikey,
    "X-Auth-Nonce": timeStamp,
    "X-Auth-Signature": signature,
  };
};

Send the authenticated request:

// createOrder.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

createOrder = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "POST";
  const path = `/api/v2/atlas/market/orders`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const orderData = {
    market: "btczar",
    side: "sell",
    volume: "0.00001",
    ord_type: "limit",
    price: "800000",
    post_only: true,
    custom_order_id: "302b0494-1a06-4089-846c-0c6b13f1b661",
  };

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
    data: orderData,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

Create order response:

{
  "id": 173757,
  "uuid": "7e86a4fa-fcae-4953-b9bc-d6f328beea48",
  "side": "sell",
  "ord_type": "limit",
  "price": "800000.0",
  "avg_price": "0.0",
  "state": "pending",
  "market": "btczar",
  "created_at": "2023-12-08T08:46:59+01:00",
  "updated_at": "2023-12-08T08:46:59+01:00",
  "origin_volume": "0.00001",
  "remaining_volume": "0.00001",
  "executed_volume": "0.0",
  "trades_count": 0,
  "custom_order_id": "302b0494-1a06-4089-846c-0c6b13f1b661"
}

https://trade.capecrypto.com/api/v2/atlas/market/orders

Create a new order on your account by signing your request and submitting the request

POST Create order as sub-account

// generateHeadersWithSubAccount.js

const crypto = require("crypto");
const apiKey = "123456...";
const secret = "ABCDEF...";
const subAccountUid = "CCT1234567"; // Sub-account or customer UID

module.exports = async () => {
  const timeStamp = new Date().getTime();
  const queryString = `${timeStamp}${apiKey}`;
  const signature = crypto
    .createHmac("sha256", secret)
    .update(queryString)
    .digest("hex");

  return {
    "Content-Type": "application/json;charset=utf-8",
    "X-Auth-Apikey": apiKey,
    "X-Auth-Nonce": timeStamp,
    "X-Auth-Signature": signature,
    "X-Auth-Subaccount-Uid": subAccountUid, // Act on behalf of sub-account/customer
  };
};

Send the authenticated request as sub-account:

// createOrderAsSubAccount.js
const axios = require("axios");
const generateHeaders = require("./generateHeadersWithSubAccount");

createOrder = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "POST";
  const path = `/api/v2/atlas/market/orders`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const orderData = {
    market: "btczar",
    side: "buy",
    volume: "0.00001",
    ord_type: "limit",
    price: "750000",
  };

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
    data: orderData,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

https://trade.capecrypto.com/api/v2/atlas/market/orders

When the X-Auth-Subaccount-Uid header is included, the order is created on behalf of the specified sub-account or customer. The parent account's API credentials are used for authentication, but the operation is performed in the context of the sub-account.

Public request example

GET Get available markets

const axios = require("axios");

getMarkets = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "GET";
  const path = `/api/v2/atlas/public/markets`;
  const url = `${baseUrl}${path}`;

  const axiosConfig = {
    method: method,
    url: url,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

getMarkets();

Markets response:

[
  {
    "id": "btczar",
    "name": "BTC/ZAR",
    "base_unit": "btc",
    "quote_unit": "zar",
    "min_price": "200000.0",
    "max_price": "1200000.0",
    "min_amount": "0.00001",
    "amount_precision": 8,
    "price_precision": 2,
    "state": "enabled"
  },
  {
    "id": "ethzar",
    "name": "ETH/ZAR",
    "base_unit": "eth",
    "quote_unit": "zar",
    "min_price": "10000.0",
    "max_price": "100000.0",
    "min_amount": "0.0001",
    "amount_precision": 8,
    "price_precision": 2,
    "state": "enabled"
  },
  {
    "id": "xrpzar",
    "name": "XRP/ZAR",
    "base_unit": "xrp",
    "quote_unit": "zar",
    "min_price": "1.0",
    "max_price": "100.0",
    "min_amount": "1.0",
    "amount_precision": 6,
    "price_precision": 2,
    "state": "enabled"
  },
  {
    "id": "usdczar",
    "name": "USDC/ZAR",
    "base_unit": "usdc",
    "quote_unit": "zar",
    "min_price": "14.0",
    "max_price": "29.0",
    "min_amount": "0.5",
    "amount_precision": 2,
    "price_precision": 2,
    "state": "enabled"
  }
]

GET https://trade.capecrypto.com/api/v2/atlas/public/markets

Returns a list of all available markets

Public

GET Alive

/public/health/alive

Description

Check that the trade engine is running. Occasionally the trade engine will be temporarily taken offline for udpates and general maintenance. Maintenence will usually not take longer than a few minutes.

Responses

Code Description
200 Trade engine is running, trading is available

GET Get VASPs

/public/vasps

Description

Retrieve a paginated list of active Virtual Asset Service Providers (VASPs) registered in the system. VASPs are used for travel rule compliance when creating beneficiaries with cryptocurrency addresses.

When creating a beneficiary with travel rule information, use the id from this endpoint as the vasp_id in the beneficiary data.

Parameters

Name Located in Description Required Schema
page query Page number (default: 1) No integer
limit query Results per page (default: 25, max: 100) No integer

Response Headers

Header Description
Page Current page number
Per-Page Number of results per page
Total Total number of VASPs available

Responses

Code Description Schema
200 List of active VASPs [ VASP ]

GET Ticker

/public/markets/{market}/tickers

Description

Get ticker of specific market.

Parameters

Name Located in Description Required Schema
market path Yes string

Responses

Code Description Schema
200 Returns a ticker. Ticker

GET All tickers

/public/markets/tickers

Description

Get ticker of all markets

Responses

Code Description Schema
200 Returns an array of tickers. Ticker

GET K Line

/public/markets/{market}/k-line

Description

Get K-Line (OHLC) of specific market.

Parameters

Name Located in Description Required Schema
market path Yes string
period query Time period of K line, default to 1. You can choose between 1, 5, 15, 30, 60, 120, 240, 360, 720, 1440, 4320, 10080 No integer
time_from query Timestamp in ms. If set, only k-line data after that time will be returned. No integer
time_to query Timestamp in ms. If set, only k-line data till that time will be returned. No integer
limit query Limit the number of returned data points default to 30. Ignored if time_from and time_to are given. No integer

Responses

Code Description
200 Returns K-Line (OHLC) of market.

GET Orderbook

/public/markets/{market}/depth

Description

Get the orderbook for the specified market eg. 'btczar'. Asks and bids are sorted away from the spread, ie asks are sorted low to high, and bids are sorted high to low. Results are returned in tupples of [price, amount] in the base currency eg. 'btc'.

Parameters

Name Located in Description Required Schema
market path Yes string
limit query Limit the number of returned price levels. Default 100. No integer

Responses

Code Description
200 Get orderbook of specified market

GET Market trades

/public/markets/{market}/trades

Description

Get recent trades for the specified market, results are paginated

Parameters

Name Located in Description Required Schema
market path Yes string
limit query Limit the number of returned trades. Default to 100. No integer
time_from query Timestamp in ms. Return trades executed after the timestamp. No integer
time_to query Timestamp in ms. Return trades executed before the timestamp. No integer
page query Paginated page number. No integer
order_by query Default is desc (newest to oldest). Options are [asc desc] No string

Responses

Code Description Schema
200 Get recent trades on market. [ Trade ]

GET Markets

/public/markets

Description

Get all available markets.

Parameters

Name Located in Description Required Schema
limit query Limit the number of returned paginations. Defaults to 100. No integer
page query Paginated page number. No integer
ordering query Order of results by field name (see order_by below), defaults to 'asc'. No string
order_by query Name of the field to order by. [id position] No string
base_unit query Return only markets that have this currency as the base unit eg. 'btc' No string
quote_unit query Return only markets that have this currency as the quote unit eg. 'zar' No string

Responses

Code Description Schema
200 Get all available markets. [ Market ]

GET All Currencies

/public/currencies

Description

Get list of currencies

Parameters

Name Located in Description Required Schema
limit query Limit the number of returned paginations. Defaults to 100. No integer
page query Paginated page number. No integer
type query Currency type No string

Responses

Code Description Schema
200 Get list of currencies [ Currency ]

GET Single Currency

/public/currencies/{id}

Description

Get a single currency

Parameters

Name Located in Description Required Schema
id path Currency code. Yes string

Responses

Code Description Schema
200 Get a currency Currency

Account

GET Get transaction history

/account/transactions

Description

Retrieve your complete transaction history with flexible filtering options. This endpoint returns all types of transactions including deposits, withdrawals, trades, internal transfers, referrals, and maker rewards.

Transactions can be filtered by currency (matches both credit and debit currencies), transaction type, time range, and specific transaction ID. Results are paginated and can be ordered by creation time.

Parameters

Name Located in Description Required Schema
currency query Filter by currency code (matches credit or debit currency, e.g., "btc", "zar") No string
type query Filter by transaction type: 'deposit', 'withdraw', 'internal_transfer', 'trade', 'referral', 'maker_reward' No string
txid query Filter by specific transaction ID No string
time_from query Start timestamp (Unix timestamp in seconds) No integer
time_to query End timestamp (Unix timestamp in seconds) No integer
order_by query Sort order: 'asc' or 'desc' (default: 'desc') No string
page query Page number (default: 1, min: 1) No integer
limit query Results per page (default: 50, min: 1, max: 1000) No integer

Response Headers

Header Description
Total Total number of transactions
Page Current page number
Per-Page Number of results per page

Responses

Code Description Schema
200 Transaction history retrieved [ Transaction ]
401 Authentication required Error array
422 Validation error Error array

Example Request

const axios = require("axios");
const generateHeaders = require("./generateHeaders");

getTransactions = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "GET";
  const path = `/api/v2/account/transactions?currency=btc&type=deposit&limit=25&page=1`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

getTransactions();

POST Create new beneficiary

/account/beneficiaries

Description

Create a new beneficiary for cryptocurrency or fiat withdrawals. Beneficiaries can be cryptocurrency wallet addresses (with required travel rule information) or bank accounts for fiat transfers. All beneficiaries created via the API are automatically activated and ready for use.

For cryptocurrency beneficiaries with travel rule requirements, you can specify whether the wallet is your own (is_own), unhosted (is_unhosted), or associated with a Virtual Asset Service Provider (VASP). Use the Get VASPs endpoint to retrieve available VASP IDs.

Parameters

Name Located in Description Required Schema Max Length
currency formData Currency code (e.g., "btc", "eth", "zar", "gbp") Yes string 10 chars
name formData Display name for the beneficiary Yes string 64 chars
description formData Optional notes about the beneficiary No string 255 chars
blockchain_key formData Blockchain identifier (defaults to currency's blockchain if omitted) No string 255 chars
withdraw_method_id formData Specific withdrawal method ID (auto-selected if omitted) No integer -
data formData Address/account details in JSON format (structure varies by currency type - see tables below) Yes json -

Cryptocurrency data Object Fields

Field Type Required Description
address string Yes Cryptocurrency wallet address
tag string No Destination tag (for XRP, Stellar, etc.) - max: 4294967295
is_own boolean No Travel rule: Is this your own wallet?
is_unhosted boolean No Travel rule: Is this an unhosted wallet?
vasp_id number No Travel rule: VASP ID (obtain from Get VASPs endpoint)
recipient_data object No Travel rule: Recipient information (see travel rule fields)

Fiat data Object Fields

Field Type Required Description
full_name string Yes Account holder's full legal name (mandatory for fiat)
account_number string No Bank account number
bank_name string No Name of the bank
bank_swift_code string No SWIFT/BIC code
bank_routing_number string No Routing number (US banks)
iban string No IBAN (International Bank Account Number)
bank_address string No Bank's physical address
bank_city string No Bank's city
intermediary_bank_swift string No Intermediary bank SWIFT code (for international transfers)
account_holder_name string No Account holder name (may differ from full_name)
account_type string No Type of account (checking, savings, etc.)

Travel Rule recipient_data Object (Nested within data) (required for Cryptocurrency Beneficiaries)

Field Type Required Description
first_name string Yes (for Individuals) Recipient's first name
last_name string Yes (for Individuals) Recipient's last name
country string Yes Recipient's country code
identity_type string Yes Individual or Business
company_name string Yes (for Businesses) Company name (for business recipients)

Request Examples

Example 1: Crypto - Bitcoin

{
  "currency": "btc",
  "name": "My Bitcoin Wallet",
  "description": "Personal cold storage wallet",
  "data": {
    "address": "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"
  }
}

Example 2: Crypto - XRP with Destination Tag

{
  "currency": "xrp",
  "name": "Exchange XRP Account",
  "description": "My Binance XRP deposit",
  "data": {
    "address": "rN7n7otQDd6FczFgLdlqtyMVrn3qHwuSaDcqC",
    "tag": "123456789"
  }
}

Example 3: Crypto - Ethereum with Travel Rule Info

{
  "currency": "eth",
  "name": "Friend's Wallet",
  "description": "John's Ethereum wallet",
  "data": {
    "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "is_own": false,
    "is_unhosted": true,
    "recipient_data": {
      "first_name": "John",
      "last_name": "Doe",
      "country": "US"
    }
  }
}

Example 4: Fiat - ZAR (South African Rand)

{
  "currency": "zar",
  "name": "South African Bank Account",
  "description": "Personal savings account",
  "data": {
    "full_name": "Thabo Mbeki",
    "account_number": "1234567890",
    "bank_name": "Standard Bank",
    "bank_swift_code": "SBZAZAJJ",
    "bank_address": "30 Baker Street",
    "bank_city": "Johannesburg",
    "account_type": "savings"
  }
}

Example 5: Fiat - GBP SWIFT Transfer

{
  "currency": "gbp",
  "name": "UK Business Account",
  "description": "Company bank account",
  "data": {
    "full_name": "John Smith",
    "account_number": "12345678",
    "bank_name": "Barclays Bank",
    "bank_swift_code": "BARCGB22",
    "bank_address": "1 Churchill Place",
    "bank_city": "London",
    "account_type": "checking"
  }
}

Example 6: Fiat - EUR IBAN Transfer

{
  "currency": "eur",
  "name": "EU Partner Account",
  "description": "European supplier payment",
  "data": {
    "full_name": "Jean Dupont",
    "iban": "FR1420041010050500013M02606",
    "bank_name": "BNP Paribas",
    "bank_swift_code": "BNPAFRPP",
    "bank_city": "Paris"
  }
}

Example 7: Fiat - International with Intermediary Bank

{
  "currency": "usd",
  "name": "International Vendor",
  "description": "Asian supplier USD payment",
  "data": {
    "full_name": "ABC Trading Company Ltd",
    "account_number": "9876543210",
    "bank_name": "Asia Commercial Bank",
    "bank_swift_code": "ACBVSGSG",
    "intermediary_bank_swift": "CHASUS33",
    "bank_address": "123 Business Street",
    "bank_city": "Singapore"
  }
}

Important Notes

Responses

Code Description Schema
201 Create new beneficiary Beneficiary

PATCH Activate beneficiary

/account/beneficiaries/{id}/activate

Description

Activates a new beneficiary with pin emailed during beneficiary creation.

Note: This activation step is only required for beneficiaries created through the web interface. Beneficiaries created via the API are automatically activated and do not require this step.

Parameters

Name Located in Description Required Schema
id path Beneficiary Identifier Yes integer
pin formData Pin code for beneficiary activation Yes integer

Responses

Code Description Schema
200 Activates beneficiary with pin Beneficiary

DELETE Delete beneficiary

/account/beneficiaries/{id}

Description

Delete beneficiary

Parameters

Name Located in Description Required Schema
id path Beneficiary Identifier Yes integer

Responses

Code Description
204 Delete beneficiary

GET Beneficiary by id

/account/beneficiaries/{id}

Description

Get beneficiary by id

Parameters

Name Located in Description Required Schema
id path Beneficiary Identifier Yes integer

Responses

Code Description Schema
200 Get beneficiary by id Beneficiary

GET List Beneficiaries

/account/beneficiaries

Description

Get list of user beneficiaries

Parameters

Name Located in Description Required Schema
currency query Beneficiary currency code. No string
state query Defines either beneficiary active - user can use it to withdraw moneyor pending - requires beneficiary activation with pin. No string

Responses

Code Description Schema
200 Get list of user beneficiaries [ Beneficiary ]

GET Withdraw quote

/account/withdraws/quote/{currency}

/account/withdraws/quote/{currency}?bolt=ln...

Description

Get the expected fee to withdraw the specified currency. Submit the quote_id with your withdraw request payload to honor that fee during the withdrawal. Quotes are currently valid for 90 seconds, ensure you submit the withdrawal request before the expires_at parameter.

Parameters

Name Located in Description Required Schema
currency path Currency code. Yes string
bolt query For BTC Lightning payments, supply the Bolt11 invoice. Fees are returned in BTC. No integer

Responses

Code Description Schema
200 Returns a withdraw quote [ Withdraw quote ]
422 Unproccesable entity Error array containing the reason why the quote could not be created

POST New withdrawal

/account/withdraws

Description

Creates new withdrawal to active beneficiary.

Note: OTP is not required when creating withdrawals via the API. The OTP requirement only applies to withdrawals created through the web app, however 2FA must still be enabled on the account to enable withdraws.

Parameters

Name Located in Description Required Schema
beneficiary_id formData ID of active Beneficiary. Yes integer
currency formData The currency code matching the beneficiary. Yes string
amount formData The amount to withdraw. Yes double
note formData ZAR withdrawals only. Use 'same_day_rtc' for Faster withdraws No string

Responses

Code Description
201 Creates new withdrawal.

GET Withdrawal history

/account/withdraws

Description

Get your withdraw history, paginated.

Parameters

Name Located in Description Required Schema
currency query Currency code. No string
limit query Number of withdraws per page (defaults to 100, maximum is 100). No integer
state query Filter by withdraw state. [ Withdraw states ] No string
rid query Destination address (blockchain withdraws only). No string
page query Page number (defaults to 1). No integer

Responses

Code Description Schema
200 List your withdraws. [ Withdraw ]

GET Get deposit methods

/api/v2/payments/deposits/methods

Description

Get all available deposit methods for the authenticated user. Returns deposit methods for both cryptocurrencies and fiat currencies, filtered based on the user's KYC level and remaining transaction limits.

Each method includes minimum and maximum amounts, fees, provider information, and remaining limits for different time periods (daily, weekly, monthly, yearly).

Responses

Code Description Schema
200 Array of available deposit methods. [ Deposit Method ]

GET Get withdraw methods

/api/v2/payments/withdraws/methods

Description

Get all available withdrawal methods for the authenticated user. Returns withdrawal methods for both cryptocurrencies and fiat currencies, filtered based on the user's KYC level and remaining transaction limits.

Each method includes minimum and maximum amounts, fees, provider information, processor details, and remaining limits for different time periods (daily, weekly, monthly, yearly).

Responses

Code Description Schema
200 Array of available withdrawal methods. [ Withdraw Method ]

POST Redeem voucher

/payments/vouchers/new

Description

Redeem a voucher code.

Parameters

Name Located in Description Required Schema
payment_method_id query The id of this deposit method (see Instant FIAT deposit methods) Yes integer
amount query The amount to redeem, eg 10 redeems R10 Yes double
voucher_pin query The voucher redemption code. Yes string

Responses

Code Description Schema
201 Voucher redemption. Voucher redemption

GET Deposit address

/account/deposit_address/{currency}

Description

Fetch the deposit address for the specified currency.

Note: If your currency deposit address has not been created yet, the result may return empty while the address is being created. If it is empty, please try again in a few seconds.

Parameters

Name Located in Description Required Schema
currency path The currency to receive. Yes string

Responses

Code Description Schema
200 Deposit address. (May be empty on first request for new accounts, see note above). Deposit

GET Deposit by ID

/account/deposits/{id}

Description

Get details of a specific deposit by its internal deposit ID.

Parameters

Name Located in Description Required Schema
id path Deposit ID Yes integer

Responses

Code Description Schema
200 Get details of specific deposit. Deposit

POST Submit deposit travel rule

/account/deposits/{id}/travel_rule

Description

Submit travel rule compliance information for a cryptocurrency deposit that is on hold pending travel rule verification.

Note: This endpoint is only applicable for cryptocurrency deposits in the on_hold_travel_rule_customer state. The deposit must be a coin deposit (not fiat).

Parameters

Name Located in Description Required Schema
id path Deposit ID Yes string
is_own formData Is this your own wallet? Yes boolean
is_unhosted formData Is this an unhosted wallet? (Cannot be true if vasp_id is provided) Yes boolean
vasp_id formData VASP ID from the system (mutually exclusive with custom_vasp fields) No integer
custom_vasp_name formData Custom VASP name (required if not using vasp_id and not unhosted) No string
custom_vasp_url formData Custom VASP URL (required if not using vasp_id and not unhosted) No string
originator_data formData Originator information in JSON format (required if is_own is false, see fields below) No json

Originator Data Fields:

When is_own is false, you must provide originator information:

Field Type Required Description
identity_type string Yes Type of identity (e.g., "passport", "id")
first_name string Yes (for Individuals) Originator's first name
last_name string Yes (for Individuals) Originator's last name
company_name string Yes (for Businesses) Company name (for business originators)
country string Yes ISO country code (e.g., "US", "ZA", "GB")

Responses

Code Description Schema
200 Travel rule submitted successfully. Deposit transitions to hold state. Deposit
422 Validation error or deposit not eligible (wrong state, already submitted). Error array

GET Deposit history

/account/deposits

Description

Get your deposit history. You can filter by specific deposit transaction using the txid query parameter, or fetch all deposits with optional filters for currency and state

Parameters

Name Located in Description Required Schema
currency query Currency code No string
state query Filter deposits by deposit state States No string
txid query Deposit transaction id. No string
limit query Number of deposits per page (defaults to 100, maximum is 100). No integer
page query Page number (defaults to 1). No integer

Responses

Code Description Schema
200 Get your deposit history. [ Deposit ]

GET Balance

/account/balances/{currency}

Description

Get single account balance by currency code eg. 'btc'

Parameters

Name Located in Description Required Schema
currency path The currency code. Yes string

Responses

Code Description Schema
200 Get account balance by currency Account

GET All balances

/account/balances

Description

Get account balances for all currencies

Parameters

Name Located in Description Required Schema
limit query Limit the number of returned paginations. Defaults to 100. No integer
page query Paginated page number. No integer
nonzero query Filter non zero balances. No Boolean

Responses

Code Description Schema
200 Array of balances [ Account ]

POST New lightning payment

/account/lightning/pays

Description

Submits a payment request to pay a BTC Lightning BOLT11 invoice (withdraws this balance from your BTC account).

As the payment is requested and validated on the lightning network, please query your withdrawal history to confirm that the payment succeeded.

Note: OTP is not required when creating withdrawals via the API. The OTP requirement only applies to withdrawals created through the web app, however 2FA must still be enabled on the account to enable withdraws.

Parameters

Name Located in Description Required Schema
bolt query The bolt11 invoice to pay (either bolt or address required) No string
amount query The amount to of the invoice in SATs. Sanity check is performed against the supplied bolt11 Yes double
quote_id query The quote_id of the withdrawal quote, see Withdraw quote No string
member_description query Personal description of this payment for your records No string
description query Payment description (visible to recipient if applicable) No string
data formData Travel rule information in JSON format (see travel rule fields below) No json

Travel Rule Data (Optional):

For compliance with travel rule requirements, you can include recipient information in the data parameter as a JSON object:

{
  "is_own": false,
  "is_unhosted": true,
  "vasp_id": 123,
  "address": "lnbc...", // Lightning address or BOLT11
  "recipient_data": {
    "first_name": "John",
    "last_name": "Doe",
    "company_name": "Acme Corp",
    "address": "123 Main St",
    "country": "US",
    "identity_type": "Passport"
  }
}

Travel Rule Fields:

Field Type Description
is_own boolean Is this your own wallet?
is_unhosted boolean Is this an unhosted wallet?
vasp_id number VASP ID (obtain from Get VASPs endpoint)
address string Lightning address or BOLT11 invoice
recipient_data object Recipient information (see fields below)

Recipient Data Fields:

Field Type Description
first_name string Recipient's first name
last_name string Recipient's last name
company_name string Company name (for business recipients)
address string Physical address
country string Country code (e.g., "US", "ZA")
identity_type string Type of identification document

Responses

Code Description Schema
201 Lightning payment. [ Lightning payment ]

POST Create a Lightning invoice

/account/lightning/invoices

Description

Create a new lightning invoice to be paid (receive) over the BTC Lightning network.

You can optionally specify a conversion_percent (1 = 100%, 0.1 = 10%) to automatically convert once the invoice is paid. The conversion percent should be such that the conversion amount is greater than or equal to the minimum order amount for the market eg. 'btczar'. For example if receiving BTC 0.00001, and the minimum order amount on 'btczar' is 0.00001, your conversion percent would have to be 1 (100%). Anything less would cause the invoice creation to fail, as the conversion amount would be below the minimum market order amount.

The invoice expires in 1 hour.

Parameters

Name Located in Description Required Schema
amount query The amount to of the invoice in SATs. Sanity check is performed against the supplied bolt11 Yes double
conversion_percent query Percent of the received amount to be automatically converted to FIAT No double
description query The description for the invoice (visible on the Bolt11 invoice to the payer). If left blank the default Cape Crypto Invoice is used No string
member_description query Personal description of this invoice for your records No string

Responses

Code Description Schema
201 Lightning invoice. [ Lightning invoice ]

Sub-Accounts

Note: Sub-account endpoints use the /api/v2/persona base path instead of /api/v2/atlas.

Sub-Account actions: To make API requests for a sub-account (e.g., placing trades, checking balances), include the X-Auth-Subaccount-Uid header with the sub-account's UID. Your parent account's API credentials are used for authentication, but operations are performed in the sub-account's context.

POST Create Sub-Account

// createSubAccount.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

createSubAccount = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "POST";
  const path = `/api/v2/persona/resource/sub_accounts`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const subAccountData = {
    account_name: "Trading Account 1",
    deposit_action: "transfer_to_parent",
  };

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
    data: subAccountData,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

createSubAccount();

/persona/resource/sub_accounts

Description

Create a new sub-account that inherits the parent's KYC verification. Sub-accounts cannot login independently and are managed by the parent account.

Parameters

Name Located in Description Required Schema
account_name formData Display name for the sub-account (1-100 characters) Yes string
deposit_action formData JSON object defining automated deposit handling (see deposit action options below) No json
data formData Optional additional data in JSON format No json

Deposit Action Options:

Configure automatic actions for deposits received by this sub-account.

The deposit_action parameter accepts a JSON object with the following structure:

{
  "action": "none|aggregate|convert_and_withdraw|aggregate_convert_and_withdraw",
  "target_currency": "currency_code", // Required for convert actions (e.g., "zar", "usd")
  "beneficiary_id": 123, // Required for withdraw actions
  "lightning_invoice": "lnbc...", // Alternative to beneficiary_id for Lightning withdrawals
  "max_slippage": 2.0 // Optional: Max slippage percentage (0.1-10, default: 2.0)
}

Available Actions:

Action Description Required Fields
none No automatic action (default) None
aggregate Automatically transfer deposits to parent account None
convert_and_withdraw Convert deposit to target currency and withdraw to beneficiary target_currency, beneficiary_id or lightning_invoice
aggregate_convert_and_withdraw Transfer to parent, then convert and withdraw target_currency, beneficiary_id or lightning_invoice

Example with Simple Aggregate:

{
  "account_name": "Trading Account 1",
  "deposit_action": {
    "action": "aggregate"
  }
}

Example with Convert and Withdraw:

{
  "account_name": "USD Conversion Account",
  "deposit_action": {
    "action": "convert_and_withdraw",
    "target_currency": "usd",
    "beneficiary_id": 789,
    "max_slippage": 1.5
  }
}

Responses

Code Description Schema
201 Sub-account created successfully SubAccount
422 Validation error Error array

GET List Sub-Accounts

// listSubAccounts.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

listSubAccounts = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "GET";
  const path = `/api/v2/persona/resource/sub_accounts?page=1&limit=100`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

listSubAccounts();

/persona/resource/sub_accounts

Description

Get a paginated list of the user's sub-accounts.

Parameters

Name Located in Description Required Schema
page query Page number (defaults to 1) No integer
limit query Number of results per page (default: 100, max: 1000) No integer

Responses

Code Description Schema
200 List of sub-accounts [ SubAccount ]

GET Get Sub-Account by UID

// getSubAccount.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

getSubAccount = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "GET";
  const subAccountUid = "CCT1234567";
  const path = `/api/v2/persona/resource/sub_accounts/${subAccountUid}`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

getSubAccount();

/persona/resource/sub_accounts/{uid}

Description

Retrieve a specific sub-account by its unique identifier.

Parameters

Name Located in Description Required Schema
uid path Sub-account identifier Yes string

Responses

Code Description Schema
200 Sub-account found SubAccount
404 Not found Error array

PUT Update Sub-Account

// updateSubAccount.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

updateSubAccount = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "PUT";
  const subAccountUid = "CCT1234567";
  const path = `/api/v2/persona/resource/sub_accounts/${subAccountUid}`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const updateData = {
    deposit_action: "none",
  };

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
    data: updateData,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

updateSubAccount();

/persona/resource/sub_accounts/{uid}

Description

Update sub-account settings.

Parameters

Name Located in Description Required Schema
uid path Sub-account identifier Yes string
deposit_action formData Action for deposits: 'none' or 'transfer_to_parent' No string

Responses

Code Description Schema
200 Sub-account updated successfully SubAccount
404 Not found Error array
422 Validation error Error array

DELETE Delete Sub-Account

// deleteSubAccount.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

deleteSubAccount = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "DELETE";
  const subAccountUid = "CCT1234567";
  const path = `/api/v2/persona/resource/sub_accounts/${subAccountUid}`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
  };

  try {
    const results = await axios(axiosConfig);
    console.log("Sub-account deleted successfully");
  } catch (err) {
    console.log(err);
  }
};

deleteSubAccount();

/persona/resource/sub_accounts/{uid}

Description

Soft delete a sub-account (sets state to 'deleted'). The sub-account will no longer be accessible.

Parameters

Name Located in Description Required Schema
uid path Sub-account identifier Yes string

Responses

Code Description
204 Sub-account deleted successfully
404 Not found

Customers Management

Note: Customer management endpoints use the /api/v2/persona base path and are available for Merchants and Aggregators only. Not all accounts are enabled for customer management, please contact us at support.capecrypto.com should you with to apply to upgrade your account to a merchant or aggregator account.

Hierarchical Structure:

Customer Types:

Acting as a Customer: To make API requests for a customer (e.g., placing trades, checking balances, making withdrawals), include the X-Auth-Subaccount-Uid header with the customer's UID. Your merchant/aggregator API credentials are used for authentication, but operations are performed in the customer's context.

POST Create Individual Customer

// createIndividualCustomer.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

createIndividualCustomer = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "POST";
  const path = `/api/v2/persona/resource/customers`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const customerData = {
    entity_type: "individual",
    email: "[email protected]",
    phone_number: "+27821234567",
    account_name: "John Doe Account",

    // Personal Information
    first_name: "John",
    last_name: "Doe",
    dob: "1990-01-15",

    // Location Information
    address: "123 Main Street, Apt 4B",
    postcode: "7700",
    city: "Cape Town",
    country: "ZA",
    nationality: "ZA",
    country_of_residence: "ZA",
    sa_id_number: "9001155800087",

    // Professional Information (See not above for approved list)
    occupation: "Software Engineer",
    source_of_funds: "Salary or Wages",

    // Document Information
    doc_number: "M12345678",
    doc_type: "Passport",
    doc_expire: "2028-12-31",
    doc_country: "ZA",

    // PEP Declaration
    is_pep_occupation: false,
    is_pep_related: false,
    is_pip: false,
  };

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
    data: customerData,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

createIndividualCustomer();

/persona/resource/customers

Description

Create a new individual customer account. Merchants create customers under their own account. Aggregators can create customers under their own account or under a specific merchant by providing merchant_uid.

Individual customers are created in two steps:

Once all documents are received the customer account will be verified by Cape Crypto, and their parent cant then create payment requests, or have the customer deposit funds into their account.


Parameters

Account Information:

Name Located in Description Required Schema
entity_type formData Must be 'individual' Yes string
email formData Customer email address (unique per merchant) Yes string
phone_number formData Phone number in international format (e.g., +27821234567) Yes string
account_name formData Display name for the account No string
merchant_uid formData Merchant UID (aggregators only - create under specific merchant) No string
first_name formData Customer first name Yes string
last_name formData Customer last name Yes string
dob formData Date of birth in YYYY-MM-DD format (must be 18+) Yes string
address formData Full street address Yes string
postcode formData Postal/ZIP code Yes string
city formData City name Yes string
country formData ISO country code (2 chars, e.g., 'ZA', 'US') Yes string
nationality formData Nationality ISO country code (2 chars) Yes string
country_of_residence formData Country of residence ISO code (2 chars) Yes string
sa_id_number formData South African ID number Yes if nationality='ZA' string
occupation formData Customer occupation (e.g., 'Software Engineer', 'Teacher', 'Business Owner') Yes string
source_of_funds formData Source of funds (e.g., 'Salary or Wages', 'Business Income', 'Investment Income') Yes string
is_pep_occupation formData Is the customer a PEP by occupation? (default: false) No boolean
pep_occupation_data formData Details if is_pep_occupation is true Yes if is_pep_occupation=true string
is_pep_related formData Is the customer related to a PEP? (default: false) No boolean
pep_related_data formData Details if is_pep_related is true Yes if is_pep_related=true string
is_pip formData Is the customer a Prominent Influential Person? (default: false) No boolean
pip_data formData Details if is_pip is true Yes if is_pip=true string
deposit_action formData JSON object defining automated deposit handling (see deposit action options below) No json

Deposit Action Options:

The optional deposit_action parameter accepts a JSON object with the following structure:

{
  "action": "none|aggregate|convert_and_withdraw|aggregate_convert_and_withdraw",
  "target_currency": "currency_code", // Required for convert actions (e.g., "zar", "usd")
  "beneficiary_id": 123, // Required for withdraw actions
  "lightning_invoice": "lnbc...", // Alternative to beneficiary_id for Lightning withdrawals
  "max_slippage": 2.0 // Optional: Max slippage percentage (0.1-10, default: 2.0)
}

Available Actions:

If no deposit action is provided, the default will be "none" which will see the deposit simply credited to the customer account, like a regular deposit.

Note: Only the "none" action is currently available, the additional actions are coming soon!

Action Description Required Fields
none No automatic action (default) None
aggregate (coming soon) Automatically transfer deposits to parent merchant/aggregator account None
convert_and_withdraw (coming soon) Convert deposit to target currency and withdraw to beneficiary target_currency, beneficiary_id or lightning_invoice
aggregate_convert_and_withdraw (coming soon) Transfer to parent, then convert and withdraw target_currency, beneficiary_id or lightning_invoice

Example with Deposit Action:

{
  "entity_type": "individual",
  "email": "[email protected]",
  "first_name": "John",
  "last_name": "Doe",
  // ... other required fields ...
  "deposit_action": {
    "action": "convert_and_withdraw",
    "target_currency": "zar",
    "beneficiary_id": 456,
    "max_slippage": 2.5
  }
}

Responses

Code Description Schema
201 Customer created successfully Customer
422 Validation error Error array

POST Create Business Customer

// createBusinessCustomer.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

createBusinessCustomer = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "POST";
  const path = `/api/v2/persona/resource/customers`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const customerData = {
    entity_type: "business",
    email: "[email protected]",
    phone_number: "+27821234567",
    account_name: "ACME Corporation",

    business_profile: {
      // Core Business Information
      company_name: "ACME Corp (Pty) Ltd",
      registration_number: "2020/123456/07",
      registration_country: "ZA",
      industry: "Financial Services",
      purpose_of_account: "Cryptocurrency trading and payment processing",
      expected_monthly_volume_usd: 500000,

      // Additional Business Information
      jurisdictions_of_operation: ["ZA", "US", "GB"],
      website: "https://www.acmecorp.com",
      employee_count: 25,

      // Business Address
      address: "123 Business Street, Suite 100",
      city: "Cape Town",
      postcode: "8001",
      country: "ZA",

      // License Information
      license_status: "Licensed",
      license_number: "FSP12345",
      license_jurisdictions: ["ZA"],
    },

    // Ultimate Beneficial Owners (at least 1 required)
    ubos: [
      {
        // Personal Information
        email: "[email protected]",
        first_name: "Jane",
        last_name: "Smith",
        phone_number: "+27821111111",
        dob: "1985-05-15",

        // Residence Information
        address: "456 Residential Ave",
        postcode: "7700",
        city: "Cape Town",
        country: "ZA",
        nationality: "ZA",
        id_number: "8505155800088",

        // Professional Information
        occupation: "Chief Executive Officer",
        source_of_funds: "Business Income",

        // Ownership & Roles
        ownership_percentage: 60,
        is_director: true,
        is_authorized_signatory: true,

        // PEP Declaration
        is_pep_occupation: false,
        is_pep_related: false,
        is_pip: false,
      },
      {
        // Second UBO example
        email: "[email protected]",
        first_name: "John",
        last_name: "Doe",
        dob: "1980-03-20",

        address: "789 Co-founder Street",
        postcode: "7800",
        city: "Cape Town",
        country: "ZA",
        nationality: "ZA",
        id_number: "8003205800089",

        occupation: "Chief Technology Officer",
        source_of_funds: "Business Income",

        ownership_percentage: 40,
        is_director: true,
        is_authorized_signatory: false,

        is_pep_occupation: false,
        is_pep_related: false,
        is_pip: false,
      },
    ],
  };

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
    data: customerData,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

createBusinessCustomer();

/persona/resource/customers

Description

Create a new business customer account with company details and Ultimate Beneficial Owner (UBO) information. Merchants create customers under their own account. Aggregators can create customers under their own account or under a specific merchant by providing merchant_uid.

Business customers start with 'pending' status and require verification. Business documents must be uploaded separately using the document upload endpoint (see "Upload Business Customer Documents" section below).


Parameters

Account Information:

Name Located in Description Required Schema
entity_type formData Must be 'business' Yes string
email formData Business email address (unique per merchant) Yes string
phone_number formData Business phone number in international format Yes string
account_name formData Business account display name (typically company name) Yes string
merchant_uid formData Merchant UID (aggregators only - create under specific merchant) No string

Business Profile (JSON Object):

Core Business Information:

Field Description Required Schema
company_name Legal company name Yes string
registration_number Company registration number Yes string
registration_country ISO country code where company is registered (2-3 chars) Yes string
industry Industry classification (e.g., 'Financial Services', 'Technology', 'Healthcare', 'Retail') Yes string
purpose_of_account Business purpose for the account Yes string
expected_monthly_volume_usd Expected monthly trading volume in USD Yes number
jurisdictions_of_operation Array of country codes where business operates No array of strings
website Company website URL No string
employee_count Number of employees No number
address Business street address No string
city Business city No string
postcode Business postal code No string
country Business country ISO code (2 chars) No string
license_status License status: 'Licensed', 'Not Licensed', 'Pending' No string
license_number License number (required if license_status is 'Licensed') Conditional string
license_jurisdictions Array of country codes where licensed (required if license_status is 'Licensed') Conditional array of strings

UBOs Array (Ultimate Beneficial Owners) - At least 1 required:

Each UBO object must include:

Personal Information:

Field Description Required Schema
email UBO email address Yes string
first_name UBO first name Yes string
last_name UBO last name Yes string
phone_number UBO phone number No string
dob Date of birth (YYYY-MM-DD, must be 18+) Yes string
address UBO street address Yes string
postcode UBO postal code Yes string
city UBO city Yes string
country UBO country ISO code (2 chars) Yes string
nationality UBO nationality ISO code (2 chars) Yes string
id_number UBO national ID or passport number Yes string
occupation UBO occupation Yes string
source_of_funds UBO source of funds Yes string
ownership_percentage Ownership percentage (minimum 5%, maximum 100%) Yes number
is_director Is the UBO a company director? No boolean
is_authorized_signatory Is the UBO an authorized signatory? No boolean
is_pep_occupation Is the UBO a PEP by occupation? No boolean
pep_occupation_data Details if is_pep_occupation is true Conditional string
is_pep_related Is the UBO related to a PEP? No boolean
pep_related_data Details if is_pep_related is true Conditional string
is_pip Is the UBO a Prominent Influential Person? No boolean
pip_data Details if is_pip is true Conditional string

Responses

Code Description Schema
201 Business customer created successfully Customer
422 Validation error Error array

GET List Customers

// listCustomers.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

listCustomers = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "GET";
  const path = `/api/v2/persona/resource/customers?page=1&limit=100`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

listCustomers();

/persona/resource/customers

Description

Get a paginated list of customers. Merchants see only their own customers. Aggregators see all customers (their own + all their merchants' customers). Aggregators can filter by specific merchant using merchant_uid.

Parameters

Name Located in Description Required Schema
page query Page number (defaults to 1) No integer
limit query Number of results per page (default: 100, max: 1000) No integer
merchant_uid query Filter by merchant UID (aggregators only) No string

Responses

Code Description Schema
200 List of customers with pagination [ Customer ]

Response Headers:

Page: 1
Per-Page: 100
Total: 150

Response Body (Array):

[
  {
    "uid": "CCT1234567",
    "account_name": "CUST-001",
    "state": "active",
    "custom_id": "CUST-001",
    "kyc_status": "verified",
    "created_at": "2024-01-01T00:00:00.000Z"
  },
  {
    "uid": "CCT1234567",
    "account_name": "CUST-002",
    "state": "active",
    "custom_id": "CUST-002",
    "kyc_status": "verified",
    "created_at": "2024-01-02T00:00:00.000Z"
  }
]

Note: Pagination information is returned in HTTP headers (Page, Per-Page, Total), not in the response body.

GET Get Customer by UID

// getCustomer.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

getCustomer = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "GET";
  const customerUid = "CCT1234567";
  const path = `/api/v2/persona/resource/customers/${customerUid}`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

getCustomer();

/persona/resource/customers/{uid}

Description

Retrieve a specific customer with full details including profile, phone, labels, and business information (if applicable).

Parameters

Name Located in Description Required Schema
uid path Customer identifier Yes string

Responses

Code Description Schema
200 Customer found Customer
404 Not found Error array

PUT Update Customer

// updateCustomer.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

updateCustomer = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "PUT";
  const customerUid = "CCT1234567";
  const path = `/api/v2/persona/resource/customers/${customerUid}`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const updateData = {
    custom_id: "CUST-001-UPDATED",
    external_verification_url: "https://example.com/verify/updated",
  };

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
    data: updateData,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

updateCustomer();

/persona/resource/customers/{uid}

Description

Update customer metadata (custom_id and external_verification_url).

Parameters

Name Located in Description Required Schema
uid path Customer identifier Yes string
custom_id formData Updated custom identifier No string
external_verification_url formData Updated external verification URL No string

Responses

Code Description Schema
200 Customer updated successfully Customer
404 Not found Error array
422 Validation error Error array

POST Upload Individual Customer Documents

// uploadIndividualDocuments.js
const axios = require("axios");
const FormData = require("form-data");
const fs = require("fs");
const generateHeaders = require("./generateHeaders");

uploadDocuments = async (customerUid) => {
  const baseUrl = "https://trade.capecrypto.com";
  const path = `/api/v2/verification/customers/documents`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  // Document 1: ID Front
  const formData1 = new FormData();
  formData1.append("upload", fs.createReadStream("./id_front.jpg"));
  formData1.append("doc_type", "Passport");
  formData1.append("customer_uid", customerUid);
  formData1.append("doc_number", "M12345678");
  formData1.append("doc_expire", "2028-12-31");

  try {
    await axios.post(url, formData1, {
      headers: { ...headers, ...formData1.getHeaders() },
    });
    console.log("ID front uploaded successfully");
  } catch (err) {
    console.log("Error uploading ID front:", err);
  }

  // Document 2: ID Back
  const formData2 = new FormData();
  formData2.append("upload", fs.createReadStream("./id_back.jpg"));
  formData2.append("doc_type", "Drivers license back");
  formData2.append("customer_uid", customerUid);
  formData2.append("doc_number", "DL987654");
  formData2.append("doc_expire", "2028-12-31");

  try {
    await axios.post(url, formData2, {
      headers: { ...headers, ...formData2.getHeaders() },
    });
    console.log("ID back uploaded successfully");
  } catch (err) {
    console.log("Error uploading ID back:", err);
  }

  // Document 3: Selfie
  const formData3 = new FormData();
  formData3.append("upload", fs.createReadStream("./selfie.jpg"));
  formData3.append("doc_type", "Selfie");
  formData3.append("customer_uid", customerUid);

  try {
    await axios.post(url, formData3, {
      headers: { ...headers, ...formData3.getHeaders() },
    });
    console.log("Selfie uploaded successfully");
  } catch (err) {
    console.log("Error uploading selfie:", err);
  }

  // Document 4: Proof of Address
  const formData4 = new FormData();
  formData4.append("upload", fs.createReadStream("./proof_of_address.pdf"));
  formData4.append("doc_type", "Proof of Address");
  formData4.append("customer_uid", customerUid);

  try {
    await axios.post(url, formData4, {
      headers: { ...headers, ...formData4.getHeaders() },
    });
    console.log("Proof of Address uploaded successfully");
  } catch (err) {
    console.log("Error uploading proof of address:", err);
  }
};

// Upload documents after customer creation
uploadDocuments("CCT1234567");

/api/v2/verification/customers/documents

Description

Upload KYC verification documents for individual customers. Multiple documents are required for complete verification:

Each document must be uploaded as a separate request with FormData.

Maximum file size: 4MB per document.

Note: This endpoint should be called after creating an individual customer using the Create Individual Customer endpoint. Use the customer UID returned from the creation response. All documents must be uploaded seperately.

Parameters

Name Located in Description Required Schema
upload formData The document file (image file: JPG, PNG, PDF) - Max 4MB Yes File
doc_type formData Document type (see Available Document Types below) Yes string
customer_uid formData Customer UID from the customer creation response Yes string
doc_number formData ID document number (optional for selfie) No string
doc_expire formData Document expiry date in YYYY-MM-DD format (optional for selfie) No string

Required Documents

The following documents must be uploaded for individual customers:

  1. ID Document (Front) - Front image of the identification document
  1. ID Document (Back) - Back image of identification document (Identity card and Driver license only)
  1. Selfie - Photograph of customer holding their ID document
  1. Proof of Address - Utility bill, bank statement, or lease agreement
    • doc_type: 'Proof of Address'
    • Document must be less than 3 months old
    • doc_number and doc_expire are optional

Available Document Types

Valid doc_type values for individual customer KYC documents:

Note: Use Title Case format exactly as shown below (e.g., "Passport", "Proof of Address", "Board Resolution"). The system automatically converts these to internal format.

Responses

Code Description Schema
200 Document uploaded successfully Success object
400 Invalid doc_type Error array
422 Validation error Error array

POST Upload Business Customer Documents

// uploadBusinessDocuments.js
const axios = require("axios");
const FormData = require("form-data");
const fs = require("fs");
const generateHeaders = require("./generateHeaders");

uploadBusinessDocument = async (customerUid, documentType, filePath) => {
  const baseUrl = "https://trade.capecrypto.com";
  const path = `/api/v2/verification/customers/documents`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const formData = new FormData();
  formData.append("upload", fs.createReadStream(filePath));
  formData.append("doc_type", documentType);
  formData.append("customer_uid", customerUid);
  formData.append("doc_number", "N/A");
  formData.append("doc_expire", "N/A");

  try {
    const result = await axios.post(url, formData, {
      headers: { ...headers, ...formData.getHeaders() },
    });
    console.log(`${documentType} uploaded successfully`);
    return result.data;
  } catch (err) {
    console.log(`Error uploading ${documentType}:`, err);
    throw err;
  }
};

// Upload all required business documents
const uploadAllBusinessDocuments = async (customerUid) => {
  const documents = [
    { type: "Board Resolution", file: "./board_resolution.pdf" },
    { type: "Company Registration", file: "./company_registration.pdf" },
    { type: "AML Policy", file: "./aml_policy.pdf" },
    { type: "Business License", file: "./business_license.pdf" },
    { type: "Business Proof of Address", file: "./proof_of_address.pdf" },
    { type: "Shareholder Certificate", file: "./shareholder_certificate.pdf" },
    { type: "Incorporation Certificate", file: "./incorporation_cert.pdf" },
    { type: "Bank Statement", file: "./bank_statement.pdf" },
    { type: "Tax Statement", file: "./tax_statement.pdf" },
  ];

  for (const doc of documents) {
    await uploadBusinessDocument(customerUid, doc.type, doc.file);
  }

  console.log("All business documents uploaded successfully");
};

// Upload documents after business customer creation
uploadAllBusinessDocuments("CCT1234567");

/api/v2/verification/customers/documents

Description

Upload KYB verification documents for business customers. Nine business documents are required for complete verification. Additionally, all UBOs (Ultimate Beneficial Owners) must upload their individual KYC documents (see "Upload UBO Documents" section below). Each document must be uploaded as a separate request with FormData.

Maximum file size: 4MB per document.

Note: This endpoint should be called after creating a business customer using the "Create Business Customer" endpoint. Use the customer UID returned from the creation response. When all 9 business documents and all UBO documents are uploaded, the system will notify administrators for manual review.

Parameters

Name Located in Description Required Schema
upload formData The document file (PDF, JPG, PNG) - Max 10MB Yes File
doc_type formData Business document type (see list below) Yes string
customer_uid formData Customer UID from the business customer creation response Yes string
doc_number formData Use 'N/A' for business documents No string
doc_expire formData Use 'N/A' for business documents No string

Available Business Document Types

Valid doc_type values for business customer KYB documents:

Note: Use Title Case format exactly as shown below (e.g., "Board Resolution", "Company Registration"). The system automatically converts these to internal format.

Responses

Code Description Schema
200 Document uploaded successfully Success object
400 Invalid doc_type Error array
422 Validation error Error array

POST Upload UBO Documents

// uploadUBODocuments.js
const axios = require("axios");
const FormData = require("form-data");
const fs = require("fs");
const generateHeaders = require("./generateHeaders");

uploadUBODocuments = async (uboUid) => {
  const baseUrl = "https://trade.capecrypto.com";
  const path = `/api/v2/verification/customers/documents`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  // Document 1: ID Front (Passport example)
  const formData1 = new FormData();
  formData1.append("upload", fs.createReadStream("./ubo_passport.jpg"));
  formData1.append("doc_type", "Passport");
  formData1.append("customer_uid", uboUid);
  formData1.append("doc_number", "M98765432");
  formData1.append("doc_expire", "2029-06-30");

  try {
    await axios.post(url, formData1, {
      headers: { ...headers, ...formData1.getHeaders() },
    });
    console.log("UBO ID uploaded successfully");
  } catch (err) {
    console.log("Error uploading UBO ID:", err);
  }

  // Document 2: Selfie
  const formData2 = new FormData();
  formData2.append("upload", fs.createReadStream("./ubo_selfie.jpg"));
  formData2.append("doc_type", "Selfie");
  formData2.append("customer_uid", uboUid);

  try {
    await axios.post(url, formData2, {
      headers: { ...headers, ...formData2.getHeaders() },
    });
    console.log("UBO Selfie uploaded successfully");
  } catch (err) {
    console.log("Error uploading UBO selfie:", err);
  }

  // Document 3: Proof of Address
  const formData3 = new FormData();
  formData3.append("upload", fs.createReadStream("./ubo_proof_of_address.pdf"));
  formData3.append("doc_type", "Proof of Address");
  formData3.append("customer_uid", uboUid);

  try {
    await axios.post(url, formData3, {
      headers: { ...headers, ...formData3.getHeaders() },
    });
    console.log("UBO Proof of Address uploaded successfully");
  } catch (err) {
    console.log("Error uploading UBO proof of address:", err);
  }
};

// Upload documents for each UBO
// UBO UIDs are returned when creating the business customer
uploadUBODocuments("ID_UBO_001_ABC");
uploadUBODocuments("ID_UBO_002_XYZ");

/api/v2/verification/customers/documents

Description

Upload KYC verification documents for Ultimate Beneficial Owners (UBOs) of business customers. Each UBO must complete individual KYC verification by uploading their personal documents. This is required in addition to the business entity documents.

When creating a business customer, UBO user accounts are automatically created and their UIDs are returned in the creation response. Use these UBO UIDs as the customer_uid when uploading UBO documents.

Business KYB completion requires:

  1. All 9 business entity documents uploaded
  2. All UBOs must have complete KYC documents

Each document must be uploaded as a separate request with FormData.

Maximum file size: 4MB per document.

Parameters

Name Located in Description Required Schema
upload formData The document file (image file: JPG, PNG, PDF) - Max 10MB Yes File
doc_type formData Document type (see Required UBO Documents below) Yes string
customer_uid formData UBO's UID from the business customer creation response Yes string
doc_number formData ID document number (optional for selfie) No string
doc_expire formData Document expiry date in YYYY-MM-DD format No string

Required UBO Documents

Each UBO must upload the following documents:

  1. ID Document (Front) - Front image of the identification document
  1. ID Document (Back) - Back image (Identity card and Driver license only)
  1. Selfie - Photograph of UBO holding their ID document
  1. Proof of Address - Utility bill, bank statement, or lease agreement

Available Document Types for UBOs

Valid doc_type values for UBO KYC documents (same as individual customers):

Responses

Code Description Schema
200 Document uploaded successfully Success object
400 Invalid doc_type Error array
422 Validation error Error array

Business KYB Completion

When all requirements are met:

The system will:

POST Create payment reference

// createPaymentReference.js
const axios = require("axios");

const url = "https://trade.capecrypto.com/api/v2/atlas/account/payments";

const createPaymentReference = async () => {
  const headers = {
    "Content-Type": "application/json",
    "X-Auth-Apikey": "your_api_key",
    "X-Auth-Nonce": Date.now(),
    "X-Auth-Signature": "your_generated_signature",
  };

  const paymentData = {
    uid: "ID123456789",
    currency: "zar",
    expected_amount: "100"
    deposit_action: {
      action: "aggregate_convert_and_withdraw",
      target_currency: "zar",
      beneficiary_id: 456,
      max_slippage: 0.5,
    },
    webhook_url: "https://merchant.com/webhooks/deposits",
    webhook_secret: "my_secret_key",
    webhook_protocol: "hmac",
  };

  const axiosConfig = {
    method: "post",
    url: url,
    headers: headers,
    data: paymentData,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

createPaymentReference();

/api/v2/account/payments

Description

Create a new payment reference for a customer account. The payment reference provides a unique deposit reference code that the customer can use when making deposits. Optionally configure automated actions (aggregate, convert, withdraw) that execute when deposits are received. You can optionally provide a webhook url, with optional secret and protocol that will be sent as a header with the webhook notification.

Access: Merchants and Aggregators only

Webhook Notifications: When the configured action completes successfully, a webhook notification will be sent to the provided webhook URL (optional).

When the configured action completes successfully, a webhook notification will be sent to the provided webhook URL (optional).

Request Details

Authentication / Protocol Modes (webhook_protocol)

webhook_protocol Behavior Header X-Auth-Webhook contains
hmac (recommended) The raw request body is signed with HMAC-SHA256 using your webhook_secret. The signature is base64-encoded. Base64(HMAC-SHA256(body, webhook_secret))
plain_text The secret itself is sent in clear text (not recommended for production) The raw webhook_secret value
not set (default) No authentication header is added Header is omitted

Successful Response Requirement

Your endpoint must return an HTTP 2xx status code (e.g., 200, 201, 204) as quickly as possible.

Attempt Delay Notes
1 30 seconds Initial retry after first failure
2 60 seconds
3 5 minutes
4 10 minutes
5 30 minutes
6 1 hour
7 2 hours Final retry attempt

Recommendations

Webhook Verification

When you receive a webhook notification, you should verify its authenticity before processing.

For webhook_protocol: "hmac", the payload is HMAC signed and BASE64 encoded (BASE64(HMAC(payload, secret))), and then sent. Validate the sent HMAC signature by repeating what was done by Cape Crypto: HMAC sign the received body with your secret, then encode the resulting HMAC signature to BASE64, and finally compare the received BASE64 encoded signture with your calculated BASE64 encoded signature.

Warning: Plain text mode is not recommended for production as the secret is transmitted in clear text.

No Authentication Mode

For webhook_protocol: "none" or when not set, no X-Auth-Webhook header is sent. Consider implementing additional security measures like IP whitelisting or using webhook signing URLs.

Parameters

Name Located in Description Required Schema
uid body Customer sub-account UID Yes string
currency body Currency code (e.g., "btc", "zar") Yes string
expected_amount body Expected amount the customer will deposit (e.g., "1000") No string
deposit_action body Automated action configuration (see Deposit Action Object below) No object
webhook_url body Webhook URL for notifications No string
webhook_secret body Webhook secret for HMAC signing No string
webhook_protocol body Webhook protocol: "hmac", "plain_text", or "none" (default: "none") No string

Deposit Action Object:

Field Description Required Schema
action Action type: "none", "aggregate", "convert_and_withdraw", "aggregate_convert_and_withdraw" Yes string
target_currency Target currency for conversion (required if action includes conversion) Conditional string
beneficiary_id Beneficiary ID for withdrawal (required if action includes withdrawal) Conditional integer
lightning_invoice Lightning invoice for withdrawal (alternative to beneficiary_id) No string
max_slippage Maximum slippage percentage for conversion (default: 1.0, max: 5.0) No decimal

Deposit Action Types:

Responses

Code Description Schema
201 Payment reference created PaymentReference
403 Forbidden (not merchant/aggregator) Error array
422 Validation error Error array

Example Response:

API Response - Payment Reference Created:

// Request Body examples - POST /api/v2/account/payments
{
  "id": 123,
  "reference": "PAY-ABC123",
  "uid": "ID123456789",
  "currency": "btc",
  "deposit_action": {
    "action": "aggregate_convert_and_withdraw",
    "target_currency": "zar",
    "beneficiary_id": 456,
    "lightning_invoice": null,
    "max_slippage": 0.5
  },
  "state": "pending_deposit",
  "webhook_url": "https://merchant.com/webhooks/deposits",
  "webhook_protocol": "hmac",
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:30:00Z"
}

Additional Request Examples

Example: Simple Deposit (No Action)

{
  "uid": "ID123456789",
  "currency": "btc"
}

Example: Aggregate Only

{
  "uid": "ID123456789",
  "currency": "btc",
  "deposit_action": {
    "action": "aggregate"
  }
}

Example: Convert and Withdraw (Lightning)

{
  "uid": "ID123456789",
  "currency": "btc",
  "deposit_action": {
    "action": "convert_and_withdraw",
    "target_currency": "btc",
    "lightning_invoice": "lnbc100n1...",
    "max_slippage": 1.0
  },
  "webhook_url": "https://merchant.com/webhooks/deposits",
  "webhook_secret": "my_secret_key",
  "webhook_protocol": "hmac"
}

Webhook Payload Examples

When a payment reference progresses through its lifecycle, webhook notifications are sent to your configured webhook_url with different event payloads. Below are the four event types and their payload structures.


Event 1: Deposit Matched

Event Name: payment_reference.deposit_matched

When Triggered: When a deposit is successfully linked to the payment reference

Payload:

// Webhook Payload - POST to your webhook_url
{
  "event": "payment_reference.deposit_matched",
  "payment_reference_id": 123,
  "reference": "PAY-ABC123",
  "uid": "ID123456789",
  "deposit_id": 456,
  "amount": "100.00",
  "currency": "usd",
  "deposit_blockchain": "ethereum-erc20",
  "txid": "0xabc123def456...",
  "invoice": "lnbc1000u1...",
  "created_at": "2025-01-17T10:00:00.000Z"
}

Field Notes:


Event 2: Completed

Event Name: payment_reference.completed

When Triggered: When all configured actions (aggregate, convert, withdraw) complete successfully

Payload:

// Webhook Payload - POST to your webhook_url
{
  "event": "payment_reference.completed",
  "payment_reference_id": 123,
  "reference": "PAY-ABC123",
  "uid": "ID123456789",
  "deposit_id": 456,
  "internal_transfer_id": 202,
  "order_id": 789,
  "withdraw_id": 101,
  "deposit_blockchain": "ethereum-erc20",
  "invoice": "lnbc1000u1...",
  "withdraw_blockchain": "bitcoin",
  "created_at": "2025-01-17T10:05:00.000Z"
}

Field Notes (Conditional):


Event 3: Failed

Event Name: payment_reference.failed

When Triggered: When processing encounters a permanent failure

Payload:

// Webhook Payload - POST to your webhook_url
{
  "event": "payment_reference.failed",
  "payment_reference_id": 123,
  "reference": "PAY-ABC123",
  "uid": "ID123456789",
  "deposit_id": 456,
  "error": "Insufficient liquidity for market order on btcusd",
  "deposit_blockchain": "ethereum-erc20",
  "invoice": "lnbc1000u1...",
  "created_at": "2025-01-17T10:10:00.000Z"
}

Field Notes:


Event 4: Duplicate Deposit

Event Name: payment_reference.duplicate_deposit

When Triggered: When a second deposit is received for a payment reference that already has a deposit linked

Payload:

// Webhook Payload - POST to your webhook_url
{
  "event": "payment_reference.duplicate_deposit",
  "reference": "PAY-ABC123",
  "member_uid": "ID123456789",
  "currency_id": "usd",
  "amount": "100.00",
  "original_deposit_id": 456,
  "duplicate_deposit_id": 789,
  "duplicate_count": 1,
  "timestamp": "2025-01-17T10:15:00.000Z"
}

Field Notes:


GET List payment references

// listPaymentReferences.js
const axios = require("axios");

const url = "https://trade.capecrypto.com/api/v2/atlas/account/payments";

const listPaymentReferences = async () => {
  const headers = {
    "X-Auth-Apikey": "your_api_key",
    "X-Auth-Nonce": Date.now(),
    "X-Auth-Signature": "your_generated_signature",
  };

  const axiosConfig = {
    method: "get",
    url: url,
    headers: headers,
    params: {
      page: 1,
      limit: 50,
    },
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

listPaymentReferences();

/api/v2/account/payments

Description

List all payment references for your customer accounts. Returns paginated results.

Access: Merchants and Aggregators only

Acting as a Customer: Provide uid parameter to list payment references for that customer sub-account. The customer must belong to your merchant account.


Parameters

Name Located in Description Required Schema
uid query Customer sub-account UID No string
page query Page number (default: 1) No integer
limit query Results per page (max: 100) No integer

Responses

Headers:

Header Description
Total Total number of records
Page Current page number
Per-Page Records returned per page
Code Description Schema
200 Payment references retrieved Array of PaymentReference
403 Forbidden (not merchant/aggregator) Error array
422 Validation error Error array

Example Response:

[
  {
    "id": 123,
    "reference": "PAY-ABC123",
    "uid": "ID123456789",
    "currency": "btc",
    "deposit_action": {
      "action": "convert_and_withdraw",
      "target_currency": "zar",
      "beneficiary_id": 456,
      "max_slippage": 0.5
    },
    "state": "pending_deposit",
    "webhook_url": "https://merchant.com/webhooks/deposits",
    "created_at": "2024-01-15T10:30:00Z",
    "updated_at": "2024-01-15T10:30:00Z"
  }
]

GET Get payment reference by id

// showPaymentReference.js
const axios = require("axios");

const paymentId = 123;
const url = `https://trade.capecrypto.com/api/v2/atlas/account/payments/${paymentId}`;

const showPaymentReference = async () => {
  const headers = {
    "X-Auth-Apikey": "your_api_key",
    "X-Auth-Nonce": Date.now(),
    "X-Auth-Signature": "your_generated_signature",
  };

  const axiosConfig = {
    method: "get",
    url: url,
    headers: headers,
    params: {
      includeSecret: true, // Optional: include webhook secret
    },
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

showPaymentReference();

/api/v2/account/payments/:id

Description

Retrieve details of a specific payment reference.

Access: Merchants and Aggregators only


Parameters

Name Located in Description Required Schema
id path Payment reference ID Yes integer
includeSecret query Include webhook secret in response (default: false) No boolean

Responses

Code Description Schema
200 Payment reference retrieved PaymentReference
403 Forbidden (not merchant/aggregator) Error array
404 Payment reference not found Error array

Example Response (with includeSecret=true):

{
  "id": 123,
  "reference": "PAY-ABC123",
  "uid": "ID123456789",
  "currency": "btc",
  "deposit_action": {
    "action": "aggregate_convert_and_withdraw",
    "target_currency": "zar",
    "beneficiary_id": 456,
    "lightning_invoice": null,
    "max_slippage": 0.5
  },
  "state": "completed",
  "webhook_url": "https://merchant.com/webhooks/deposits",
  "webhook_secret": "wh_secret_abc123xyz",
  "webhook_protocol": "hmac",
  "deposit_id": 789,
  "order_id": 1011,
  "withdraw_id": 1213,
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T14:45:00Z"
}

Merchant Management (Aggregators Only)

Note: These endpoints are only available for Aggregators to manage their merchant accounts.

GET List Merchants

// listMerchants.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

listMerchants = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "GET";
  const path = `/api/v2/persona/resource/merchants?page=1&limit=100`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

listMerchants();

/persona/resource/merchants

Description

Get a paginated list of merchant accounts managed by the aggregator.

Parameters

Name Located in Description Required Schema
page query Page number (defaults to 1) No integer
limit query Number of results per page (default: 100, max: 1000) No integer

Responses

Code Description Schema
200 List of merchants with pagination Array of merchants (see Customer (Business))

Note: Pagination information is returned in HTTP headers (Page, Per-Page, Total), not in the response body.

GET List Merchant's Customers

// listMerchantCustomers.js
const axios = require("axios");
const generateHeaders = require("./generateHeaders");

listMerchantCustomers = async () => {
  const baseUrl = "https://trade.capecrypto.com";
  const method = "GET";
  const merchantUid = "CCT1234567";
  const path = `/api/v2/persona/resource/merchants/${merchantUid}/customers?page=1&limit=100`;
  const url = `${baseUrl}${path}`;
  const headers = await generateHeaders();

  const axiosConfig = {
    method: method,
    url: url,
    headers: headers,
  };

  try {
    const results = await axios(axiosConfig);
    console.log(JSON.stringify(results.data, undefined, 2));
  } catch (err) {
    console.log(err);
  }
};

listMerchantCustomers();

/persona/resource/merchants/{uid}/customers

Description

Get a paginated list of customers belonging to a specific merchant. This provides a nested view for aggregators to see which customers belong to which merchant.

Parameters

Name Located in Description Required Schema
uid path Merchant identifier Yes string
page query Page number (defaults to 1) No integer
limit query Number of results per page (default: 100, max: 1000) No integer

Responses

Code Description Schema
200 List of merchant's customers with pagination [ Customer ]
404 Merchant not found Error array

Note: Pagination information is returned in HTTP headers (Page, Per-Page, Total), not in the response body.

Trading

POST Create Order

/market/orders

Description

Submits a Buy/Sell order to the order book. Orders should be confirmed that they have been created either by querying the order ID that is returned in the response, or subscribing to the websockets for order updates.

You can supply your own custom_order_id for tracking purposes for you own system.

For limit orders, you can specify post_only to ensure that orders wont complete if they would have matched. The order request will fail if post_only is true, and the order would have matched.

Parameters

Name Description Required Schema
market The market name eg 'btczar' Yes string
side The direction of the order eg buy Yes string
volume The order size in base currency Yes string
ord_type Limit / Market Yes string
price The limit order price No string
post_only Fail limit order if matched No boolean
custom_order_id Your custom order id No string

Responses

Code Description Schema
201 Order submitted Order

GET Order by id

/market/orders/{id}

Description

Get single order details.

Parameters

Name Located in Description Required Schema
id path Yes string

Responses

Code Description Schema
200 Order details. Order

GET All orders

/market/orders

Description

Get orders, result is paginated.

Parameters

Name Located in Description Required Schema
market query No string
base_unit query No string
quote_unit query No string
state query Filter order by state. No string
limit query Limit the number of returned orders, default to 100. No integer
page query Paginated page number. No integer
order_by query If set, returned orders will be sorted in specific order, default to "desc". No string
ord_type query Filter order by ord_type. No string
type query Filter order by type. No string
time_from query Timestamp in ms.If set, only orders created after the time will be returned. No integer
time_to query Timestamp in ms.If set, only orders created before the time will be returned. No integer

Responses

Code Description Schema
200 Get your orders, result is paginated. [ Order ]

GET Trades

/market/trades

Description

Fetch your trades executed on your orders.

Parameters

Name Located in Description Required Schema
market query No string
order_id query Get the trades for a specific order_id No integer
limit query Limit the number of returned trades. Default to 100. No integer
page query Paginated page number. No integer
time_from query Timestamp in ms. Return trades executed after the timestamp. No integer
time_to query Timestamp in ms. Return trades executed after the timestamp. No integer
order_by query Order by created_at, defaults to 'desc'. [asc desc] No string

Responses

Code Description Schema
200 Get your executed trades.. [ Trade ]

POST Cancel Order

/market/orders/{id}/cancel

Description

Cancel a specific order by Order id.

Parameters

Name Located in Description Required Schema
id path Yes string

Responses

Code Description
201 Cancel an order.

POST Cancel All Orders

/market/orders/cancel

Description

Cancel all of your open orders.

Parameters

Name Located in Description Required Schema
market formData No string
side formData Only cancel orders on this side [buy sell] No string

Responses

Code Description Schema
201 Cancel all my orders. Order

Websocket

// generateHeaders.js

const crypto = require("crypto");
const apiKey = "123456...";
const secret = "ABCDEF...";

module.exports = async () => {
  const timeStamp = new Date().getTime();
  const queryString = `${timeStamp}${apikey}`;
  const signature = crypto
    .createHmac("sha256", secret)
    .update(queryString)
    .digest("hex");

  return {
    "Content-Type": "application/json;charset=utf-8",
    "X-Auth-Apikey": apikey,
    "X-Auth-Nonce": timeStamp,
    "X-Auth-Signature": signature,
  };
};

The high performance websocket provides near realtime updates on the orderbook, your orders and balances.

The orderbook endpoint does not require authenticaiton, while the private endpoints (orders, trades, and balances) do require authentication.

To connect to the private endpoints on the websocket, send the authentication headers with the connection request, similar to the REST authentication, and subscribe to the streams you are interested in. You can combine multiple subscriptions in one request, and also provide the subscriptions at the same time as connecting - ie. you only need 1x request to get fully connected.

You can also combine the public and private streams into one so that you only need to maintain 1x connection for example ?stream=btczar.ob-inc&stream=order&stream=trade&stream=balance

SUBSCRIBE Orderbook

const WebSocket = require("ws");

async function connectWssPublic() {
  try {
    const baseUrl = `wss://trade.capecrypto.com/api/v2`;
    const streams = "?stream=btczar.ob-inc&stream=ethzar.ob-inc";
    const path = `/websocket/public${streams}`;

    ws = new WebSocket(`${baseUrl}${path}`);
  } catch (err) {
    console.log(err);
  }

  ws.on("open", () => {
    console.log("Connected to websocket");
  });

  ws.on("message", function message(data) {
    data = JSON.parse(data);
    console.log(JSON.stringify(data, undefined, 2));

    if (["ob-snap", "ob-inc"].includes(data)) {
      // handle all subscribed markets
      // updateOrderBook(data);
    }
  });
}

connectWssPublic();

?stream={market}.ob-inc

Description

Subscribe to orderbook updates for the specified market. You can subscribe to multiple markets at the same time by adding each market as a param, eg

?stream=btczar.ob-inc&ethzar.ob-inc

Note: if the amount is empty "", the order has been removed from the orderbook

Parameters

Name Located in Description Required Schema
{market}-ob.inc query Name of the market to subscribe to yes string

Responses

Description Schema
snapshot [ Snapshot ]
increment [ Increment ]

SUBSCRIBE K-Line

const WebSocket = require("ws");

async function connectWssPublic() {
  try {
    const baseUrl = `wss://trade.capecrypto.com/api/v2`;
    const streams = "?stream=btczar.kline-15m";
    const path = `/websocket/public${streams}`;

    ws = new WebSocket(`${baseUrl}${path}`);
  } catch (err) {
    console.log(err);
  }

  ws.on("open", () => {
    console.log("Connected to websocket");
  });

  ws.on("message", function message(data) {
    data = JSON.parse(data);
    console.log(JSON.stringify(data, undefined, 2));

    if (["kline-"].includes(data)) {
      // handle all subscribed markets
      // updateOrderBook(data);
    }
  });
}

connectWssPublic();

?stream={market}.kline-{period}

Description

Subscribe to the K-LINE (OHLC) for the specified market to get updates on the OHLC for each period. Similary to the otherendpoints, you can combine streams into a single subscription request.

Parameters

Name Located in Description Required Schema
{market} query Name of the market to subscribe to yes string
{period} query The period to get updates at "1m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "12h", "1d", "3d", "1w" yes string

Responses

Name Type Description
{market}.kline-{period} object Key name of the OHLC for the market and period
OHLC for that period [number] Array of Timestamp, Open, High, Low, Close, Volume

SUBSCRIBE Private

async function connectWssPrivate() {
  try {
    const baseUrl = `wss://trade.capecrypto.com/api/v2`;
    const headers = await generateHeaders();
    const streams =
      "?stream=btczar.ob-inc&stream=ethzar.ob-inc&stream=order&stream=trade&stream=balance";
    const path = `/websocket/private${streams}`;

    ws = new WebSocket(`${baseUrl}${path}`, { headers });
  } catch (err) {
    console.log(err);
  }

  ws.on("open", () => {
    console.log("Connected to websocket");
  });

  ws.on("message", (data) => {
    data = JSON.parse(data);
    console.log(JSON.stringify(data, undefined, 2));

    if (data["xrpzar.ob-snap"] || data["xrpzar.ob-inc"]) {
      // handle orderbook updates for subscribed markets
      // updateOrderBook(data);
    }
    if (data["order"]) {
      // handleOwnOrderUpdate(data);
    }
    if (data["trade"]) {
      // handleOwnTradeUpdate(data);
    }
    if (data["balance"]) {
      // handleBalanceUpdate(data);
    }
  });
}

connectWssPrivate();

Subscribe to your account and receive real-time updates on your orders, trades, and balances.

Important: You can subscribe to ALL streams (both public and private) in a single WebSocket connection using the /websocket/private endpoint. This is the recommended approach as it minimizes connection overhead and provides a unified stream of all market events.

Private streams only:

?stream=order&stream=trade&stream=balance

Combining public orderbook + private streams (recommended):

?stream=btczar.ob-inc&stream=order&stream=trade&stream=balance

Benefits of combining streams:

Order Stream

Subscription: ?stream=order

Receive real-time updates when your orders are created, updated, filled, or canceled.

Response Format:

{
  "order": {
    "id": 173757,
    "uuid": "7e86a4fa-fcae-4953-b9bc-d6f328beea48",
    "side": "sell",
    "ord_type": "limit",
    "price": "800000.0",
    "avg_price": "0.0",
    "state": "wait",
    "market": "btczar",
    "created_at": "2023-12-08T08:46:59+01:00",
    "updated_at": "2023-12-08T08:46:59+01:00",
    "origin_volume": "0.00001",
    "remaining_volume": "0.00001",
    "executed_volume": "0.0",
    "trades_count": 0
  }
}

Order Update Triggers:

Trade Stream

Subscription: ?stream=trade

Receive real-time updates when your orders are matched and trades are executed.

Response Format:

{
  "trade": {
    "id": 12345,
    "order_id": 173757,
    "price": "800000.0",
    "amount": "0.00001",
    "total": "8.0",
    "market": "btczar",
    "side": "sell",
    "taker_type": "buy",
    "fee_currency": "zar",
    "fee": "0.002",
    "fee_amount": "0.016",
    "created_at": "2023-12-08T08:47:01+01:00"
  }
}

Trade Event Triggers:

Balance Stream

Subscription: ?stream=balance

Receive real-time updates when your account balances change. Balance updates are triggered by trading activity, order placement, and order cancellation.

Response Format:

{
  "balance": {
    "currency": "btc",
    "balance": "0.05000000",
    "locked": "0.00001000",
    "available": "0.04999000",
    "updated_at": 1702027621000
  }
}

Response Fields:

Field Type Description
currency string Currency code (btc, eth, zar, usdt, etc.)
balance string Total balance (available + locked). String for decimal precision
locked string Amount currently locked in open orders
available string Available balance for trading (balance - locked)
updated_at integer Unix timestamp in milliseconds when balance was updated

Balance Update Triggers:

  1. Order Creation - Locks balance when a limit or market order is placed
  2. Order Cancellation - Unlocks balance when an order is canceled
  3. Trade Execution (Maker) - Updates both base and quote currency balances
  4. Trade Execution (Taker) - Updates both base and quote currency balances with fee deduction
  5. Partial Fill - Each partial fill triggers separate balance updates
  6. Order Rejection - Balance unlock if order validation fails after initial lock

Example 1: Order Creation (Balance Lock)

When you place a buy order for 0.001 BTC @ 800,000 ZAR, your ZAR balance is locked:

{
  "balance": {
    "currency": "zar",
    "balance": "10000.00",
    "locked": "800.00",
    "available": "9200.00",
    "updated_at": 1702027621000
  }
}

Example 2: Order Cancellation (Balance Unlock)

When you cancel the order, the locked ZAR is released:

{
  "balance": {
    "currency": "zar",
    "balance": "10000.00",
    "locked": "0.00",
    "available": "10000.00",
    "updated_at": 1702027625000
  }
}

Example 3: Trade Execution - Sell Order (Base Currency)

When your sell order for 0.00001 BTC executes, you receive two balance updates. First, the BTC is deducted:

{
  "balance": {
    "currency": "btc",
    "balance": "0.04999000",
    "locked": "0.00000000",
    "available": "0.04999000",
    "updated_at": 1702027630000
  }
}

Example 4: Trade Execution - Sell Order (Quote Currency)

Immediately after, you receive ZAR (minus the taker fee of 0.2%):

{
  "balance": {
    "currency": "zar",
    "balance": "10007.984",
    "locked": "0.00",
    "available": "10007.984",
    "updated_at": 1702027630001
  }
}

Example 5: Partial Fill

When your order is partially filled (50% of 0.001 BTC order):

{
  "balance": {
    "currency": "btc",
    "balance": "0.04999500",
    "locked": "0.00000500",
    "available": "0.04999000",
    "updated_at": 1702027635000
  }
}

Important Notes:

Edge Cases:

Integration Example:

ws.on("message", (data) => {
  data = JSON.parse(data);

  if (data["balance"]) {
    const { currency, balance, locked, available, updated_at } = data.balance;

    console.log(`Balance Update: ${currency}`);
    console.log(`  Total: ${balance}`);
    console.log(`  Locked: ${locked}`);
    console.log(`  Available: ${available}`);
    console.log(`  Updated: ${new Date(updated_at).toISOString()}`);

    // Update your UI
    updateBalanceDisplay(currency, available, locked);
  }

  if (data["order"]) {
    // Handle order updates
    handleOrderUpdate(data.order);
  }

  if (data["trade"]) {
    // Handle trade updates - expect balance updates to follow immediately
    handleTradeUpdate(data.trade);
  }
});

Models

Pagination

All paginated list endpoints return an array of items in the response body, with pagination information provided in HTTP response headers:

Header Description Example
Page Current page number 1
Per-Page Number of items per page 100
Total Total number of items across all pages 150

Ticker

Name Type Description
at integer Timestamp of ticker
ticker TickerEntry Ticker entry for specified time

TickerEntry

Name Type Description
low double The lowest trade price during last 24 hours (0.0 if no trades executed during last 24 hours)
high double The highest trade price during last 24 hours (0.0 if no trades executed during last 24 hours)
open double Price of the first trade executed 24 hours ago or less
last double The last executed trade price
volume double Total volume of trades executed during last 24 hours
amount double Total amount of trades executed during last 24 hours
vol double Alias to volume
avg_price double Average price more precisely VWAP (volume weighted average price)
price_change_percent string Price change over the last 24 hours
at integer Timestamp of ticker

Orderbook entry

Name Type Description
price number The price of the order
amount number The amount of the order at that price. Note: if the amount is empty "", the order has been removed from the orderbook

Trade

Name Type Description
id string Trade ID.
price double Trade price.
amount double Trade amount.
total double Trade total (Amount * Price).
fee_currency double Currency user's fees were charged in.
fee double Percentage of fee user was charged for performed trade.
fee_amount double Amount of fee user was charged for performed trade.
market string Trade market id.
created_at string Trade create time in iso8601 format.
taker_type string Trade taker order type (sell or buy).
side string Trade side.
order_id integer Order id.
custom_order_id integer Optional custom Order id if supplied.

Order

Name Type Description
id integer Unique order id.
custom_order_id string Custom order id if supplied during order creation.
side string Either 'sell' or 'buy'.
ord_type string Type of order, either 'limit' or 'market'.
post_only boolean If the Limit order was submitted as post_only .
price double Price for 1x base unit.
avg_price double Average execution price over all executed trades.
state string Current state of the order Order states
market string The market in which the order is placed, e.g. 'btczar'.
created_at string Order create time in iso8601 format.
updated_at string Order updated time in iso8601 format.
origin_volume double The original amount of the order.
remaining_volume double The remaining amount of the original order volume.
executed_volume double The executed volume of the original order volume.
trades_count integer Number of trades executed against this limit order.
trades [Trade] Array of trades executed against this order.

Market

Name Type Description
id string Unique market id eg 'btczar'
name string Market name.
base_unit string Market Base unit.
quote_unit string Market Quote unit.
min_price double Minimum order price.
max_price double Maximum order price.
min_amount double Minimum order amount.
amount_precision double Precision for order amount.
price_precision double Precision for order price.
state string If the market is enabled for trading or not.

Currency

Name Type Description
id string Currency code.
name string Currency name
symbol string Currency symbol
explorer_transaction string Currency transaction exprorer url template
explorer_address string Currency address exprorer url template
type string Currency type
deposit_enabled string Currency deposit possibility status (true/false).
withdrawal_enabled string Currency withdrawal possibility status (true/false).
deposit_fee string Currency deposit fee
min_deposit_amount string Minimal deposit amount
withdraw_fee string Currency withdraw fee
min_withdraw_amount string Minimal withdraw amount
withdraw_limit_24h string Currency 24h withdraw limit
withdraw_limit_72h string Currency 72h withdraw limit
base_factor string Currency base factor
precision string Currency precision
position string Position used for defining currencies order
icon_url string Currency icon
min_confirmations string Number of confirmations required for confirming deposit or withdrawal
layer_two_withdraw_fee string (Layer two dependant) The base layer two withdraw fee
layer_two_max_deposit_amount string (Layer two dependant) The maximum deposit amount
layer_two_max_withdraw_amount string (Layer two dependant) The maxumum withdrawal amount

Withdraw

Name Type Description
id integer The withdrawal id.
currency string The currency code.
type string The withdrawal type
amount string The withdrawal amount
fee double The exchange fee.
blockchain_txid string The withdrawal transaction id.
rid string The beneficiary ID or wallet address on the Blockchain.
state string The withdrawal state.
confirmations integer Number of confirmations.
note string Optional withdraw note.
created_at string The datetime for the withdrawal.
updated_at string The datetime for the withdrawal.
done_at string The datetime when withdraw was completed

Withdraw quote

Name Type Description
id string The id of the quote. Submit this with your withdrawal request
currency string The currency code.
fee string The fee for the withdrawal
blockchain string The blockchain for this quote
created_at string The datetime the quote was created.
expires_at string The datetime the quote expires.

Beneficiary

Name Type Description
uuid integer Beneficiary Identifier
currency string Beneficiary currency code.
name string Name of the beneficiary.
description string A personal description of the beneficiary for your records.
data json Bank Account details for FIAT Beneficiary in JSON format. For crypto it's blockchain address and or tag, as well as the travel rule information
state string Pending, Active, Archived (0,1,2)

SubAccount

Name Type Description
uid string Sub-account unique identifier
account_name string Display name for the sub-account
state string Account state (active, deleted)
deposit_action string Action for deposits: 'none' (keep in sub-account) or 'transfer_to_parent' (auto-transfer)
created_at string Timestamp when the sub-account was created in ISO 8601 format

Customer

Customer accounts come in two types with different response structures:

Customer (Individual)

Name Type Description
uid string Customer unique identifier
account_name string Display name for the customer (usually custom_id)
state string Account state (active, deleted)
custom_id string Custom identifier set by merchant/aggregator
external_verification_url string URL for external verification system
kyc_status string KYC verification status (verified, pending, rejected)
created_at string Timestamp when customer was created in ISO 8601 format
profile object Customer profile information (first_name, last_name, dob, address, etc.)
phone object Phone information (country, number, validated_at)

Customer (Business)

Name Type Description
id integer Customer database ID
uid string Customer unique identifier
email string Business email address
role string Account role (customer)
entity_type string Entity type (business)
level integer Account level
state string Account state (active, pending, deleted)
custom_id string Custom identifier set by merchant/aggregator
business_profile object Business details (legal_name, trading_name, registration_number, tax_id, incorporation_date, business_type, industry, address, city, postcode, country, website)
ubos array Array of Ultimate Beneficial Owners with ownership details
created_at string Timestamp when customer was created in ISO 8601 format
updated_at string Timestamp when customer was last updated in ISO 8601 format

Deposit

Name Type Description
id integer Unique deposit id.
blockchain string Blockchain id.
currency string Deposit currency id.
amount double Deposit amount.
fee double Deposit fee.
txid string Deposit transaction id.
confirmations integer Number of deposit confirmations.
state string Deposit state.
created_at string The datetime when deposit was created.
completed_at string The datetime when deposit was completed..
tid string The shared transaction ID

Transaction

Name Type Description
id integer Unique transaction identifier
type string Transaction type (deposit, withdraw, internal_transfer, trade, referral, maker_reward)
reference_type string Related entity type (Deposit, Withdraw, Order, etc.)
reference_id integer Related entity ID
credit_currency string Currency received (if applicable)
credit_amount decimal Amount received
debit_currency string Currency sent (if applicable)
debit_amount decimal Amount sent
fee_currency string Fee currency
fee decimal Fee amount
blockchain_key string Blockchain identifier (for crypto transactions)
blockchain_name string Blockchain name
market_id string Market ID (for trades)
order_id string Order ID (for trades)
price decimal Trade price (for trades)
origin_volume decimal Original order volume (for trades)
payment_method_provider string Payment provider (for fiat transactions)
payment_method_name string Payment method name
payment_method_precision integer Decimal precision for payment method
payment_method_icon_url string Payment method icon URL
voucher_pin string Voucher PIN (if applicable)
change_voucher_pin string Change voucher PIN (if change was issued)
change_voucher_expiry string Change voucher expiry date (ISO8601)
change_voucher_amount decimal Change voucher amount
description string Transaction description
member_description string Your personal note for this transaction
sender string Sender information
receiver string Receiver information
state string Transaction state
address string Blockchain address (for crypto transactions)
txid string Transaction ID (blockchain txid or internal reference)
beneficiary_id integer Beneficiary ID (for withdrawals)
paid_at string Payment completion timestamp (ISO8601)
expires_at string Expiry timestamp (ISO8601, for vouchers)
created_at string Transaction creation timestamp (ISO8601)
updated_at string Last update timestamp (ISO8601)

VASP

Name Type Description
id integer Unique VASP identifier (use for vasp_id)
name string VASP name
url string VASP website URL
email string VASP contact email
state string VASP state (active, pending, rejected, requested)
icon_url string VASP icon/logo URL
created_at string ISO8601 timestamp of VASP registration

PaymentReference

Name Type Description
id integer Payment reference ID
reference string Unique payment reference code (used by customer for deposits)
uid string Customer sub-account UID
currency string Currency code (e.g., "btc", "zar")
expected_amount string Expected deposit amount (null if not specified)
deposit_action object Automated action configuration (null if not configured)
state string Current state (see PaymentReference States)
expires_at string ISO8601 expiration timestamp
usage_count integer Number of times the reference has been used
last_used_at string ISO8601 timestamp of last use (null if never used)
webhook_url string Webhook URL (only included if configured)
webhook_protocol string Webhook protocol: "hmac", "plain_text", or "none" (only if configured)
webhook_secret string Webhook secret (only included for owner with includeSecret option)
deposit_id integer Linked deposit ID (only included if linked)
order_id integer Linked order ID (only included if linked)
withdraw_id integer Linked withdrawal ID (only included if linked)
internal_transfer_id integer Linked internal transfer ID (only included if linked)
error string Error message (only included if state is "failed")
created_at string ISO8601 creation timestamp
updated_at string ISO8601 last update timestamp

Deposit Action Object Structure:

Field Type Description
action string Action type: "none", "aggregate", "convert_and_withdraw", "aggregate_convert_and_withdraw"
target_currency string Target currency for conversion (only for convert actions)
beneficiary_id integer Beneficiary ID for withdrawals (only for withdraw actions)
lightning_invoice string Lightning invoice for lightning withdrawals (alternative to beneficiary_id)
max_slippage number Maximum slippage percentage (0.1-10, default: 2.0)

PaymentReference States:

Payment references progress through various states based on the configured actions. Webhook notifications are sent when the reference reaches completed or failed state.

State Description
pending_deposit Waiting for deposit
pending_internal_transfer Processing aggregation
pending_buy_order Processing conversion (buy)
pending_sell_order Processing conversion (sell)
pending_withdraw Processing withdrawal
completed All actions completed successfully
failed Processing failed (see error field)

Account

Name Type Description
currency string Currency code.
balance double Account balance.
locked double Account locked funds.

Deposit Method

Name Type Description
uuid string Unique identifier for the deposit method
name string Display name of the deposit method
description string Human-readable description of the method
currencyId string Currency identifier (e.g., "btc", "zar")
symbol string Currency symbol (e.g., "BTC", "ZAR")
blockchainName string Blockchain network name (e.g., "bitcoin", "ethereum", "fiat")
type string Method type (e.g., "bank", "crypto", "voucher")
currencyType string Currency type: "fiat" or "crypto"
providerId string Payment provider identifier (e.g., "stitch", "xago", "cape")
minAmount number Minimum deposit amount (after baseFactor conversion)
maxAmount number Maximum deposit amount (derived from remaining limits)
baseFactor number Conversion factor (divide stored amounts by this for display)
precision number Decimal precision for display
feeCape number Cape's fee amount or percentage
feeProvider number Provider's fee amount or percentage
position number Display order position
iconUrl string Icon URL for UI display
verified boolean Whether user is verified for this method
remainingLimits object Remaining transaction limits (see Remaining Limits object below)

Withdraw Method

Name Type Description
uuid string Unique identifier for the withdraw method
id number Database ID (included for legacy support)
name string Display name of the withdraw method
description string Human-readable description of the method
currencyId string Currency identifier (e.g., "btc", "zar")
symbol string Currency symbol (e.g., "BTC", "ZAR")
blockchainName string Blockchain network name (e.g., "bitcoin", "ethereum", "fiat")
type string Method type (e.g., "crypto", "bank", "wallet")
currencyType string Currency type: "fiat" or "crypto"
providerId string Payment provider identifier (e.g., "xago", "celbux", "cape")
processorId string Processor that handles withdrawals for this method
minAmount number Minimum withdrawal amount (after baseFactor conversion)
maxAmount number Maximum withdrawal amount (derived from remaining limits)
baseFactor number Conversion factor (divide stored amounts by this for display)
precision number Decimal precision for display
feeCape number Cape's withdrawal fee amount or percentage
feeProvider number Provider's withdrawal fee amount or percentage
position number Display order position
iconUrl string Icon URL for UI display
verified boolean Whether user is verified for this method
remainingLimits object Remaining transaction limits (see Remaining Limits object below)

Remaining Limits

The remainingLimits object is included in both Deposit Method and Withdraw Method responses.

Name Type Description
transaction_max number Maximum amount allowed per single transaction
day number Remaining limit for the current day
week number Remaining limit for the current week
month number Remaining limit for the current month
year_to_date number Remaining limit for the current year

Voucher Redemption

Name Type Description
amount number The amount deposited after fees.
origin_amount number The original amount of the voucher.
change_voucher Change voucher Optional if provider offers this, a new voucher for the remaining amount

Change voucher

Name Type Description
change_amount number Amount of change from the redeemed voucher, available to be spent
change_expiry string Expiry date of the new voucher.
change_pin number The new voucher_pin to be used for redemption.

Lightning payment

Name Type Description
bolt String The invoice bolt11.
address String The address of the invoice if not bolt11.
amount number Invoice amount paid in SAT.
description String Description for this invoice in the bolt11.
member_description String Private member description for this invoice provided by member.
state String Payment state.
created_at String (ISO8601 formatted) The datetime when payment was created.

Lightning invoice

Name Type Description
bolt String The invoice in bolt11.
amount BigDecimal Invoice amount to be paid in SAT.
conversion_percent BigDecimal The amount to automatically convert to FIAT.
amount_msat BigDecimal Invoice amount to be paid in Mili SAT.
description String Description for this invoice.
member_description String Private member Description for this invoice.
state String Payment state.
created_at String (ISO8601 formatted) The datetime when invoice was created.
expires_at String (ISO8601 formatted) The datetime when invoice expires.
paid_at String (ISO8601 formatted) The datetime when invoice was paid.

Websocket orderbook snapshot

Name Type Description
{market}.ob-snap Object The object key for the subscription
asks [Orderbook entry] Array of ask / sell orderbook entries.
bids [Orderbook entry] Array of bid / buy orderbook entries.
sequence integer The current sequence of the orderbook, used to ensure updates are sequential

Websocket orderbook increment

Name Type Description
{market}.ob-inc Object The object key for the subscription
asks [Orderbook entry] Array of ask / sell orderbook entries.
bids [Orderbook entry] Array of bid / buy orderbook entries.
sequence integer The current sequence of the orderbook, used to ensure updates are sequential
{
  "btczar.ob-snap": {
    "asks": [
      ["841039.73", "0.156431"],
      ["845402.1", "0.004389"],
      ["900000.0", "0.00001"]
    ],
    "bids": [
      ["835956.8", "0.047756"],
      ["831012.48", "0.03749"],
      ["829990.51", "0.019462"],
      ["400000.0", "0.0001"]
    ],
    "sequence": 9712
  }
}

States

Order states Description
pending Initial state - order has been submitted (but is not yet placed / confirmed)
wait Order has been placed and is available on the orderbook
done Order has been completely filled
cancel The order has been cancelled (A cancelled limit order may still have executed trades against it)
reject The order submission was rejected
Deposit states
submitted
canceled
rejected
accepted
collected
skipped
Withdraw states
prepared
submitted
rejected
accepted
skipped
processing
succeed
canceled
failed
errored
confirming

Errors

Code Reason Description
400 Bad Request Your request is invalid.
401 Unauthorized Apikey / secret / HMAC signature incorrect
403 Forbidden You are authenticated, but unauthorized to access this endpoint.
404 Not Found Invalid endpoint.
405 Method Not Allowed Invalid method for endpoint eg POST instead of GET.
422 Unprocessible Entity The server understood the request, but the data provided is incorrect.
429 Too Many Requests You're sending too many requests too quickly. Slow down!
500 Internal Server Error We had a problem with our server. Try again later.
503 Service Unavailable We're temporarily offline for maintenance. Please try again later.