Getting Started
Introduction
Welcome to the official API documentation. This guide provides comprehensive information on how to interact with our services programmatically. Whether you're building a complex integration or automating simple tasks, you'll find the necessary details here.
Our API is designed following REST principles, using standard HTTP verbs, predictable resource-oriented URLs, and x-www-form-urlencoded responses. Authentication is handled via secure methods outlined in the Authentication section.
Quickstart Guide
Ready to make your first API call? Follow these steps:
- Obtain your API credentials (API Key and Secret) from your account settings.
- Review the Authentication methods, we recommend Signature Validation.
- Choose the appropriate Base URL (Sandbox or Production).
- Explore the API Endpoints section for available operations.
- Construct your request including necessary headers and parameters.
- Send the request and handle the JSON response.
Base URLs
While the main service URLs are the domains below, all API requests must be made to the specific
/api/ path on those domains:
-
Production (Live) API Base:
https://gate.columis.com/api/ -
Sandbox (Testing) API Base:
https://gate.columis.tech/api/
Endpoint paths listed later in this documentation (e.g., /payment) should be
appended to these base URLs.
Authentication
Overview
Authenticating your API requests is crucial for security. We support multiple methods, with Signature Validation being the recommended approach for server-to-server communication.
Generating API Tokens
To interact with the API, you first need API credentials. These credentials are used for both the recommended Signature Validation method and the older (deprecated) API Key + Secret method.
You can create and manage your API Tokens on the API Settings page in your account dashboard.
Generating a token will provide you with:
- API Key: Your public identifier. Used in the
X-API-Keyheader for Signature Validation. - API Secret: A confidential key. Do not send this directly for Signature Validation. Instead, you generate your own RSA key pair (see Signature Validation) and provide the public key in API Settings. The secret is used in the deprecated method.
- UUID: A unique identifier used only for the deprecated authentication method.
Signature Validation (Recommended)
When using request signature validation (the recommended method), each request must be
signed using your private RSA key. The necessary X-API-Key header value can be
found in your API Token details. We utilize the RSASSA-PSS
signing scheme with SHA-512 for hashing, and the final signature should be Base64 encoded.
Authorization Headers
X-API-Key: Your public API Key.X-API-Signature: The Base64 encoded signature of the hashed request body. See the Signature Validation (Recommended) section above for detailed instructions and code examples on how to generate this signature.
Required Body Parameter
- For POST requests: Include a
timestampfield with the current UNIX timestamp in the JSON body (must be included in the data used for hashing and signing). - For GET requests: Send an empty body (
{}) for signature verification. No timestamp field is required.
Generating RSA Keys with OpenSSL
1. Generate the Private Key
# Generate Private Key (PKCS#1)
openssl genrsa -out private.key 4096
2. Extract the Public Key
Extract the public key from your private key file:
# From PKCS#1 private key
openssl rsa -in private.key -outform PEM -pubout -out public.pub
Securely store your private key (`private.key`). You will need to provide the content of your public key file (`public.pub`) to our support team.
Generating the Request Signature
To create the X-API-Signature header:
- Construct your request body as a compact JSON string (no extra whitespace or unnecessary backslashes).
- For POST requests: Include a
timestampfield with the current UNIX timestamp in the JSON body. Ensure any included URLs (like callback URLs) are URL-encoded within the JSON string. - For GET requests: Use an empty JSON body (
{}) for signature verification. No timestamp field is required. - Generate a SHA-512 hash of the raw, compact JSON string.
- Get the **hexadecimal representation** of the SHA-512 hash.
- Sign the **hexadecimal hash string** using your private RSA key, specifying RSASSA-PSS padding and SHA-512 for both the hash algorithm and the MGF1 hash algorithm.
- Base64 encode the resulting binary signature.
Include the final Base64 encoded signature in the X-API-Signature header and your
API Key in the X-API-Key header.
Important:
- Add the generated signature to the
X-API-Signatureheader. The request will be verified on our end using the public key you provide to our support team. - Always use URL-encoded values for any URLs included in the request body JSON (e.g., callback URLs).
- Ensure no null values are included in the data used for signature generation (e.g., omit optional fields like `state_code` if they are null/empty, rather than including them with a null value).
Code Examples: Generating the Signature
PHP Example (using phpseclib)
use phpseclib\Crypt\RSA;
// Ensure your private key is in the correct format (PKCS#8 recommended)
$privateKey = '-----BEGIN PRIVATE KEY-----\n...
-----END PRIVATE KEY-----';
// Compact JSON body - ensure URLs are encoded, timestamp included, no extra whitespace
$requestBody = '{"amount":"30.00","reference":"10010049","currency_code":"USD","email":"johndoe@mail.com","first_name":"John","last_name":"Doe","country_code":"US","state_code":"CA","timestamp":"1720444671","callback_confirmed_url":"https%3A%2F%2Fmerchant.com%2Fapi%2Fpayment-confirmed","failed_callback_url":"https%3A%2F%2Fmerchant.com%2Fapi%2Fpayment-failed"}';
// 1. Create SHA-512 hash of the request body
// 2. Get the HEX representation of the hash
$requestBodyHexHash = hash('sha512', $requestBody);
// 3. Sign the HEX hash using RSA-PSS with SHA-512
$rsa = new RSA();
$rsa->setSignatureMode(RSA::SIGNATURE_PSS);
$rsa->setHash('sha512');
$rsa->setMGFHash('sha512'); // Important: Ensure MGF1 hash matches
$rsa->loadKey($privateKey);
// Sign the *hexadecimal hash string*
$binarySignature = $rsa->sign($requestBodyHexHash);
// 4. Base64 encode the binary signature
$signature = base64_encode($binarySignature);
// Now use $signature in the X-API-Signature header
echo "X-API-Signature: " . $signature;
Node.js Example
const crypto = require('crypto');
// Ensure your private key is in PEM format (PKCS#8 recommended)
const privateKey = `-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----`;
// Compact JSON body - ensure URLs are encoded, timestamp included, no extra whitespace
const requestBody = '{"amount":"30.00","reference":"10010049","currency_code":"USD","email":"johndoe@mail.com","first_name":"John","last_name":"Doe","country_code":"US","state_code":"CA","timestamp":"1720444671","callback_confirmed_url":"https%3A%2F%2Fmerchant.com%2Fapi%2Fpayment-confirmed","callback_failed_url":"https%3A%2F%2Fmerchant.com%2Fapi%2Fpayment-failed"}';
// 1. Create SHA-512 hash of the request body
// 2. Get the HEX representation of the hash
const hash = crypto.createHash('sha512');
const requestBodyHexHash = hash.update(requestBody, 'utf-8').digest('hex');
// 3. Sign the HEX hash using RSA-PSS with SHA-512
const signer = crypto.createSign('RSA-SHA512'); // Use RSA-SHA512 algorithm hint
signer.update(requestBodyHexHash, 'utf8'); // Sign the hex hash string
const binarySignature = signer.sign({
key: privateKey,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING, // Use PSS padding
saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST // Match digest length (SHA512 -> 64 bytes)
}, 'binary'); // Get binary output before base64 encoding
// 4. Base64 encode the binary signature
const signature = Buffer.from(binarySignature, 'binary').toString('base64');
// Now use $signature in the X-API-Signature header
console.log(`X-API-Signature: ${signature}`);
Kotlin Example (using BouncyCastle)
import java.security.KeyFactory
import java.security.PrivateKey
import java.security.Signature
import java.util.Base64
import java.nio.charset.StandardCharsets
import java.security.spec.PKCS8EncodedKeySpec
import java.security.MessageDigest
import java.security.Security
import org.bouncycastle.jce.provider.BouncyCastleProvider
import java.security.spec.MGF1ParameterSpec
import java.security.spec.PSSParameterSpec
fun main() {
// Add BouncyCastle provider if not already added
Security.addProvider(BouncyCastleProvider())
// Your private key in PEM PKCS#8 format
val privateKeyPem = """
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
""".trimIndent()
// Compact JSON body - ensure URLs are encoded, timestamp included, no extra whitespace
val requestBody = """{"amount":"10.00","callback_confirmed_url":"https%3A%2F%2Fmerchant.com%2Fapi%2Fpayment-confirmed","state_code":"AL","country_code":"US","currency_code":"USD","email":"johndoe@mail.com","failed_callback_url":"https%3A%2F%2Fmerchant.com%2Fapi%2Fpayment-failed","first_name":"John","last_name":"Doe","reference":"payment000142","timestamp":"1731348091"}"""
// 1. Create SHA-512 hash of the request body
// 2. Get the HEX representation of the hash
val requestBodyHexHash = hashSha512ToHex(requestBody)
// 3. Load the RSA private key
val privateKey = loadPrivateKey(privateKeyPem)
// 4. Sign the HEX hash using SHA512withRSA/PSS
val signature = signHexData(requestBodyHexHash, privateKey)
println("X-API-Signature: $signature")
}
// Helper to hash data with SHA-512 and return HEX string
fun hashSha512ToHex(input: String): String {
val digest = MessageDigest.getInstance("SHA-512")
val hashBytes = digest.digest(input.toByteArray(StandardCharsets.UTF_8))
// Convert byte array to hexadecimal string
return hashBytes.joinToString("") { "%02x".format(it) }
}
// Helper to load PKCS#8 private key from PEM string
fun loadPrivateKey(keyPem: String): PrivateKey {
val keyPemFormatted = keyPem
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replace("\\s+".toRegex(), "") // Remove whitespace
val keyBytes = Base64.getDecoder().decode(keyPemFormatted)
val keySpec = PKCS8EncodedKeySpec(keyBytes)
val keyFactory = KeyFactory.getInstance("RSA")
return keyFactory.generatePrivate(keySpec)
}
// Helper to sign HEXADECIMAL data using SHA512withRSA/PSS and return Base64 signature
fun signHexData(hexData: String, privateKey: PrivateKey): String {
val signatureInstance = Signature.getInstance("SHA512withRSA/PSS", "BC") // Specify BouncyCastle provider
// Define PSS parameters: SHA-512 for hash, MGF1 with SHA-512, salt length = hash length (64), trailer field = 1
val pssParameterSpec = PSSParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 64, 1)
signatureInstance.setParameter(pssParameterSpec)
signatureInstance.initSign(privateKey)
// Update signature with the bytes of the HEX string
signatureInstance.update(hexData.toByteArray(StandardCharsets.UTF_8))
val signatureBytes = signatureInstance.sign()
return Base64.getEncoder().encodeToString(signatureBytes)
}
API Key + Secret Authorization (Deprecated)
This older method uses a combination of an API Token UUID and an encrypted secret derived from your API Key and API Secret, which can be obtained by generating an API Token via the API Settings page.
Authorization Headers
API-UUID: The unique ID of your API Token.API-Secret: An OpenSSL (AES-128-CTR) encrypted string derived from your API Key and hashed API Secret.
Generating the API-Secret Header
The encrypted API-Secret string is constructed as follows:
- The plain-text API Secret is first hashed using SHA-256.
- This hashed API Secret is used as the data to be encrypted.
- The API Key is hashed (SHA-256), and the first 16 bytes of its hex representation are used as the Initialization Vector (IV) for AES-128-CTR.
- The hashed API Secret (from step 1) is also used as the Encryption Key for AES-128-CTR.
- The resulting encrypted binary data is then Base64 encoded to form the header value.
This process ensures the plain-text API Secret never travels over the network.
Code Examples: Generating the API-Secret Header
PHP Example
// Replace with your actual API Secret and Key
$plainApiSecret = 'wq6vNQDlfo6aSwe6wbjhCQV2D2tRvzzj';
$apiKey = 'AV80GEDp52u7aPQYsertdNhipY7fWs9J';
// 1. Hash the API Secret
$hashedApiSecret = hash('sha256', $plainApiSecret);
// Data to encrypt is the hashed secret itself
$encryptionString = $hashedApiSecret;
// AES-128-CTR setup
$ciphering = "AES-128-CTR";
$options = OPENSSL_RAW_DATA; // Use OPENSSL_RAW_DATA for direct binary output before base64
// 3. Generate IV from API Key
$encryptionIv = substr(hash('sha256', $apiKey), 0, 16);
// 4. Use hashed secret as encryption key
// Note: AES-128 requires a 16-byte key. If hash is longer, usually first 16 bytes are used.
// Check library specifics, but common practice is truncation if key is derived.
// For simplicity here, assuming the library handles key length or using a truncated key.
// $encryptionKey = substr($hashedApiSecret, 0, 16); // Example if key needs explicit length
$encryptionKey = $hashedApiSecret; // Often libraries handle key length derivation
// 5. Encrypt
$encryptedBinary = openssl_encrypt($encryptionString, $ciphering, $encryptionKey, $options, $encryptionIv);
// 6. Base64 encode the binary result for the header
$encryptedApiSecretHeader = base64_encode($encryptedBinary);
echo "API-Secret: " . $encryptedApiSecretHeader;
TypeScript Example
import { createCipheriv, createHash } from 'crypto';
// Replace with your actual API Secret, Key, and UUID
const apiSecret: string = '***';
const apiKey: string = '***';
const uuid: string = '***';
// Example request body (adjust as needed)
const body = {
amount: 100.00,
currency_code: "USD",
reference: "xxxxxxxx",
callback_confirmed_url: "https://api.example.com/v1/webhooks/confirmed",
callback_failed_url: "https://api.example.com/v1/webhooks/failed",
email: "customer@example.com",
first_name: "John",
last_name: "Doe",
country_code: "US",
state_code: "AL",
};
// 1. Hash the API Secret
const hashedSecret: string = createHash('sha256').update(apiSecret).digest('hex');
// 2. Data to encrypt is the hashed secret
const encryptionString: string = hashedSecret;
const ciphering: string = 'aes-128-ctr';
// 3. Generate IV from API Key (first 16 bytes of SHA256 hash hex)
const encryptionIv: string = createHash('sha256').update(apiKey).digest('hex').substring(0, 16);
// 4. Use hashed secret as encryption key (first 16 bytes for AES-128)
const encryptionKey: string = hashedSecret.substring(0, 32); // Use first 32 hex chars -> 16 bytes
// 5. Encrypt
const cipher = createCipheriv(ciphering, Buffer.from(encryptionKey, 'hex'), Buffer.from(encryptionIv, 'utf8'));
let encryptedBinary = cipher.update(encryptionString, 'utf8');
encryptedBinary = Buffer.concat([encryptedBinary, cipher.final()]);
// 6. Base64 encode the binary result
const encryptedApiSecretHeader = encryptedBinary.toString('base64');
console.log(`API-UUID: ${uuid}`);
console.log(`API-Secret: ${encryptedApiSecretHeader}`);
// Example usage with axios (ensure axios is installed)
/*
import axios from 'axios';
async function makeRequest() {
try {
const response = await axios.post("https://columis.tech/api/payment", body, {
headers: {
"API-UUID": uuid,
"API-Secret": encryptedApiSecretHeader,
"Content-Type": "application/json",
},
});
console.log('Response:', response.data);
} catch (error) {
console.error('Error making request:', error.response?.data || error.message);
}
}
makeRequest();
*/
API Requests Data Encryption (Optional)
If you have enabled the "Uses API request encryption" setting when creating or updating an API Token via the API Settings page, you must send your request parameters encrypted.
When this setting is active, instead of sending your parameters directly in the request body,
you should include a single parameter named encrypted_data. The value of this
parameter must be the encrypted representation of your original request data.
The encryption method used is AES-128-CTR. The encryption key is derived from your API Secret, and the Initialization Vector (IV) is derived from your API Key. See the example below.
Code Example: Encrypting Request Data (PHP)
// Your API Key and Secret
$apiKey = 'Ajh9QHvwOgr7IHHie2q2yf6JCYhVs6Zs'; // Replace with your API Key
$apiSecretPlain = 'Aw44LvMTmWFV5SLV0udDGpzgG2KGhwWz'; // Replace with your API Secret
// Your request data array
$data = [
'amount' => '10.00', // Use string for amounts if required
'base_currency_code' => 'USD',
'exchange_currency_code' => 'USDT',
'client_uuid' => 'fe9e49af-b06c-468d-837c-c355ec133077',
'client_email' => 'example@columis.com',
'confirmation_callback_url' => 'https://example.com/confirmation-callback-url',
'type' => 'internal',
// Add timestamp if required by the endpoint and auth method
// 'timestamp' => time(),
];
// 1. Prepare the data string for encryption
// Option A: HTTP Query String (as per original example)
$dataString = http_build_query($data);
// Option B: JSON String (more common for APIs)
// $dataString = json_encode($data);
// 2. Hash the API Secret (SHA-256) to use as the encryption key
$encryptionKey = hash('sha256', $apiSecretPlain);
// Note: AES-128 requires a 16-byte key. openssl_encrypt might use the first 16 bytes
// of the derived hash, or handle it internally. Verify behaviour if issues arise.
// For explicit 16-byte key: $encryptionKey = substr(hash('sha256', $apiSecretPlain, true), 0, 16);
// 3. Generate the IV from the API Key (first 16 bytes of SHA-256 hash)
$iv = substr(hash('sha256', $apiKey, true), 0, 16); // Use raw output=true for binary IV
// 4. Encrypt the data string using AES-128-CTR
$ciphering = "AES-128-CTR";
$options = OPENSSL_RAW_DATA; // Use raw output for base64 encoding later
$encryptedDataBinary = openssl_encrypt(
$dataString,
$ciphering,
$encryptionKey, // Use the hashed secret
$options,
$iv // Use the derived IV
);
if ($encryptedDataBinary === false) {
throw new \Exception("Encryption failed: " . openssl_error_string());
}
// 5. Base64 encode the encrypted binary data
$encryptedDataString = base64_encode($encryptedDataBinary);
// Now, send this $encryptedDataString as the value for the 'encrypted_data' parameter
// in your API request body.
// Example structure for the final request body:
// {
// "encrypted_data": "... base64 encoded string ..."
// }
// If using Signature Auth, remember the signature is calculated on the *unencrypted* data
// including the timestamp, before encryption occurs.
Webhooks
Webhook Data Decryption
To enable Webhook Data Encryption, visit the API Settings page and retrieve the public key
provided for data decryption.
Once enabled in your settings, webhook notifications sent to your server will include an
additional parameter named encrypted_data.
The encrypted_data value is generated by encrypting/signing the webhook payload with
your private key. You should use the public key from your dashboard to decrypt/verify it in your
webhook listener.
Code Examples: Decrypting Webhook Data
PHP Decryption Example (Using Public Key)
// Your Public Key from dashboard
$publicKeyPem = <<<'EOT'
-----BEGIN PUBLIC KEY-----
... your public key content ...
-----END PUBLIC KEY-----
EOT;
// Load the public key
$publicKey = openssl_pkey_get_public($publicKeyPem);
if ($publicKey === false) {
throw new \Exception('Public key format not valid or key is incorrect.');
}
// Extract encrypted data from the POST request
$encryptedDataEncoded = $_POST['encrypted_data'] ?? '';
if (empty($encryptedDataEncoded)) {
throw new \Exception('Encrypted data not found in webhook payload.');
}
// Base64 decode the encrypted value
$encryptedData = base64_decode($encryptedDataEncoded);
if ($encryptedData === false) {
throw new \Exception('Failed to Base64 decode the encrypted data.');
}
// Decrypt/verify data using the public key
$decryptionSuccess = openssl_public_decrypt(
$encryptedData,
$decryptedData,
$publicKey,
OPENSSL_PKCS1_PADDING
);
if (!$decryptionSuccess) {
$error = openssl_error_string();
throw new \Exception('Failed to decrypt data: ' . $error);
}
// Decrypted data is likely JSON, decode it
$data = json_decode($decryptedData, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \Exception('Failed to decode decrypted JSON data: ' . json_last_error_msg());
}
// Now you can use the $data array
// e.g., $reference = $data['reference'];
TypeScript Decryption Example (Using Public Key)
import { constants, createPublicKey, publicDecrypt } from 'crypto';
// Assuming 'encryptedDataEncoded' is the raw value from the webhook payload (e.g., req.body.encrypted_data)
const encryptedDataEncoded: string = "EtIb...Kx7M="; // Example encoded data
// Your Public Key PEM string from dashboard
const publicKeyPem: string = `-----BEGIN PUBLIC KEY-----
... your public key content ...
-----END PUBLIC KEY-----`;
try {
// Load the public key object
const publicKey = createPublicKey(publicKeyPem);
// Base64 decode the encrypted value
const decodedData = Buffer.from(encryptedDataEncoded, 'base64');
// Decrypt/verify data using the public key
const decryptedBuffer = publicDecrypt(
{
key: publicKey,
padding: constants.RSA_PKCS1_PADDING,
},
decodedData
);
// Convert decrypted buffer to string (assuming UTF8)
const decryptedDataString = decryptedBuffer.toString('utf8');
// Parse the JSON data
const data = JSON.parse(decryptedDataString);
// Now you can use the 'data' object
console.log('Decrypted Data:', data);
// e.g., const reference = data.reference;
} catch (error) {
console.error('Decryption failed:', error);
// Handle error appropriately
}
Payments
The Payments section provides two main integration methods for processing fiat payments:
-
API + Redirect Method: Make a server-to-server API call to create a payment request.
The API response includes a
redirect_urlthat you must use to redirect the customer's browser to complete the payment on our hosted page. This method requires API token generation and signature verification for authentication. - JS SDK Widget Method: Embed our payment widget directly into your webpage. The widget appears as a modal where users can complete their payment without leaving your site. This method also requires API token generation and signature verification.
Both methods support webhook notifications for payment status updates, and you can enable optional data encryption for enhanced security.
Authentication Requirements
To use either payment method, you must:
- Generate API Tokens: Visit the API Settings page to create your API credentials. You'll receive an API Key and Secret that are used for authentication.
- Implement Signature Validation: Each API request must be signed using your private RSA key. Follow the Signature Validation guide to properly implement this security measure.
-
Include Required Headers: Every request must include:
X-API-Key: Your public API KeyX-API-Signature: The Base64 encoded signature of the hashed request body
Create Payment (API + Redirect)
/payment
Creates a new payment request. This involves making a server-to-server API call with the payment
details. The API response will contain a redirect_url. You must then redirect the
customer's browser to this URL to complete the payment process on our hosted page.
The final status of the payment will be sent via webhook notifications to your specified callback URLs.
Request Body Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
amount |
String | Required | Payment amount (e.g., "100.00"). |
currency_code |
String | Required | Payment Currency Code (e.g., "USD", "EUR"). |
reference |
String | Required | Your unique payment reference ID. |
email |
String | Required | Customer's email address. |
first_name |
String | Required | Customer's first name. |
last_name |
String | Required | Customer's last name. |
country_code |
String | Required | Customer's two-letter country code (ISO 3166-1 alpha-2, e.g., "US"). |
state_code |
String | Optional | Customer's two-letter state/province code (e.g., "CA", "AL"). Required for some regions/methods. |
address |
String | Optional | Customer's street address. |
city |
String | Optional | Customer's city. |
zip |
String | Optional | Customer's postal/ZIP code. |
callback_confirmed_url |
String (URL Encoded) | Optional | Webhook URL for successful payment notification. |
callback_failed_url |
String (URL Encoded) | Optional | Webhook URL for failed/expired/cancelled payment notification. |
return_url |
String (URL Encoded) | Optional | URL to redirect user after successful payment completion on hosted page. |
cancel_url |
String (URL Encoded) | Optional | URL to redirect user after failed/cancelled payment. |
description |
String | Optional | Optional payment description displayed to the customer. |
source_of_funds |
String (URL) | Optional | URL to a document verifying source of funds. |
timestamp |
String | Required for Signature Auth | Current UNIX timestamp. |
* These fields are only required if your merchant account is configured to receive customer crypto wallets. Contact support to enable this feature.
Note on Callbacks: While `return_url` and `cancel_url` handle user redirection, rely on `callback_confirmed_url` and `callback_failed_url` webhooks for the definitive status updates for your backend systems.
Note on Signature Validation: If using Signature Validation, ensure the request body JSON is compact, URLs are encoded, all required fields (including `timestamp`) are present, and optional fields are only included if non-null before generating the hash and signature.
Example Request Body
{
"amount": "30.00",
"currency_code": "USD",
"reference": "10010049",
"email": "johndoe@mail.com",
"first_name": "John",
"last_name": "Doe",
"country_code": "US",
"state_code": "CA",
"timestamp": "1720444671",
"callback_confirmed_url": "https%3A%2F%2Fmerchant.com%2Fapi%2Fpayment-confirmed",
"callback_failed_url": "https%3A%2F%2Fmerchant.com%2Fapi%2Fpayment-failed",
"return_url": "https%3A%2F%2Fmerchant.com%2Fpayment-complete%3Fref%3D10010049",
"cancel_url": "https%3A%2F%2Fmerchant.com%2Fpayment-cancelled%3Fref%3D10010049",
"description": "Example Payment #10010049",
}
Example Success Response (200)
{
"redirect_url": "https://columis.tech/payment/f04e1061-661f-48c8-bc55-28d038998e17"
}
Webhook Notifications
- Without webhook encryption enabled: All fields listed below are sent as top-level parameters.
- With webhook encryption enabled for payments: The webhook will
include your plain
referenceplus a single additional fieldencrypted_data, which contains an encrypted JSON representation of all other fields described below.
Payment Confirmed Webhook
Triggered at your callback_confirmed_url when the payment is fully processed and
funds are confirmed.
application/x-www-form-urlencoded
Webhook Request Data Parameters
When webhook encryption is disabled, the following fields are sent as regular POST parameters:
reference(String) - Your original unique payment reference (e.g., "payment1234").currency_code(String) - The currency the payment was requested in (e.g., "EUR").amount(String) - The original amount requested (e.g., "20.00").amount_with_fees(String) - The total amount including any processing fees (e.g., "21.03").verified_currency_code(String) - The currency code of the funds actually received/verified (e.g., "USD").verified_amount(String) - The amount actually received/verified inverified_currency_code. (Seems missing from original docs text but implied)merchant_deposit_amount(String) - The final amount deposited to your merchant account after conversion/fees (e.g., "0.01531000").merchant_deposit_currency_code(String) - The currency code of the final deposit (e.g., "ETH").
When webhook encryption is enabled for payments, the webhook body instead contains:
reference(String) - Your original unique payment reference.encrypted_data(String, base64) - Encrypted JSON that contains all of the fields listed above.
If automatic exchange is disabled (or fails), `verified_amount` and `verified_currency_code` will match `merchant_deposit_amount` and `merchant_deposit_currency_code`.
Payment Failed Webhook
Triggered at your callback_failed_url when a payment does not complete
successfully.
application/x-www-form-urlencoded
Possible Statuses Received:
Expired
Failed
Cancelled
Rejected
Webhook Request Data Parameters
When webhook encryption is disabled, the following fields are sent as regular POST parameters:
reference(String) - Your original unique payment reference (e.g., "payment1234").status(String) - The final failed status (e.g., "Expired", "Failed", "Cancelled", "Rejected").fail_reason(String) - A description of why the payment failed (e.g., "Payment not submitted in 24 hours.").
When webhook encryption is enabled for payments, the webhook body instead contains:
reference(String) - Your original unique payment reference.encrypted_data(String, base64) - Encrypted JSON that containsstatusandfail_reason(and any future additional fields).
Payment JS SDK Widget
Our JavaScript SDK allows you to embed a payment widget directly into your webpage, providing a seamless checkout experience. Integration involves two main steps:
- Server-Side Token Generation: Your server must first call our secure API endpoint to obtain a temporary access token.
- Client-Side Widget Initialization: Use the obtained token to initialize the JS widget on your webpage.
Step 1: Get Access Token (Server-Side API Call)
/api/payments-sdk/get-token
Call this secure server-to-server endpoint from your backend to generate an access token required for initializing the JS widget on the client-side.
Required Headers
X-API-Key: Your public API Key.X-API-Signature: The Base64 encoded signature of the hashed request body. See the Signature Validation (Recommended) section above for detailed instructions and code examples on how to generate this signature.
Request Body Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
reference |
String | Required | Your unique reference ID for this payment instance. |
amount |
String | Required | Payment amount. |
currency_code |
String | Required | Payment currency code (e.g., "USD"). |
email |
String | Required | Customer's email address. |
first_name |
String | Required | Customer's first name. |
last_name |
String | Required | Customer's last name. |
country_code |
String | Required | Customer's two-letter country code. |
state_code |
String | Optional | Customer's two-letter state/province code. |
address |
String | Optional | Customer's street address. |
city |
String | Optional | Customer's city. |
zip |
String | Optional | Customer's postal/ZIP code. |
callback_confirmed_url |
String (URL Encoded) | Optional | Webhook URL for payment success notification. |
callback_failed_url |
String (URL Encoded) | Optional | Webhook URL for payment failure notification. |
timestamp |
String | Required | Current UNIX timestamp. |
source_of_funds |
String (URL) | Optional | URL to a source of funds document. |
* These fields are only required if your merchant account is configured to receive customer crypto wallets. Contact support to enable this feature.
Note: The parameters sent to this endpoint define the payment context that the generated token will represent when used to initialize the JS widget.
Example Success Response (200 OK)
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"expires_in": 3600
}
Step 2: Initialize JS Widget (Client-Side)
Include SDK Files
Include the following script and stylesheet in the <head> or before the
closing </body> tag of your web page:
<!-- Replace with actual paths to your hosted SDK files -->
<link rel="stylesheet" href="https://columis.com/libs/co-payments-sdk.min.css">
<script type="application/javascript" src="https://columis.com/libs/co-payments-sdk.ob.min.js"></script>
Note: Ensure these files (`co-payments-sdk.min.css` and `co-payments-sdk.ob.min.js`) are correctly placed and accessible in your project's public `libs` directory or adjust the paths accordingly.
Initialization
accessToken obtained from the server-side Get Token API call. Set the mode
parameter to 'production' for the live environment.
// Assuming you have fetched the accessToken from your server
const accessToken = '734ac916-8b84-4b2e-996f-5df8384ddab8'; // Replace with actual token
const columisPaymentsSDK = new ColumisPaymentsSDK({
'accessToken': accessToken,
'mode': 'sandbox' // Use 'production' for live environment, 'sandbox' or 'local' for testing
});
// The SDK will typically handle rendering the widget in a designated container
// Check the SDK's specific documentation if a container ID is needed.
Handling JS Events
Listen for events dispatched by the SDK to know when the payment process is complete or has failed on the client-side:
columisPaymentsSDK.addEventListener("paymentCompleted", function(eventData) {
console.log('CO Payments SDK: Payment Completed', eventData);
// Add your logic here for successful payment (e.g., show success message)
});
columisPaymentsSDK.addEventListener("paymentFailed", function(eventData) {
console.log('CO Payments SDK: Payment Failed/Cancelled', eventData);
// Add your logic here for failed/cancelled payment (e.g., show error message)
});
Webhook Notifications
While the JS events provide client-side feedback, always rely on server-side webhooks for definitive confirmation of payment status. Refer to the webhook details documented in the Create Payment (API) section (specifically the Webhook Notifications part) and the Webhook Data Decryption section (if encryption is enabled).
Convertible Withdrawals
The Convertible Withdrawals section provides functionality for withdrawing funds with optional currency conversion. This feature supports both internal transfers (to linked accounts) and external withdrawals (to cryptocurrency addresses).
Authentication Requirements
To use the Convertible Withdrawals API, you must:
- Generate API Tokens: Visit the API Settings page to create your API credentials. You'll receive an API Key and Secret that are used for authentication.
- Implement Signature Validation: Each API request must be signed using your private RSA key. Follow the Signature Validation guide to properly implement this security measure.
-
Include Required Headers: Every request must include:
X-API-Key: Your public API KeyX-API-Signature: The Base64 encoded signature of the hashed request body
Create Convertible Withdrawal (API)
/convertible-withdrawal
Initiates a withdrawal request, potentially involving currency conversion.
type: internal, the recipient user on your platform must first be linked to
their Columis account. This requires completing both the Register Redirect and Link Account Redirect flows.
Request Body Parameters
Parameters depend on the withdrawal type specified.
Common Parameters (Required for both types):
| Parameter | Type | Status | Description |
|---|---|---|---|
amount |
Numeric String | Required | The amount to withdraw (e.g., "0.00482741"). |
exchange_currency_code |
String | Required | The target currency code for the withdrawal after conversion (e.g., "USDT"). |
confirmation_callback_url |
String (URL Encoded) | Required | Webhook URL for successful withdrawal notification. |
type |
String | Required | Withdrawal type: internal or
external.external (Default): Requires
crypto_address,
base_currency_code.internal: Requires
client_uuid, client_email.
|
client_email |
String | Optional | Customer's email address (valid email format). |
first_name |
String | Optional | Customer's first name. |
last_name |
String | Optional | Customer's last name. |
country |
String | Optional | Customer's two-letter country code (e.g., "US", "CA", "GB"). |
timestamp |
String | Required for Signature Auth | Current UNIX timestamp. |
Parameters for Type "external":
| Parameter | Type | Status | Description |
|---|---|---|---|
crypto_address |
String | Required | The destination cryptocurrency address (e.g., "0xAf592..."). |
base_currency_code |
String | Required | The currency code of the source balance being withdrawn from (e.g., "USDC"). |
Parameters for Type "internal":
| Parameter | Type | Status | Description |
|---|---|---|---|
client_uuid |
String | Required | Unique ID of the linked client account receiving the withdrawal. |
client_email |
String | Required | Email address of the linked client account receiving the withdrawal. |
Optional Parameters (Applicable to either type):
| Parameter | Type | Status | Description |
|---|---|---|---|
failed_callback_url |
String (URL Encoded) | Optional | Webhook URL for failed withdrawal notification. |
remote_payout_id |
String | Optional | Your unique identifier for this payout/withdrawal. |
client_login_uuid |
String | Optional | Unique login identifier of the user on your platform. |
remote_payout_affiliate_id |
String | Optional | Unique affiliate identifier related to this payout. |
address |
String | Optional | Customer's street address. |
city |
String | Optional | Customer's city. |
zip |
String | Optional | Customer's postal/ZIP code. |
state_code |
String | Optional | Customer's two-letter state/province code. |
source_of_funds |
String (URL) | Optional | URL to a source of funds document. |
is_custodian |
Boolean | Optional | true if withdrawing to an exchange/VASP; false if
withdrawing to a self-controlled wallet. |
Example Success Response (200 OK)
{
"amount": "0.02100000",
"base_currency_code": "USDC",
"exchange_currency_code": "USDT",
"uuid": "b67d2908-279f-4f04-a016-10b530ad5521",
"message": "Withdrawal sucessfully submited."
}
Supported Currency and Blockchain Combinations
Withdrawals support the following combinations:
- Currency: BTC --- Blockchain: BTC
- Currency: ETH --- Blockchain: ETH
- Currency: ROX --- Blockchain: ETH
- Currency: USDT --- Blockchain: ETH
- Currency: ECC --- Blockchain: ETH
- Currency: USDC --- Blockchain: ETH
- Currency: DAI --- Blockchain: ETH
- Currency: PAXG --- Blockchain: ETH
- Currency: MANA --- Blockchain: ETH
- Currency: SAND --- Blockchain: ETH
- Currency: USDT --- Blockchain: POLYGON
- Currency: USDC --- Blockchain: POLYGON
- Currency: USDT --- Blockchain: TRON
Webhook Notifications
encrypted_data.
Confirmation Webhook
Triggered at your confirmation_callback_url when the withdrawal is fully
processed.
application/x-www-form-urlencoded
Webhook Request Data Parameters
exchanged_amount(Numeric String) - The amount credited/sent in thecurrency(e.g., "0.00482741").currency(String) - The currency code of the final exchanged/withdrawn amount (e.g., "ETH").uuid(String) - The unique identifier of the convertible withdrawal (e.g., "LDMRLKSKH8AP").confirmation_callback_url(String) - The callback URL originally provided.remote_payout_id(String, Optional) - Your payout ID, if provided in the original request.remote_payout_affiliate_id(String, Optional) - Your affiliate ID, if provided.client_login_uuid(String, Optional) - Your client login UUID, if provided.customer_email(String) - Email address associated with the withdrawal (might be the receiving client's email for internal transfers).customer_uuid(String) - Unique identifier associated with the withdrawal (might be the receiving client's UUID for internal transfers).- Additional data for "internal" type only:
payout_amount(Numeric String) - Amount paid out (relevant for internal transfers, potentially matches `exchanged_amount`?).
Failed Webhook
Triggered at your failed_callback_url (if provided) when a withdrawal fails.
application/x-www-form-urlencoded
Possible Statuses Received:
Exchange Failed
Withdrawal Failed
Rejected
Webhook Request Data Parameters
uuid(String) - The unique identifier of the failed convertible withdrawal (e.g., "LDMRLKSKH8AP").status(String) - The reason for failure (e.g., "Exchange Failed", "Withdrawal Failed", "Rejected").
Check Client Status (API)
/convertible-withdrawal/check-client-status
Use this endpoint before initiating an "internal" type convertible withdrawal to verify if the target user has a linked account on the platform.
Request Body Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
client_uuid |
String | Required | Unique ID of the potential recipient client account on your platform. |
client_email |
String | Required | Email address of the potential recipient client account on your platform. |
timestamp |
String | Required for Signature Auth | Current UNIX timestamp. |
Example Success Response (200 OK)
{
"is_registered": false,
"is_account_linked": false
}
Indicates whether the user identified by email/UUID is registered and if their account is linked for internal transfers.
Get Exchange Minimum (API)
/convertible-withdrawal/get-exchange-minimum
Retrieves the minimum amount required for a convertible withdrawal between specified currencies.
Request Body Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
base_currency_code |
String | Required | The source currency code. |
exchange_currency_code |
String | Required | The target currency code. |
timestamp |
String | Required for Signature Auth | Current UNIX timestamp. |
Example Success Response (200 OK)
{
"min_amount": "29.00"
}
Register Account Redirect (Internal Convertible Withdrawals/Payouts)
/payouts/register
This endpoint redirects a potential internal withdrawal recipient to a registration page for the designated payout partner. This is the first step in enabling them to receive internal convertible withdrawals.
Use this flow to onboard users on your platform who need to receive funds via internal convertible withdrawals but haven't yet registered with the payout partner.
Request Body Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
uuid |
String | Required | Your unique identifier for the customer/recipient being registered (e.g., "8e5835ea-..."). |
email |
String | Required | The customer/recipient's email address (e.g., "user@mail.com"). |
token |
String | Required | Payout Partner Token provided to you (e.g., "GX0DaEY0..."). |
register_callback_url |
String (URL Encoded) | Required | Webhook URL to notify you upon completion of the registration process. |
link_callback_url |
String (URL Encoded) | Required | Webhook URL to notify you upon completion of the subsequent account linking process. |
Note: This endpoint initiates a redirect. Your server should make a POST request to this endpoint, and the response will typically be a redirect instruction for the user's browser.
Link Account Redirect (Internal Convertible Withdrawals/Payouts)
/payouts/link
This endpoint redirects an internal withdrawal recipient (who should already be registered with the payout partner, possibly via the Register Redirect flow) to the authorization page to link their account.
Linking is necessary for them to receive internal convertible withdrawals. Users typically need to complete KYC and enable 2FA during this process.
Request Body Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
uuid |
String | Required | Your unique identifier for the customer/recipient (e.g., "8e5835ea-..."). |
email |
String | Required | The customer/recipient's email address (e.g., "user@mail.com"). |
token |
String | Required | Payout Partner Token provided to you (e.g., "GX0DaEY0..."). |
link_callback_url |
String (URL Encoded) | Required | Webhook URL to notify you upon completion of the account linking process. |
Note: This endpoint initiates a redirect. Your server should make a POST request, and the response will typically be a redirect instruction for the user's browser.
Utilities
Get Balances (API)
/balances
Retrieves all available balances for your account.
Authentication
This endpoint requires authentication using either Signature Validation or API Key + Secret (Deprecated) in the request headers.
Example Success Response (200 OK)
{
"balances": {
"USD": "10500.75",
"EUR": "8200.50",
"BTC": "1.2534",
"ETH": "4.95880232"
// ... other currency balances ...
}
}
The response is a JSON object where keys are currency codes and values are the available balances as strings.
Get Balance By Currency
/balances/{currencyCode}
Retrieves the available balance for a specific currency in your account.
URL Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
currencyCode |
String | Required | The currency code (e.g., "ETH", "USD", "BTC") for which to retrieve the balance. |
Authentication
This endpoint requires authentication using either Signature Validation or API Key + Secret (Deprecated) in the request headers.
Example Success Response (200 OK)
{
"ETH": "4.95880232"
}
The response is a JSON object where the key is the requested currency code and the value is the available balance as a string.
Get Transactions (API)
/transactions
Retrieves a paginated list of transactions for your account.
Query Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
page |
Integer | Optional | The page number of results to retrieve. Defaults to 1. |
per_page |
Integer | Optional | The number of transactions to retrieve per page. Defaults to 10. |
Authentication
This endpoint requires authentication using either Signature Validation or API Key + Secret (Deprecated) in the request headers.
Example Success Response (200 OK)
{
"transactions": {
"current_page": 1,
"data": [
{
"transaction_date": "2017-05-15 12:56:38",
"order_type": "Trade",
"tx_hash": 232424234,
"incoming_asset": "USDC",
"incoming_volume": "65.20965992",
"outgoing_asset": "USDT",
"outgoing_volume": "65.34653500",
"fee_asset": "USDT",
"fee_volume": "0.15643798",
"other_parties": null,
"note": "2342545",
"success": 1,
"internal_transfer": 0
}
// ... more transactions ...
],
"first_page_url": "https://your-api-domain.com/api/transactions?page=1",
"from": 1,
"last_page": 10791,
"last_page_url": "https://your-api-domain.com/api/transactions?page=10791",
"next_page_url": "https://your-api-domain.com/api/transactions?page=2",
"path": "https://your-api-domain.com/api/transactions",
"per_page": "10", // Adjust per_page based on request or default
"prev_page_url": null,
"to": 10, // Adjust based on per_page
"total": 10791
}
}
Get Countries (API)
/countries
Retrieves a list of supported countries, typically used for populating dropdown menus or validation.
Authentication
This endpoint requires authentication using either Signature Validation or API Key + Secret (Deprecated) in the request headers.
Example Success Response (200 OK)
{
"countries": {
"1": "Afghanistan",
"2": "Albania",
"3": "Algeria",
"4": "American Samoa",
"5": "Andorra",
"6": "Angola",
"8": "Antigua",
"9": "Argentina",
"10": "Armenia",
// ... full list of countries ...
"235": "United States of America",
"236": "Syria",
"237": "Trinidad & Tobago",
"238": "Yemen",
"239": "Sri Lanka",
"240": "Iraq",
"241": "Ethiopia",
"242": "St. Vincent and the Grenadines"
}
}
The response contains a JSON object under the countries key. This object maps
country IDs (keys) to country names (values).
English
Español
Croatian
Deutsch