Postman Setup Guide
This guide will walk you through setting up Postman to test BITXpay merchant-facing APIs with automatic Ed25519 signature generation.
Prerequisites
- Postman installed (Desktop or Web)
- Merchant API Key
- Merchant Private Key (Ed25519 format)
Quick Setup (3 Steps)
Step 1: Create a New Environment
- Open Postman
- Click Environments in the left sidebar
- Click Create Environment (+ button)
- Name it
BITXpay SandboxorBITXpay Production
Step 2: Configure Environment Variables
Add the following variables to your environment:
| Variable Name | Type | Initial Value | Current Value |
|---|---|---|---|
BASE_URL | default | https://sandboxapi.bitxpay.com/api/v1 | (same) |
MERCHANT_API_KEY | default | btxm_xxxxxxxxxxxx | Your API key |
MERCHANT_PRIVATE_KEY | secret | Ed25519:base64_encoded_pkcs8_key | Your private key |
X_API_KEY | default | (leave empty) | (auto-generated) |
X_API_SIGNATURE | default | (leave empty) | (auto-generated) |
X_API_TIMESTAMP | default | (leave empty) | (auto-generated) |
TIP
The last three variables (X_API_KEY, X_API_SIGNATURE, X_API_TIMESTAMP) will be automatically populated by the pre-request script.
How to Add Your Private Key
Your private key should be in Ed25519:base64 format:
Ed25519:MC4CAQAwBQYDK2VwBCIEIE8VdFJvfWXyGKm9...base64_encoded_pkcs8_keyImportant: The key must start with Ed25519: prefix followed by the base64-encoded PKCS8 private key.
Step 3: Add Pre-Request Script
- Create a new Collection (e.g., "BITXpay Merchant APIs")
- Click on the collection name
- Go to the Pre-request Script tab
- Paste the following script:
// ============================================
// BITXpay Merchant API - Ed25519 Signature
// ============================================
new Promise((resolve, reject) => {
const forgeUrl = 'https://cdn.jsdelivr.net/npm/node-forge@1.3.1/dist/forge.min.js';
const ed25519Url = 'https://cdn.jsdelivr.net/npm/@noble/ed25519@1.7.3/+esm';
// Load forge first
pm.sendRequest(forgeUrl, (err, res) => {
if (err) {
console.error('❌ Failed to load forge:', err);
reject(err);
return;
}
try {
// Mock browser globals and capture forge
var window = {};
var self = window;
var document = {};
// Wrap and execute forge to capture exports
const forgeCode = res.text();
const wrappedCode = `
(function(window, self, document) {
${forgeCode}
return window.forge || forge;
})(window, self, document)
`;
const forge = eval(wrappedCode);
const apiKey = pm.environment.get("MERCHANT_API_KEY");
const privateKeyFormatted = pm.environment.get("MERCHANT_PRIVATE_KEY");
if (!apiKey || !privateKeyFormatted) {
throw new Error("❌ Missing MERCHANT_API_KEY or MERCHANT_PRIVATE_KEY");
}
// Check if private key is in Ed25519:base64 format
if (!privateKeyFormatted.startsWith("Ed25519:")) {
throw new Error("❌ Private key must be in Ed25519:base64 format");
}
const method = pm.request.method;
const path = pm.request.url.getPath();
const timestamp = new Date().toISOString();
const body = pm.request.body ? pm.request.body.raw || '' : '';
const message = `${method}${path}${timestamp}${body}`;
console.log('🔐 Signing:', method, path);
console.log(' Timestamp:', timestamp);
console.log(' API Key:', apiKey);
// Extract base64 part from Ed25519:base64 format
const base64PrivateKey = privateKeyFormatted.substring(8); // Remove "Ed25519:" prefix
// Decode the PKCS8 private key using forge
const pkcs8Bytes = forge.util.decode64(base64PrivateKey);
const pkcs8Der = forge.util.createBuffer(pkcs8Bytes, 'raw');
// Parse PKCS8 DER to extract Ed25519 seed
const asn1 = forge.asn1.fromDer(pkcs8Der);
// Navigate ASN.1 structure: SEQUENCE -> PrivateKey OctetString
const privateKeyOctetString = asn1.value[2]; // Third element contains the private key
const privateKeyBytes = privateKeyOctetString.value;
// Parse inner OCTET STRING to get the actual 32-byte seed
const innerAsn1 = forge.asn1.fromDer(forge.util.createBuffer(privateKeyBytes, 'raw'));
const seedBytes = innerAsn1.value;
// Convert seed to Uint8Array
const seedArray = new Uint8Array(32);
for (let i = 0; i < 32; i++) {
seedArray[i] = seedBytes.charCodeAt(i) & 0xff;
}
// Manual Ed25519 signing using pure JavaScript
// Since we can't load external Ed25519 libraries easily, let's use a different approach
// We'll use the tweetnacl standalone version
const tweetnaclStandaloneUrl = 'https://cdn.jsdelivr.net/npm/tweetnacl@1.0.3/nacl-fast.js';
pm.sendRequest(tweetnaclStandaloneUrl, (err2, res2) => {
if (err2) {
console.error('❌ Failed to load tweetnacl:', err2);
reject(err2);
return;
}
try {
// Clean the response to avoid 'use strict' issues
let tweetnaclCode = res2.text();
// Remove any 'use strict' at the top that might cause issues
tweetnaclCode = tweetnaclCode.replace(/['"]use strict['"];?\s*/g, '');
// Wrap in IIFE to avoid global scope pollution
const wrappedNacl = `
(function() {
var window = window || {};
var self = window;
var document = {};
var global = window;
var crypto = undefined;
var require = undefined;
var module = { exports: {} };
var exports = module.exports;
${tweetnaclCode}
return module.exports || window.nacl || nacl;
})()
`;
const nacl = eval(wrappedNacl);
// Generate Ed25519 keypair from seed
const keyPair = nacl.sign.keyPair.fromSeed(seedArray);
// Convert message string to Uint8Array
const messageArray = new Uint8Array(message.length);
for (let i = 0; i < message.length; i++) {
messageArray[i] = message.charCodeAt(i);
}
// Sign the message with Ed25519
const signature = nacl.sign.detached(messageArray, keyPair.secretKey);
// Convert signature Uint8Array to base64 using forge
let sigBytes = '';
for (let i = 0; i < signature.length; i++) {
sigBytes += String.fromCharCode(signature[i]);
}
const signatureBase64 = forge.util.encode64(sigBytes);
console.log('✅ Signature:', signatureBase64.substring(0, 50) + '...');
pm.environment.set("X_API_KEY", apiKey);
pm.environment.set("X_API_SIGNATURE", signatureBase64);
pm.environment.set("X_API_TIMESTAMP", timestamp);
// pm.request.headers.upsert({ key: 'X-API-Key', value: apiKey });
// pm.request.headers.upsert({ key: 'X-API-Signature', value: signatureBase64 });
// pm.request.headers.upsert({ key: 'X-API-Timestamp', value: timestamp });
resolve();
} catch (error) {
console.error('❌ Error during Ed25519 signing:', error.message);
console.error('Stack:', error.stack);
reject(error);
}
});
} catch (error) {
console.error('❌ Error parsing private key:', error.message);
console.error('Stack:', error.stack);
reject(error);
}
});
});- Click Save
Understanding the Script
What It Does
The pre-request script automatically:
- Loads cryptographic libraries - node-forge for ASN.1 parsing and TweetNaCl for Ed25519 signing
- Parses the private key - Extracts the 32-byte seed from PKCS8 format
- Constructs the signature message - Combines
METHOD + PATH + TIMESTAMP + BODY - Signs the message - Uses Ed25519 signature algorithm (64-byte signature)
- Encodes the signature - Converts to Base64
- Sets environment variables - Populates
X_API_KEY,X_API_SIGNATURE, andX_API_TIMESTAMP
Signature Components
Message = METHOD + PATH + TIMESTAMP + BODYExample:
POST/payment_links2026-01-31T17:53:56Z{"payment_name":"Invoice #12345","amount":100.50,"currency":"USDT"}This message is then signed using:
- Algorithm: Ed25519 (EdDSA)
- Hashing: performed internally by Ed25519 (SHA-512) — the message is not pre-hashed
- Output Format: raw 64-byte signature, base64-encoded for transmission
- Key: Ed25519 private key (PKCS8,
Ed25519:-prefixed base64 in the environment variable) - Library: TweetNaCl (
nacl.sign.detached), loaded via CDN in the pre-request script
Creating Your First Request
1. Create Payment Link Request
- In your collection, click Add Request
- Name it "Create Payment Link"
- Set method to POST
- Set URL to:
/payment_links - Go to Headers tab and add:
| Key | Value |
|---|---|
Content-Type | application/json |
X-API-Key | |
X-API-Signature | |
X-API-Timestamp | |
- Go to Body tab, select raw and JSON, then paste:
{
"payment_name": "Test Payment",
"amount": 10,
"currency": "USDT",
"success_url": "https://example.com/success",
"cancel_url": "https://example.com/cancel"
}- Click Send
Expected Response
The response follows the documented { message, data } envelope (see Payments API):
{
"message": "Payment link created successfully",
"data": {
"id": "f7a9ff0a-678f-45ba-a918-dcafc5d479e9",
"payment_name": "Test Payment",
"amount": 10,
"currency": "USDT",
"payment_url": "https://sandboxpay.bitxpay.com/payment_link?payment_id=f7a9ff0a-678f-45ba-a918-dcafc5d479e9",
"payment_status": "pending",
"payment_type": "one_time",
"max_uses": 1,
"current_uses": 0,
"is_active": true,
"created_at": "2026-03-12T15:21:12.988356Z"
}
}Verify against live output
The exact field set may vary by request parameters. If your sandbox returns a different shape (e.g. flat, non-enveloped), report it — the Payments API reference is the canonical contract and should be reconciled with the backend.
Viewing Console Logs
To see the signature generation process:
- Click Console at the bottom of Postman (or View → Show Postman Console)
- Send a request
- You'll see logs like:
🔐 Signing: POST /payment_links
Timestamp: 2026-01-31T17:53:56Z
✅ Signature: CHJRTWpRdJ4Wqo8AoGE4QQ0FnQIUJQSWt0YmWyqZZq4J...Testing Other Endpoints
Get Payment Link by ID
Method: GET
URL: {{BASE_URL}}/payment_links/{{payment_id}}
Headers: Same as above (X-API-Key, X-API-Signature, X-API-Timestamp)
Body: NoneDelete Payment Link
Method: DELETE
URL: {{BASE_URL}}/payment_links/{{payment_id}}
Headers: Same as above
Body: NoneList Payment Links
Method: GET
URL: {{BASE_URL}}/payment_links?page=1&limit=20
Headers: Same as above (X-API-Key, X-API-Signature, X-API-Timestamp)
Body: NoneTIP
All /payment_links endpoints use the same Ed25519 signature authentication headers: X-API-Key, X-API-Signature, and X-API-Timestamp.
Advanced Configuration
Multiple Environments
Create separate environments for different stages:
BITXpay Sandbox
BASE_URL:https://sandboxapi.bitxpay.com/api/v1- Use sandbox API keys
BITXpay Production
BASE_URL:https://api.bitxpay.com/api/v1- Use production API keys
Switch between environments using the dropdown in the top-right corner.
Collection Variables
For values used across all requests in a collection:
- Click on your collection
- Go to Variables tab
- Add common values (e.g.,
merchant_key)
Saving Responses
To save a payment ID for subsequent requests:
- Go to the Tests tab of your "Create Payment Link" request
- Add this script:
const response = pm.response.json();
pm.environment.set("payment_id", response.payment_reference);Now you can use in other requests.
Troubleshooting
Error: "Missing MERCHANT_API_KEY or MERCHANT_PRIVATE_KEY"
Solution: Ensure your environment variables are set correctly and the environment is selected.
Error: "Invalid signature"
Possible causes:
- Incorrect private key format - Must be in
Ed25519:base64format - Missing Ed25519 prefix - Key must start with
Ed25519: - Key mismatch - The private key must match the public key associated with your API key
- Timestamp issues - Check your system clock is synchronized
- Library loading failure - Check console for CDN errors
Debug steps:
- Open Postman Console
- Check the signature generation logs
- Verify the message being signed matches:
METHOD + PATH + TIMESTAMP + BODY
Error: "Timestamp expired"
Solution: Timestamps are valid for 5 minutes. If you see this error:
- Your system clock may be incorrect
- You may have left a request open too long before sending
The pre-request script generates a fresh timestamp each time, so simply resend the request.
Script Not Running
Solution:
- Ensure the script is in the Collection pre-request script, not individual request
- Check Postman Console for JavaScript errors
- Verify you have internet access (script loads node-forge and TweetNaCl from CDN)
- Check that both libraries loaded successfully in the console logs
Private Key Format Issues
If your private key has issues, ensure it's formatted correctly:
Correct format:
Ed25519:MC4CAQAwBQYDK2VwBCIEIE8VdFJvfWXyGKm9base64encodedkeyFormat explanation:
- Prefix:
Ed25519: - Followed by: Base64-encoded PKCS8 private key
- No newlines or spaces in the base64 part
Downloadable Resources
Postman Collection
Download our pre-configured Postman collection:
Download BITXpay Collection (Coming soon)
Environment Template
Download the environment template:
Download Environment Template (Coming soon)
Next Steps
- Explore API Endpoints - Learn about all available APIs
- Test with Other Tools - Try cURL, Insomnia, etc.
- Troubleshooting Guide - Solve common issues
Support
Need help with Postman setup?
- Email: api-support@bitxpay.com
- Documentation: https://docs.bitxpay.com/