Introduction
VelaPay provides two distinct APIs for payment processing and terminal management:
- Local Terminal API (In-Network): Direct communication with VelaPay terminals on your local network for real-time payment processing, device control, and terminal management.
- Cloud API (VelaPay API): Cloud platform API for card-not-present payments and hosted payment links, plus courier delivery, SMS, merchant wallet, and campaign blasts — no physical terminal required.
This documentation covers both APIs comprehensively, providing all the information needed to integrate VelaPay into your payment solutions.
Getting Started
Prerequisites
For Local Terminal API:
- VelaPay Manager application installed on a compatible terminal
- Terminal connected to your local network
- Terminal's IP address
- Store secret (for remote authentication)
For Cloud API:
- API Key and API Secret (contact VelaPay Support)
- API base URL — provided together with your credentials; it is not published in this documentation
- HTTPS-capable client
- Server time synchronized with UTC
Quick Decision Guide
Use Local Terminal API when you need to:
- Process card-present transactions with physical terminals
- Control terminal hardware (printer, camera, MSR)
- Display dialogs and collect customer input on terminal
- Capture signatures
- Manage terminal configuration
- Access terminal device information
Use Cloud API when you need to:
- Collect payments with hosted payment page links (e-commerce checkouts, invoices)
- Process card-not-present transactions (e-commerce, phone orders)
- Process payments without physical terminal hardware
- Integrate with web applications or remote services
- Tokenize card data for recurring payments
- Book courier deliveries, send SMS, manage the merchant wallet, or send campaign blasts
Best Practices
General Best Practices
- Request IDs: Always include a
requestIdfor payment transactions to enable request/response logging and retrieval. - Timeout Handling: Implement appropriate timeout handling on the client side, especially for payment and dialog requests that may require customer interaction.
- Error Recovery: Always check the
statusfield in responses and handle errors appropriately with proper retry logic. - Amount Formatting: Always send amounts as strings in decimal format (e.g., "10.00" for $10.00) for both the Local Terminal API and the Cloud API.
- Concurrent Requests: Avoid sending multiple payment requests simultaneously as they will be rejected with "Another transaction in process" error.
- Signature Generation: Always compute the signature using the exact request body that will be sent (including whitespace and formatting).
Authentication Best Practices
- Secure Secret Storage: Store secrets and API keys securely and never expose them in client-side code. Use environment variables or secure key management systems. Never commit secrets to version control. Treat the API base URL the same way — keep it in server-side configuration and do not publish it in client-side code or public repositories.
- HMAC-SHA256 Authentication: Use proper HMAC-SHA256 authentication with timestamp validation. Ensure server and client clocks are synchronized (use NTP if possible). Handle "Request timestamp expired" errors by checking clock synchronization.
- API Key Management: Rotate API keys periodically. Use different API keys for development and production. Monitor API key usage for suspicious activity.
Cloud API Best Practices
- PCI Compliance: Never store unencrypted card data. Use tokenization for recurring payments. Implement proper data encryption in transit (HTTPS). Follow PCI DSS requirements for card data handling.
- Transaction Management: Store transaction IDs for refund/void operations. Implement idempotency for duplicate prevention. Log all transaction requests and responses.
- Error Handling: Implement retry logic for network failures. Handle declined transactions appropriately. Provide clear error messages to users.
Support
Contact Information
For API access, credentials, technical support, or questions about VelaPay APIs:
VelaPay Support
- Website: https://www.velapay.net
- Email: support@velapay.net
Getting API Credentials
For Cloud API:
- Contact VelaPay Support to obtain your API Key and API Secret
- Specify your intended use case and estimated transaction volume
- Receive credentials and base URL for production environment
For Local Terminal API:
- The store secret is configured in the VelaPay Manager application on each terminal
- Contact VelaPay Support for assistance with terminal configuration
- No registration required for localhost access
Local Terminal API
Direct HTTP communication with VelaPay terminals on your local network for real-time payment processing, device control, and terminal management.
Payment Processing
Card-present transactions with EMV, NFC, and MSR support
Terminal Control
Display dialogs, collect input, and manage screen navigation
Hardware Integration
Printer, camera, and magnetic stripe reader control
Configuration
Terminal settings and cloud configuration management
Getting Started
VelaPay Manager serves as a universal payment middleware running on Android terminals, providing HTTP API access on port 7501 for payment processing and device management.
Prerequisites
- VelaPay Manager application installed on a compatible terminal
- Terminal connected to your local network
- Terminal's IP address (find in VelaPay Manager settings)
- Store secret (for remote authentication - configured in terminal settings)
Server Configuration
- Port: 7501
- Protocol: HTTP
- Content-Type: application/json; charset=utf-8
- CORS: Enabled for all origins
- Base URL: http://[terminal-ip]:7501
Authentication
Local Requests
Requests from localhost (127.0.0.1 or ::1) do not require authentication.
Remote Requests - HMAC-SHA256 Signature
Remote requests require cryptographic signature authentication using HMAC-SHA256:
- X-Timestamp: Current Unix timestamp in seconds (epoch time)
- X-Signature: HMAC-SHA256(secret, timestamp + body)
- The secret is configured in
store_secretand is never transmitted - Timestamps must be within 5 minutes of server time (prevents replay attacks)
Signature Calculation Example (JavaScript)
const crypto = require('crypto');
function generateSignature(secret, timestamp, body) {
const message = timestamp + body;
return crypto.createHmac('sha256', secret)
.update(message)
.digest('hex');
}
const timestamp = Math.floor(Date.now() / 1000).toString();
const body = JSON.stringify({ requestType: 'payment', amount: '100.00' });
const signature = generateSignature(secret, timestamp, body);
Request/Response Format
Base Request Structure
{
"requestType": "string", // Required: Type of request
"resourceType": "string", // Context-specific resource
"actionType": "string", // Context-specific action
"requestId": "string", // Optional: For logging
// Additional parameters...
}
Base Response Structure
{
"status": "ok|error",
"timestamp": 1234567890000,
"version": "1.0.0",
"message": "string",
// Additional data...
}
Supported Payment Types
VelaPay Manager supports all major payment types for in-person transactions:
- Credit/Debit Cards: Visa, Mastercard, Amex, Discover
- EMV Chip: Full EMV compliance for card-present transactions
- Contactless: NFC tap-to-pay support
- EBT: Electronic Benefits Transfer processing
- Gift Cards: Integrated gift card management system
API Request Types
system
App control, configuration, logs, screenshots
device
Device information and terminal status
payment
Sale, auth, capture, return, batch settlement
dialog
Customer input, signature capture, button prompts
Receipt printing and custom templates
msr
Magnetic stripe card reader
System API
The System API provides access to application management, configuration, logging, and screen capture.
Request Parameters
| Parameter | Type | Required | Description | Valid Values |
|---|---|---|---|---|
requestType |
string | Required | Type of request | "system" |
resourceType |
string | Required | Resource to access | "app", "config", "log", "screen" |
actionType |
string | Required | Action to perform (varies by resourceType) | See specific endpoints below |
POST Restart Application
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
requestType |
string | Required | Must be "system" |
resourceType |
string | Required | Must be "app" |
actionType |
string | Required | Must be "restart" |
// Request
{
"requestType": "system",
"resourceType": "app",
"actionType": "restart"
}
// Response
{
"status": "ok",
"timestamp": 1234567890000,
"version": "1.0.0"
}
POST Capture Screenshot
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
requestType |
string | Required | Must be "system" |
resourceType |
string | Required | Must be "screen" |
actionType |
string | Required | Must be "capture" |
Response Fields:
| Field | Type | Description |
|---|---|---|
status |
string | Response status: "ok" or "error" |
imageData |
string | Base64-encoded PNG image data |
format |
string | Image format (always "png") |
// Request
{
"requestType": "system",
"resourceType": "screen",
"actionType": "capture"
}
// Response
{
"status": "ok",
"timestamp": 1234567890000,
"version": "1.0.0",
"imageData": "base64_encoded_png_data",
"format": "png"
}
POST Get Log List
Request Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
requestType |
string | Required | Must be "system" |
resourceType |
string | Required | Must be "log" |
logType |
string | Required | Type of logs to retrieve (e.g., "requestResult") |
actionType |
string | Required | "get", "getList", or "clear" |
logId |
string | Optional | Specific log ID (required when actionType is "get") |
logFilter |
object | Optional | Filter criteria for log retrieval |
logFilter.createdSince |
integer | Optional | Unix timestamp in milliseconds - retrieve logs created since this time |
// Request
{
"requestType": "system",
"resourceType": "log",
"logType": "requestResult",
"actionType": "getList",
"logFilter": {
"createdSince": 1234567890000
}
}
// Response
{
"status": "ok",
"logs": [...],
"count": 10
}
Device API
Retrieve device information and terminal status.
GET Get Device Info
// Request
{
"requestType": "device",
"resourceType": "info"
}
// Response
{
"status": "ok",
"uid": "GN1234567890",
"manufacture": "GENERIC",
"model": "GENERIC",
"sn": "1234567890"
}
Payment API
Process card-present transactions including sale, authorization, capture, refund, and batch settlement.
Transaction Types
sale- Regular sale transactionauth- Authorization onlycapture- Capture a previous authorizationreturn- Return/refund transactionvoid- Void transactionadjust- Adjust tip amountbatch- Batch settlement
Tender Types
credit- Credit carddebit- Debit cardEBT- Electronic Benefits Transfergift- Gift cardloyalty- Loyalty card
Payment Request Parameters
| Parameter | Type | Required | Description | Valid Values / Example |
|---|---|---|---|---|
requestType |
string | Required | Type of request | "payment" |
actionType |
string | Optional | Action to perform | "process" |
transactionType |
string | Required | Type of payment transaction | "sale", "auth", "capture", "return", "void", "adjust", "batch" |
amount |
string | Required | Transaction amount in decimal format | "100.00", "25.99" |
tenderType |
string | Optional | Payment method type. If not provided, user will be prompted to select | "credit", "debit", "EBT", "gift", "loyalty" |
amountTip |
string | Optional | Pre-set tip amount | "10.00", "5.50" |
invoiceNumber |
string | Optional | Invoice or order number for reference | "INV-12345", "ORDER-001" |
referenceNumber |
string | Conditional | Required for capture, void, and adjust transactions. Transaction reference from original auth | "REF123456" |
signatureCapture |
boolean | Optional | Request signature capture after approval | true, false |
requestId |
string | Optional | Unique identifier for logging and retrieval | "unique_request_id" |
company |
string | Optional | Company name for receipt | "ACME Corp" |
printReceipt |
string | Optional | Whether to print receipt automatically | "true", "false" |
Tip Prompt Parameters
Optional parameters for customizing tip prompts during payment:
| Parameter | Type | Required | Description |
|---|---|---|---|
tipPrompt |
string | Optional | Tip prompt style: "button" for preset options, "custom" for manual entry |
tip1Label |
string | Optional | Label for first tip option (e.g., "15%") |
tip1Amount |
string | Optional | Amount for first tip option |
tip2Label |
string | Optional | Label for second tip option (e.g., "18%") |
tip2Amount |
string | Optional | Amount for second tip option |
tip3Label |
string | Optional | Label for third tip option (e.g., "20%") |
tip3Amount |
string | Optional | Amount for third tip option |
tipShowCustomTip |
boolean | Optional | Show "Custom Tip" button for manual entry |
tipShowNoTip |
boolean | Optional | Show "No Tip" button option |
Payment Response Fields
| Field | Type | Description | Example Value |
|---|---|---|---|
status |
string | Overall request status | "ok", "error" |
timestamp |
integer | Response timestamp in milliseconds | 1234567890000 |
version |
string | API version | "1.0.0" |
paymentStatus |
string | Payment transaction status | "APPROVED", "DECLINED", "ERROR" |
batchID |
string | Current batch identifier | "123" |
transactionID |
string | Unique transaction identifier from processor | "456789" |
transactionNumber |
string | Local transaction number | "789" |
transactionType |
string | Type of transaction performed | "SALE", "AUTH", "CAPTURE", etc. |
tenderType |
string | Payment method used | "CREDIT", "DEBIT" |
amount |
string | Final transaction amount | "100.00" |
amountTip |
string | Tip amount included | "10.00" |
amountCashBack |
string | Cash back amount (if applicable) | "0.00" |
amountRequest |
string | Originally requested amount | "100.00" |
invoiceNumber |
string | Invoice number from request | "INV-123" |
referenceNumber |
string | Transaction reference (use for capture/void/adjust) | "REF123" |
authCode |
string | Authorization code from processor | "AUTH456" |
resultCode |
string | Result code from processor | "00" (success) |
token |
string | Tokenized card data (if tokenization enabled) | "tokenized_card_data" |
token2 |
string | Additional token data | "additional_token" |
cardEntry |
string | Card entry method | "CHIP", "SWIPE", "MANUAL", "CONTACTLESS" |
merchantID |
string | Merchant identifier | "MERCHANT123" |
cardType |
string | Card brand | "VISA", "MASTERCARD", "AMEX", "DISCOVER" |
cardNumber |
string | Masked card number (last 4 digits) | "****1234" |
cardHolder |
string | Cardholder name from card | "JOHN DOE" |
signatureData |
string | Base64-encoded signature image (if captured) | "base64_signature" |
message |
string | Response message or error description | "Transaction approved" |
Batch Settlement Response Fields
Additional fields returned for batch settlement transactions:
| Field | Type | Description |
|---|---|---|
batch_settlementTime |
string | Batch settlement timestamp (ISO format) |
batch_creditSaleCount |
string | Number of credit card sale transactions |
batch_creditSaleAmount |
string | Total amount of credit card sales |
batch_creditReturnCount |
string | Number of credit card return transactions |
batch_creditReturnAmount |
string | Total amount of credit card returns |
batch_debitSaleCount |
string | Number of debit card sale transactions |
batch_debitSaleAmount |
string | Total amount of debit card sales |
batch_debitReturnCount |
string | Number of debit card return transactions |
batch_debitReturnAmount |
string | Total amount of debit card returns |
POST Process Sale
// Request
{
"requestType": "payment",
"actionType": "process",
"transactionType": "sale",
"tenderType": "credit",
"amount": "100.00",
"amountTip": "10.00",
"invoiceNumber": "INV-123",
"signatureCapture": true,
"tipPrompt": "button",
"tip1Label": "15%",
"tip1Amount": "15.00",
"tip2Label": "18%",
"tip2Amount": "18.00",
"tip3Label": "20%",
"tip3Amount": "20.00",
"tipShowCustomTip": true,
"tipShowNoTip": true,
"company": "ACME Corp",
"printReceipt": "true",
"requestId": "unique_request_id"
}
// Response
{
"status": "ok",
"timestamp": 1234567890000,
"version": "1.0.0",
"paymentStatus": "APPROVED",
"batchID": "123",
"transactionID": "456789",
"transactionType": "SALE",
"tenderType": "CREDIT",
"amount": "100.00",
"amountTip": "10.00",
"amountCashBack": "0.00",
"amountRequest": "100.00",
"invoiceNumber": "INV-123",
"resultCode": "00",
"token": "tokenized_card_data",
"token2": "additional_token",
"cardEntry": "CHIP",
"transactionNumber": "789",
"referenceNumber": "REF123",
"authCode": "AUTH456",
"merchantID": "MERCHANT123",
"cardType": "VISA",
"cardNumber": "****1234",
"cardHolder": "JOHN DOE",
"signatureData": "base64_signature"
}
POST Batch Settlement
// Request
{
"requestType": "payment",
"transactionType": "batch"
}
// Response
{
"status": "ok",
"batch_settlementTime": "2023-12-01T10:00:00Z",
"batch_creditSaleCount": "10",
"batch_creditSaleAmount": "1000.00",
"batch_creditReturnCount": "2",
"batch_creditReturnAmount": "50.00",
"batch_debitSaleCount": "5",
"batch_debitSaleAmount": "250.00",
"batch_debitReturnCount": "1",
"batch_debitReturnAmount": "25.00"
}
Dialog API
Display customer input dialogs, signature capture, and button prompts on the terminal screen.
Dialog Types
input- Collect various types of user input (number, phone, money, email, date, time)signature- Capture customer signaturebutton- Display multiple button options (up to 5 buttons)
Common Dialog Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
requestType |
string | Required | Must be "dialog" |
dialogType |
string | Required | Type of dialog: "input", "signature", or "button" |
title |
string | Optional | Dialog title (supports HTML formatting) |
description |
string | Optional | Dialog description or instructions |
timeout |
integer | Optional | Timeout in seconds (dialog will auto-close after this time) |
Input Dialog Specific Parameters
| Parameter | Type | Required | Description | Valid Values |
|---|---|---|---|---|
inputType |
string | Required | Type of input to collect | "number", "phone", "money", "email", "date", "time", "datetime" |
Input Dialog Response Fields:
| Field | Type | Description |
|---|---|---|
inputValue |
string | Raw input value entered by user |
inputValueFormatted |
string | Formatted value (for money type, formatted with decimals) |
Button Dialog Specific Parameters
Support for up to 5 buttons with customizable labels and return values:
| Parameter | Type | Required | Description |
|---|---|---|---|
button1Label |
string | Optional | Text displayed on first button |
button1Value |
string | Optional | Value returned when first button is clicked |
button2Label |
string | Optional | Text displayed on second button |
button2Value |
string | Optional | Value returned when second button is clicked |
button3Label |
string | Optional | Text displayed on third button |
button3Value |
string | Optional | Value returned when third button is clicked |
button4Label |
string | Optional | Text displayed on fourth button |
button4Value |
string | Optional | Value returned when fourth button is clicked |
button5Label |
string | Optional | Text displayed on fifth button |
button5Value |
string | Optional | Value returned when fifth button is clicked |
Button Dialog Response Fields:
| Field | Type | Description |
|---|---|---|
buttonLabel |
string | Label of the button that was clicked |
buttonValue |
string | Value associated with the button that was clicked |
Signature Dialog Specific Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
description |
string | Optional | Agreement text or instructions (e.g., "I agree to pay...") |
Signature Dialog Response Fields:
| Field | Type | Description |
|---|---|---|
signatureData |
string | Base64-encoded PNG image of the signature |
POST Button Dialog
// Request
{
"requestType": "dialog",
"dialogType": "button",
"title": "Select Option
",
"description": "Choose an option",
"button1Label": "Option 1",
"button2Label": "Option 2",
"button3Label": "Option 3",
"button4Label": "Option 4",
"button5Label": "Option 5",
"button1Value": "value1",
"button2Value": "value2",
"button3Value": "value3",
"button4Value": "value4",
"button5Value": "value5"
}
// Response
{
"status": "ok",
"timestamp": 1234567890000,
"version": "1.0.0",
"buttonLabel": "Option 2",
"buttonValue": "value2"
}
POST Input Dialog
Supported input types: number, phone, money, email, date, time, datetime
// Request
{
"requestType": "dialog",
"dialogType": "input",
"inputType": "money",
"title": "Enter Amount
",
"timeout": 30
}
// Response
{
"status": "ok",
"timestamp": 1234567890000,
"version": "1.0.0",
"inputValue": "1000",
"inputValueFormatted": "10.00"
}
POST Signature Dialog
// Request
{
"requestType": "dialog",
"dialogType": "signature",
"title": "APPROVED
Signature Required",
"description": "I agree to pay the above total amount according to the card issuer agreement",
"timeout": 60
}
// Response
{
"status": "ok",
"timestamp": 1234567890000,
"version": "1.0.0",
"signatureData": "base64_encoded_signature_image"
}
Print API
Print receipts and custom templates using the terminal's built-in printer.
Print Types
image- Print a base64-encoded imagetemplate- Print using a structured template with multiple item types
Common Print Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
requestType |
string | Required | Must be "print" |
printType |
string | Required | Type of print job: "image" or "template" |
Image Print Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
imageData |
string | Required | Base64-encoded image data (PNG, JPG, or other supported formats) |
Template Print Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
printItemList |
array | Required | Array of print item objects, each defining a specific element to print |
Print Item Types
Each item in printItemList must have a type field. Supported types:
Text Item
| Property | Type | Required | Description | Valid Values |
|---|---|---|---|---|
type |
string | Required | Item type | "text" |
fontSize |
string | Optional | Font size | "small", "medium", "large", "xlarge" |
textLeft |
string | Optional | Text aligned to left | Any string |
textCenter |
string | Optional | Text aligned to center | Any string |
textRight |
string | Optional | Text aligned to right | Any string |
isBold |
boolean | Optional | Apply bold formatting | true, false |
isItalic |
boolean | Optional | Apply italic formatting | true, false |
isUnderline |
boolean | Optional | Apply underline formatting | true, false |
isInvert |
boolean | Optional | Invert colors (white text on black background) | true, false |
Barcode Item
| Property | Type | Required | Description | Valid Values |
|---|---|---|---|---|
type |
string | Required | Item type | "barcode" |
code |
string | Optional | Barcode data to encode | Any string |
format |
string | Optional | Barcode format | "code128", "code39", "upc" |
width |
integer | Optional | Barcode width in pixels | e.g., 300 |
height |
integer | Optional | Barcode height in pixels | e.g., 100 |
alignment |
string | Optional | Barcode alignment | "left", "center", "right" |
QR Code Item
| Property | Type | Required | Description | Valid Values |
|---|---|---|---|---|
type |
string | Required | Item type | "qrcode" |
code |
string | Optional | Data to encode in QR code (e.g., URL, text) | Any string |
height |
integer | Optional | QR code size in pixels | e.g., 200 |
alignment |
string | Optional | QR code alignment | "left", "center", "right" |
Line Item
| Property | Type | Required | Description |
|---|---|---|---|
type |
string | Required | Must be "line" |
height |
integer | Optional | Line thickness in pixels (default: 1) |
Feed Item
| Property | Type | Required | Description |
|---|---|---|---|
type |
string | Required | Must be "feed" |
height |
integer | Optional | Feed distance in pixels (advances paper without printing) |
Cut Item
| Property | Type | Required | Description |
|---|---|---|---|
type |
string | Required | Must be "cut" - triggers paper cutter (no additional properties) |
POST Print Image
// Request
{
"requestType": "print",
"printType": "image",
"imageData": "base64_encoded_image_data"
}
// Response
{
"status": "ok",
"timestamp": 1234567890000,
"version": "1.0.0"
}
POST Print Template
// Request
{
"requestType": "print",
"printType": "template",
"printItemList": [
{
"type": "text",
"fontSize": "large",
"textCenter": "Receipt",
"isBold": true,
"isUnderline": true
},
{
"type": "text",
"textLeft": "Item",
"textRight": "$10.00"
},
{
"type": "line",
"height": 2
},
{
"type": "qrcode",
"code": "https://example.com",
"height": 200,
"alignment": "center"
},
{
"type": "barcode",
"code": "123456789",
"format": "code128",
"width": 300,
"height": 100,
"alignment": "center"
},
{
"type": "feed",
"height": 50
},
{
"type": "cut"
}
]
}
// Response
{
"status": "ok",
"timestamp": 1234567890000,
"version": "1.0.0"
}
MSR API
Magnetic stripe reader operations for reading card data.
Request Parameters
| Parameter | Type | Required | Description | Valid Values |
|---|---|---|---|---|
requestType |
string | Required | Type of request | "msr" |
actionType |
string | Required | Action to perform | "read" |
Response Fields
| Field | Type | Description | Example |
|---|---|---|---|
status |
string | Response status | "ok", "error" |
track1 |
string | Track 1 data from magnetic stripe | Raw track data |
track2 |
string | Track 2 data from magnetic stripe | Raw track data |
track3 |
string | Track 3 data from magnetic stripe | Raw track data |
cardNumber |
string | Masked card number (last 4 digits visible) | "****1234" |
expiryDate |
string | Card expiry date in MMYY format | "1225" (December 2025) |
cardholderName |
string | Cardholder name from track data | "JOHN DOE" |
POST Read Card (MSR)
// Request
{
"requestType": "msr",
"actionType": "read"
}
// Response
{
"status": "ok",
"timestamp": 1234567890000,
"version": "1.0.0",
"track1": "...",
"track2": "...",
"track3": "...",
"cardNumber": "****1234",
"expiryDate": "1225",
"cardholderName": "JOHN DOE"
}
Note: The device will automatically display a "SWIPE" screen and wait for the user to swipe their card through the magnetic stripe reader.
Local Terminal API Tester
Response
Complete Integration Example
This example demonstrates a complete payment flow with the Local Terminal API.
JavaScript/Node.js Example
const crypto = require('crypto');
const fetch = require('node-fetch');
class VelaPayTerminalClient {
constructor(terminalIp, secret) {
this.baseUrl = `http://${terminalIp}:7501`;
this.secret = secret;
}
generateSignature(timestamp, body) {
const message = timestamp + body;
return crypto
.createHmac('sha256', this.secret)
.update(message)
.digest('hex');
}
async makeRequest(payload) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const body = JSON.stringify(payload);
const signature = this.generateSignature(timestamp, body);
const response = await fetch(this.baseUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Timestamp': timestamp,
'X-Signature': signature
},
body: body
});
return response.json();
}
async processPayment(amount, options = {}) {
const requestId = options.requestId || `ORDER-${Date.now()}`;
const paymentRequest = {
requestType: "payment",
transactionType: "sale",
amount: amount,
signatureCapture: options.signatureCapture || true,
requestId: requestId,
invoiceNumber: options.invoiceNumber,
tipPrompt: "button",
tip1Label: "15%",
tip1Amount: (parseFloat(amount) * 0.15).toFixed(2),
tip2Label: "18%",
tip2Amount: (parseFloat(amount) * 0.18).toFixed(2),
tip3Label: "20%",
tip3Amount: (parseFloat(amount) * 0.20).toFixed(2),
tipShowCustomTip: true,
tipShowNoTip: true
};
return this.makeRequest(paymentRequest);
}
}
// Usage
const client = new VelaPayTerminalClient('192.168.1.100', 'your-secret-key');
const result = await client.processPayment('50.00', {
invoiceNumber: 'INV-12345',
signatureCapture: true
});
console.log('Payment Result:', result);
Python Example
import hmac
import hashlib
import time
import json
import requests
class VelaPayTerminalClient:
def __init__(self, terminal_ip, secret):
self.base_url = f'http://{terminal_ip}:7501'
self.secret = secret
def generate_signature(self, timestamp, body):
message = str(timestamp) + body
return hmac.new(
self.secret.encode('utf-8'),
message.encode('utf-8'),
hashlib.sha256
).hexdigest()
def make_request(self, payload):
timestamp = str(int(time.time()))
body = json.dumps(payload)
signature = self.generate_signature(timestamp, body)
headers = {
'Content-Type': 'application/json',
'X-Timestamp': timestamp,
'X-Signature': signature
}
response = requests.post(self.base_url, headers=headers, data=body)
return response.json()
def process_payment(self, amount, **options):
request_id = options.get('request_id', f'ORDER-{int(time.time())}')
payment_request = {
'requestType': 'payment',
'transactionType': 'sale',
'amount': amount,
'signatureCapture': options.get('signature_capture', True),
'requestId': request_id,
'invoiceNumber': options.get('invoice_number'),
'tipPrompt': 'button',
'tip1Label': '15%',
'tip1Amount': f'{float(amount) * 0.15:.2f}',
'tip2Label': '18%',
'tip2Amount': f'{float(amount) * 0.18:.2f}',
'tip3Label': '20%',
'tip3Amount': f'{float(amount) * 0.20:.2f}',
'tipShowCustomTip': True,
'tipShowNoTip': True
}
return self.make_request(payment_request)
# Usage
client = VelaPayTerminalClient('192.168.1.100', 'your-secret-key')
result = client.process_payment('75.50', invoice_number='INV-67890')
print(f"Payment Result: {result}")
VelaPay Cloud API
External developer API for the VelaPay platform: payments and hosted payment links, courier delivery, SMS, merchant wallet, and campaign blasts.
Payments
Hosted payment links, direct card transactions, tokenization
Delivery
Courier delivery quotes, booking, tracking, and cancellation
SMS
Send text messages billed to the merchant wallet
Wallet
Merchant wallet balance, top-ups, funding cards, auto-recharge
Campaigns
Email/SMS campaign blasts over your own recipient lists
How Requests Work
The Cloud API uses a single endpoint. Every call is an HTTP POST with a JSON body; the operation is selected by requestType + actionType in the body.
POST <API_URL> // provided by VelaPay Support
Content-Type: application/json
{
"requestType": "payment",
"actionType": "getPaymentLink",
"transactionType": "sale",
"amount": "10.00"
}
requestTypeis case-insensitive (paymentandPAYMENTare equivalent).actionTypeis case-sensitive as documented below.- Money crosses this API as decimal strings (e.g.
"50.00").
Request Types
payment
Hosted payment links, direct card transactions, transaction search
delivery
Courier delivery: quote, book, track, cancel
sms
Send text messages
wallet
Merchant wallet balance, top-up, funding cards, auto-recharge
campaign
Email/SMS campaign blasts and saved audiences
Authentication
Every request is authenticated with an API key + HMAC-SHA256 signature.
Required Headers
| Header | Value |
|---|---|
Content-Type |
application/json |
X-API-Key |
Your API key (e.g. XXXX-XXXX-XXXX-XXXX) |
X-Timestamp |
Unix timestamp in seconds, as a string |
X-Signature |
Hex HMAC-SHA256 of timestamp + rawJsonBody, keyed with your API secret |
The signature covers the exact raw bytes of the JSON body you send — sign the same string you POST.
Example (JavaScript)
const crypto = require('crypto');
// API_URL, API_KEY and API_SECRET are provided by VelaPay Support.
// Keep them in server-side configuration (environment variables).
const body = JSON.stringify({ requestType: 'payment', actionType: 'getPaymentLink', transactionType: 'sale', amount: '10.00' });
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = crypto.createHmac('sha256', API_SECRET).update(timestamp + body).digest('hex');
await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY,
'X-Timestamp': timestamp,
'X-Signature': signature,
},
body,
});
Example (PHP)
$bodyJson = json_encode($body);
$timestamp = (string)time();
$signature = hash_hmac('sha256', $timestamp . $bodyJson, $apiSecret);
Permissions
Each API key carries a list of permissions. The required permission for a call is {REQUESTTYPE}_{actionType} (matched case-insensitively), e.g. payment_getPaymentLink, delivery_getQuote, sms_send. A {requestType}_manage permission is a wildcard for that whole request type (e.g. wallet_manage, campaign_manage).
See the full API Permissions Reference.
Preconditions
PAYMENT(all actions exceptsearch) requires the store to have an active payment processor configured.DELIVERYrequires an active delivery provider.SMSrequires the store's SMS product to be configured.
Response Envelope
Every response is JSON with at minimum:
| Field | Type | Description |
|---|---|---|
status |
string | "ok" or "error" |
timestamp |
int | Unix timestamp in seconds |
version |
string | API version |
message |
string | Present on errors |
Action-specific fields are merged into this envelope and documented per action below.
Payment API (requestType: payment)
Payments and hosted payment links.
getPaymentLink— create a hosted payment page linkgetPaymentInfo— look up a payment created with getPaymentLinkprocess— direct card transaction (server-to-server)search— list the store's logged payment transactions- Payment Webhook — server-to-server notification when a link finishes
Hosted Payment Page
The Hosted Payment Page provides a secure, pre-built payment form hosted by VelaPay. Instead of collecting card details directly, your application generates a payment link and redirects the customer to VelaPay's hosted page to complete the payment (or save a card). This approach simplifies PCI compliance since sensitive card data never touches your servers.
- Hosted Payment Page (
getPaymentLink) — Best for e-commerce checkouts, invoice payments, and any scenario where you want to redirect customers to a secure payment form without handling card data yourself. - Direct processing (
process) — Best when you already have card details (e.g., tokenized cards, recurring billing) and want to process payments server-to-server.
How It Works
The hosted payment flow involves three parties: Your Server, VelaPay Cloud API, and the Customer's Browser. The diagram below shows the complete lifecycle of a hosted payment.
Flow Summary
- Create Payment Link — Your server calls
getPaymentLinkwith the amount, return URL, webhook URL, and optional customer/invoice details. VelaPay creates a payment session and returns apaymentIdand a hosted payment pagelink. - Redirect Customer — Your application redirects the customer's browser to the payment link. The customer sees a secure, VelaPay-hosted form where they enter their card details and complete payment.
- Payment Processing — VelaPay processes the payment through the configured payment processor. No card data passes through your server.
- Dual Notification — After payment completes, two things happen in parallel:
- Webhook (server-to-server) — VelaPay sends a POST request to your
webhookURL with the full payment result. This is the most reliable way to confirm payment since it does not depend on the customer's browser. - Return URL (browser redirect) — The customer's browser is redirected to your
returnUrlwithpaymentIdandstatusas query parameters so you can display the appropriate confirmation page.
- Webhook (server-to-server) — VelaPay sends a POST request to your
- Verify Payment — Your server calls
getPaymentInfowith thepaymentIdto retrieve the full payment details and confirm the transaction status. This is recommended as a final verification step.
returnUrl redirect to confirm payment. The customer may close their browser before being redirected. Always use the webhook for reliable server-side payment confirmation, and/or call getPaymentInfo to verify the result.
actionType: getPaymentLink
Creates a hosted payment page link. The customer opens the link, pays (or saves a card), and is redirected / your webhook is called.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
requestType |
string | Required | Must be "payment" |
actionType |
string | Required | Must be "getPaymentLink" |
transactionType |
string | Required | "sale", "auth", or "token" (token = save card, no charge) |
amount |
string | Conditional | Decimal format (e.g. "10.00"). Required unless transactionType = token. |
invoiceNumber |
string | Optional | Invoice or order number |
referenceNumber |
string | Optional | External reference identifier |
customerName |
string | Optional | Customer name |
email |
string | Optional | Customer email |
phone |
string | Optional | Customer phone |
customerId |
string | Optional | Customer identifier |
addressBilling |
object | Optional | Billing on file — used for AVS when the page never shows a billing form. Properties: street, street2, city, state, zipcode, country, name, phone |
returnUrl |
string | Optional | Customer is redirected here after payment |
webhook |
string | Optional | Server-to-server notification URL (https, public host) |
webhookFormat |
string | Optional | "standard" opts into the standard envelope; omitted = legacy flat payload (see Payment Webhook) |
webhookSecret |
string | Optional | Per-link signing key for webhook deliveries |
paymentMethodId |
string | Optional | Saved payment method identifier |
paymentPageOptions |
object | Optional | Controls what the hosted payment page shows for this link (see below) |
Response
| Field | Type | Description |
|---|---|---|
status |
string | "ok" or "error" |
paymentId |
string | The payment session identifier |
link |
string | The URL to hand to the customer |
The link's domain is driven by the store's LIGHTBOX feature setting: lightbox-enabled stores get the hosted payment app (root path), other stores get the customer site's /pay/ page.
Example
// Request
{
"requestType": "payment",
"actionType": "getPaymentLink",
"transactionType": "sale",
"amount": "99.99",
"invoiceNumber": "INV-2024-001",
"customerName": "John Doe",
"email": "customer@example.com",
"returnUrl": "https://yourapp.com/payment/complete",
"webhook": "https://yourapp.com/api/payment-webhook",
"webhookFormat": "standard",
"webhookSecret": "your-webhook-signing-key",
"paymentPageOptions": {
"customerEmail": "REQUIRED",
"addressBilling": "SHOW",
"totalSummary": "SHOW"
}
}
// Response
{
"status": "ok",
"timestamp": 1754314211,
"version": "1.0.0",
"paymentId": "507f1f77bcf86cd799439011",
"link": "https://[payment-page-host]/pay?paymentId=507f1f77bcf86cd799439011"
}
Return URL
When the payment finishes, the customer is redirected to:
{returnUrl}?paymentId={paymentId}&status={approved|declined|cancelled}
Set paymentPageOptions.showResultScreen = "YES" to keep the customer on the hosted result screen instead of redirecting.
Payment Page Options
paymentPageOptions controls what the hosted payment page shows for this link. All keys are optional; an omitted key uses the default. Unknown keys are stored and echoed back but ignored by the page.
Validated at link creation (invalid values are rejected)
| Key | Values | Description |
|---|---|---|
lightbox |
"ENABLED" | "DISABLED" |
Omitted/ENABLED: follows the store's LIGHTBOX feature. DISABLED: forces the manual card form on this link only. |
customerLogin |
"SHOW" | "HIDE" |
Omitted/HIDE: login UI is hidden (default). SHOW: offers customer login (saved cards) — only on stores with the customer-account feature. |
Form field visibility
Value is one of "SHOW", "REQUIRED", "OPTIONAL"; omitted = field hidden:
| Key | Description |
|---|---|
customerName |
Customer name input |
customerEmail |
Customer email input |
customerPhone |
Customer phone input |
addressBilling |
Billing address section. Also accepts "LIGHTBOX": use the billing address sent on the link and never show the form; falls back to SHOW when the link carries no usable billing address. |
addressShipping |
Shipping address section (legacy alias: shippingAddress) |
Page chrome
| Key | Values | Description |
|---|---|---|
totalSummary |
"SHOW" |
Order/total panel; shown by default when paymentPageOptions is absent entirely |
storeName |
"SHOW" |
Store name display (same default rule as totalSummary) |
storeAddress |
"SHOW" |
Store address display |
pageTitle |
free text | Replaces "Finish your payment" |
pageSubtitle |
free text | Subtitle text |
showResultScreen |
"YES" |
Keep the buyer on the result screen even when returnUrl is set |
paymentPageOptions at all) auto-opens the lightbox. Requesting any visible field (SHOW/REQUIRED/OPTIONAL) or customerLogin: "SHOW" renders the page instead.
actionType: getPaymentInfo
Look up a payment created with getPaymentLink. Use this to verify the payment result on your server after receiving a webhook notification or when the customer returns via the return URL.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
requestType |
string | Required | Must be "payment" |
actionType |
string | Required | Must be "getPaymentInfo" |
paymentId |
string | Required | The payment ID returned from getPaymentLink |
Response Fields
| Field | Type | Description |
|---|---|---|
paymentId |
string | The payment session identifier |
cartStatus |
string | "ACTIVE", "COMPLETED", "CANCELLED", "FAILED" |
request |
object | What the link was created with — amount, email, paymentPageOptions, ... |
result |
object | Processor outcome once paid — paymentStatus, cardType, cardNumber, cardExpDate, authCode, referenceNumber, transactionNumber, transactionId, token, message |
addPaymentMethod |
boolean | true for token links |
created |
int | Unix timestamp when the payment session was created |
modified |
int | Unix timestamp of the last update |
Example — Completed Payment
// Request
{
"requestType": "payment",
"actionType": "getPaymentInfo",
"paymentId": "507f1f77bcf86cd799439011"
}
// Response
{
"status": "ok",
"timestamp": 1754314211,
"version": "1.0.0",
"paymentId": "507f1f77bcf86cd799439011",
"cartStatus": "COMPLETED",
"request": {
"amount": "99.99",
"transactionType": "sale",
"email": "customer@example.com",
"invoiceNumber": "INV-2024-001"
},
"result": {
"paymentStatus": "APPROVED",
"transactionId": "TXN_123",
"authCode": "AUTH456",
"referenceNumber": "REF789",
"cardType": "VISA",
"cardNumber": "1234",
"message": "APPROVAL"
},
"addPaymentMethod": false,
"created": 1754313800,
"modified": 1754314100
}
Example — Pending Payment
// Request
{
"requestType": "payment",
"actionType": "getPaymentInfo",
"paymentId": "507f1f77bcf86cd799439011"
}
// Response
{
"status": "ok",
"timestamp": 1754314211,
"version": "1.0.0",
"paymentId": "507f1f77bcf86cd799439011",
"cartStatus": "ACTIVE",
"request": {
"amount": "99.99",
"transactionType": "sale"
},
"result": {},
"created": 1754313800,
"modified": 1754313800
}
actionType: process
Direct card transaction (server-to-server, no hosted page). Requires the payment_process permission.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
transactionType |
string | Required | "sale", "auth", "capture", "return", "void", "token", "verify", "inquiry" (supported set depends on the store's processor) |
tenderType |
string | Optional | "credit" or "debit" (default credit) |
amount |
string | Conditional | Transaction amount in decimal format, e.g. "10.00" |
amountTip |
string | Optional | Tip amount |
invoiceNumber |
string | Optional | Invoice or order number |
company |
string | Optional | Company name |
cardNumber |
string | One of | Card number — OR — |
token |
string | One of | Stored card token — OR — |
paymentMethodId |
string | One of | Saved payment method; token, billing and expiration are hydrated from it automatically |
cardExpDate |
string | Conditional | Expiration date, MMYY format |
cvv |
string | Optional | Card security code |
cardHolder |
string | Optional | Cardholder name |
addressBilling |
object | Optional | AVS; merged with the saved card's stored billing |
customerIp |
string | Optional | Customer IP address |
getToken |
boolean | Optional | Also tokenize the card during the charge |
transactionContext |
string | Optional | CIT/MIT indicator |
requestId |
string | Optional | Your correlation id, kept in logs |
Response Fields
| Field | Type | Description |
|---|---|---|
status |
string | "ok" or "error" |
paymentStatus |
string | "APPROVED", "DECLINED", "ERROR", "HELD" |
transactionId |
string | VelaPay transaction UID |
message |
string | Processor response message |
authCode, referenceNumber, transactionNumber |
string | Processor references — save referenceNumber for capture/void/return operations |
cardType, cardNumber, cardExpDate |
string | Card details (cardNumber masked) |
token |
string | When tokenization was requested |
Example — Sale with a New Card
// Request
{
"requestType": "payment",
"actionType": "process",
"transactionType": "sale",
"amount": "10.00",
"cardNumber": "4111111111111111",
"cardExpDate": "1225",
"cvv": "123",
"cardHolder": "John Doe",
"invoiceNumber": "INV-12345",
"customerIp": "203.0.113.10",
"getToken": true,
"addressBilling": {
"name": "John Doe",
"street": "123 Main St",
"city": "Anytown",
"state": "CA",
"zipcode": "12345",
"country": "US"
}
}
// Response
{
"status": "ok",
"timestamp": 1754314211,
"version": "1.0.0",
"paymentStatus": "APPROVED",
"message": "APPROVAL",
"authCode": "112892",
"referenceNumber": "211025O2C-EE3A17F7-70FD-4061-AEA3-30F60DEFBA68",
"transactionId": "MH169X56_AC3556463BD8957C_5RVH_L93D",
"transactionType": "SALE",
"amount": "10.00",
"token": "4034762327081111",
"cardType": "VISA",
"cardNumber": "1111",
"cardExpDate": "1225"
}
Example — Sale with a Stored Token
// Request
{
"requestType": "payment",
"actionType": "process",
"transactionType": "sale",
"amount": "144.11",
"invoiceNumber": "INV-67890",
"token": "4459079401220002",
"customerIp": "203.0.113.10"
}
Example — Capture / Void / Return
Reference the original transaction with referenceNumber:
// Capture a prior auth (amount optional for partial capture)
{
"requestType": "payment",
"actionType": "process",
"transactionType": "capture",
"referenceNumber": "211025O2C-AUTH-12345",
"amount": "50.00"
}
// Void a transaction
{
"requestType": "payment",
"actionType": "process",
"transactionType": "void",
"referenceNumber": "281025C45-9C229B8E-DD67-4434-AC25-293C9DD037AF"
}
// Return (refund) a transaction
{
"requestType": "payment",
"actionType": "process",
"transactionType": "return",
"referenceNumber": "211025O2C-SALE-12345",
"amount": "25.00"
}
actionType: search
List the store's logged payment transactions. Scoped to the API key's store.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
invoiceNumber |
string | Optional | Filter by invoice number |
paymentStatus |
string | Optional | Partial match, case-insensitive |
transactionType |
string | Optional | Partial match, case-insensitive |
dates |
string | Optional | Comma-separated report dates |
offset |
int | Optional | Pagination offset |
limit |
int | Optional | Page size |
Response
{
"status": "ok",
"transactions": [ ... ]
}
Payment Webhook
When a link created with a webhook URL finishes, VelaPay POSTs to it.
Legacy Payload (default)
{
"paymentId": "...",
"storeId": "...",
"status": "APPROVED",
"request": { "...": "what the link was created with" },
"result": { "...": "processor outcome" }
}
Standard Envelope (opt in with webhookFormat: "standard")
{
"type": "PAYMENT",
"event": "payment.approved",
"source": "PAYMENT",
"sourceId": "<paymentId>",
"timestamp": 1754314211,
"data": { "paymentId": "...", "storeId": "...", "status": "...", "request": {}, "result": {} }
}
Signing (opt in with webhookSecret)
Each delivery carries x-vela-timestamp and x-vela-signature headers, where the signature is base64 HMAC-SHA256 over timestamp + rawBody keyed with your webhookSecret. Verify against the raw request bytes and reject deliveries older than ~300 seconds.
// Node.js webhook verification
const crypto = require('crypto');
function verifyWebhook(headers, rawBody, webhookSecret) {
const timestamp = headers['x-vela-timestamp'];
const signature = headers['x-vela-signature'];
const expected = crypto.createHmac('sha256', webhookSecret)
.update(timestamp + rawBody) // raw request bytes, not re-serialized JSON
.digest('base64');
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
Delivery API (requestType: delivery)
Courier delivery (DoorDash Drive). Fees are charged to the merchant wallet. Requires an active delivery provider on the store.
actionType: getQuote
| Parameter | Type | Required | Description |
|---|---|---|---|
dropoffAddress |
object | Required | street, city, state, zipcode, ... |
orderSubtotal |
string | Optional | Order subtotal |
dropoffName / dropoffPhone / dropoffEmail / dropoffNotes |
string | Optional | Recipient details and courier notes |
tip |
int | Optional | Driver tip, in cents |
currency |
string | Optional | Default USD |
contactless |
boolean | Optional | Contactless dropoff |
orderNumber / orderId |
string | Optional | Your order references |
pickupReadyTimestamp |
int | Optional | When the order will be ready for pickup |
Response
{
"status": "ok",
"estimatedMinutes": 35,
"fee": "9.75",
"feeBreakdown": { "rawFeeCents": 875, "extraCents": 100, "totalCents": 975 }
}
actionType: deliver
Books the courier. Same request fields as getQuote. The merchant wallet must cover the full charge (courier fee + platform fee + tip) or the call fails without booking.
Response
{
"status": "ok",
"deliveryId": "...",
"externalDeliveryId": "...",
"trackingUrl": "...",
"deliveryStatus": "...",
"fee": "9.75",
"charge": { "...": "fee/tip breakdown" }
}
actionType: getStatus
Pass deliveryId — OR — externalDeliveryId.
Response
{
"status": "ok",
"deliveryId": "...",
"externalDeliveryId": "...",
"deliveryStatus": "...",
"trackingUrl": "...",
"events": [ ... ] // when tracked locally
}
actionType: cancel
Pass deliveryId (required).
Response
{
"status": "ok",
"deliveryId": "...",
"cancelled": true
}
actionType: getLocations
No parameters. Returns the store's pickup locations.
{
"status": "ok",
"defaultPickup": { ... },
"locations": [ ... ]
}
SMS API (requestType: sms)
actionType: send
Sends one text message; the cost is charged to the merchant wallet. Requires the store's SMS product to be configured.
| Parameter | Type | Required | Description |
|---|---|---|---|
to |
string | Required | Recipient phone number |
message |
string | Required | Message text |
from |
string | Optional | Sender number |
Response
{
"status": "ok",
"messageId": "...",
"messageStatus": "...",
"to": "...",
"from": "...",
"cost": "0.02",
"billingStatus": "..."
}
Wallet API (requestType: wallet)
The merchant wallet funds DELIVERY, SMS, and other platform products. Money crosses this API as decimal strings ("50.00").
actionType: getBalance
No parameters.
{
"status": "ok",
"balance": "50.00",
"pendingBalance": "0.00",
"availableBalance": "50.00",
"currency": "USD",
"walletStatus": "ACTIVE", // ACTIVE, SUSPENDED, or null
"autoRecharge": { "enabled": true, "threshold": "10.00", "amount": "50.00" },
"paymentMethodId": "..." // or null
}
actionType: topUp
paymentMethodId: string (required; a saved funding card)amount: string (required)
Response: paymentStatus, transactionId, balance.
actionType: getActivity
No parameters. Returns activity: the date-sorted wallet statement.
actionType: setAutoRecharge
enabled: booleanthreshold: string (required when enabling; recharge when available balance drops below)amount: string (required when enabling; amount to charge)paymentMethodId: string (required when enabling)
Response: autoRecharge, paymentMethodId.
actionType: addCard
Tokenizes and saves a merchant funding card. The card token is never returned.
cardNumber: string (required)cardExpDate: string (required, MMYY)cvv: string (required)name: string (required)description: string (optional)primary: boolean (optional)addressBilling: object (optional)
Response: card { id, name, description, cardType, cardNumber (masked), cardExpDate, status, primary, addressBilling }.
actionType: listCards
No parameters. Returns cards: array of saved funding cards (same shape as addCard).
actionType: updateCard
paymentMethodId: string (required)name,description,status,primary(mutable fields only)
actionType: deleteCard
paymentMethodId: string (required)
Response: deleted: true.
Campaign API (requestType: campaign)
Email/SMS campaign blasts over your own recipient lists. Compliance split: you certify your list has consent (setConsent); the platform owns suppression, scrubbing, footers and the unsubscribe path. send/schedule are refused until the campaign has a consent attestation.
Campaign Content Fields
Writable via create/update:
| Field | Type | Description |
|---|---|---|
name |
string | Campaign name |
channel |
string | "EMAIL" or "SMS" (default EMAIL) |
subject |
string | Email subject |
fromName |
string | Email sender name |
html |
string | Email body |
body |
string | SMS body |
audience |
object | { contactListId } — OR — { recipients: [...] } |
timezone |
string | Optional |
Campaign lifecycle: DRAFT → SCHEDULED → SENDING → SENT (or FAILED / CANCELLED). Content edits are DRAFT-only. Dispatch is asynchronous — send schedules for "now" and a background job fans it out within the minute.
Lifecycle Actions
| actionType | Request | Notes |
|---|---|---|
list |
— | All campaigns, newest first |
get |
campaignId |
|
create |
name (required) + content fields |
New DRAFT |
update |
campaignId + content fields |
DRAFT only |
duplicate |
campaignId, name? |
Copies content into a new DRAFT; consent is NOT copied |
delete |
campaignId |
Blocked mid-send |
schedule |
campaignId, scheduledAt (unix seconds) |
|
send |
campaignId |
Immediate (schedules for now) |
cancel |
campaignId |
Only before dispatch |
sendTest |
campaignId, to |
One preview message; no attestation needed |
getReport |
campaignId |
report { campaignStatus, stats, recipients breakdown } |
getAudienceCount |
one of recipients, contactListId, campaignId |
Deliverable count: cleaned, de-duped, minus suppressions |
getEstimate |
campaignId |
estimate { channel, recipientCount, segments, unitCost, total } |
setConsent |
campaignId, statement (required), source?, recipientCount?, ip? |
The consent attestation gating send |
getConsent |
campaignId |
attested + attestation audit trail |
Campaign responses return a campaign object: id, name, channel, subject, fromName, html, body, status, scheduledAt, timezone, audience ({ contactListId, recipientCount }), stats ({ received, invalid, duplicates, suppressed, deliverable, sent, failed }), sentAt, failureReason, created, modified. Audiences report a COUNT, never the addresses.
Saved Audiences
Reusable recipient lists, owned by the CAMPAIGN request type.
| actionType | Request | Notes |
|---|---|---|
addAudience |
name (required), contacts?, source? |
|
listAudiences |
— | |
getAudience |
audienceId |
|
updateAudience |
audienceId, name |
Rename only — contacts here is ignored |
replaceAudienceContacts |
audienceId, contacts (required; [] clears) |
Replaces the ENTIRE list |
deleteAudience |
audienceId |
Refused while a scheduled/sending campaign uses it |
Audience responses return an audience object: id, name, source, contactCount, created, modified.
API Permissions Reference
payment_process, payment_search, payment_getPaymentLink, payment_getPaymentInfo
delivery_getQuote, delivery_deliver, delivery_cancel, delivery_getStatus, delivery_getLocations
sms_send
wallet_getBalance, wallet_topUp, wallet_getActivity, wallet_setAutoRecharge,
wallet_addCard, wallet_listCards, wallet_updateCard, wallet_deleteCard, wallet_manage
campaign_list, campaign_get, campaign_create, campaign_update, campaign_duplicate,
campaign_delete, campaign_schedule, campaign_send, campaign_cancel,
campaign_getAudienceCount, campaign_getEstimate, campaign_sendTest, campaign_getReport,
campaign_setConsent, campaign_getConsent,
campaign_addAudience, campaign_listAudiences, campaign_getAudience,
campaign_updateAudience, campaign_replaceAudienceContacts, campaign_deleteAudience,
campaign_manage
Common Error Messages
"Not authenticated - Invalid API key or signature"— missing/wrongX-API-Key,X-Timestamp, orX-Signature"Missing request type"/"Missing action type""You do not have permission for {REQUESTTYPE}_{actionType}""Merchant payment processor not configured""Delivery provider not configured""SMS product not configured""Unsupported request type: ..."/"Unsupported action type: ...""Invalid paymentPageOptions.lightbox (ENABLED|DISABLED)""Invalid paymentPageOptions.customerLogin (SHOW|HIDE)"
Cloud API Tester
Response
Cloud API Integration Example
JavaScript/Node.js Example
const crypto = require('crypto');
// The API base URL is provided together with your API credentials by
// VelaPay Support. Keep it in server-side configuration -- do not
// hard-code it in client-side (browser or mobile) code.
const API_URL = process.env.VELAPAY_API_URL;
class VelaPayClient {
constructor(apiKey, apiSecret, baseUrl = API_URL) {
this.apiKey = apiKey;
this.apiSecret = apiSecret;
this.baseUrl = baseUrl;
}
generateSignature(timestamp, body) {
return crypto
.createHmac('sha256', this.apiSecret)
.update(timestamp + body)
.digest('hex');
}
async request(payload) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const body = JSON.stringify(payload);
const signature = this.generateSignature(timestamp, body);
const response = await fetch(this.baseUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': this.apiKey,
'X-Timestamp': timestamp,
'X-Signature': signature
},
body: body
});
return response.json();
}
// --- Payments ---
getPaymentLink(options) {
return this.request({
requestType: 'payment',
actionType: 'getPaymentLink',
...options
});
}
getPaymentInfo(paymentId) {
return this.request({
requestType: 'payment',
actionType: 'getPaymentInfo',
paymentId
});
}
processSale(amount, cardOrToken, options = {}) {
return this.request({
requestType: 'payment',
actionType: 'process',
transactionType: 'sale',
amount: amount,
...cardOrToken, // { cardNumber, cardExpDate, cvv } or { token } or { paymentMethodId }
...options
});
}
// --- Wallet / SMS / Delivery ---
getWalletBalance() {
return this.request({ requestType: 'wallet', actionType: 'getBalance' });
}
sendSms(to, message) {
return this.request({ requestType: 'sms', actionType: 'send', to, message });
}
getDeliveryQuote(dropoffAddress, options = {}) {
return this.request({
requestType: 'delivery',
actionType: 'getQuote',
dropoffAddress,
...options
});
}
}
// Usage Example: hosted payment link flow
async function collectPayment() {
const client = new VelaPayClient(
process.env.VELAPAY_API_KEY,
process.env.VELAPAY_API_SECRET
);
// 1. Create the payment link
const created = await client.getPaymentLink({
transactionType: 'sale',
amount: '99.99',
invoiceNumber: 'INV-2024-001',
email: 'customer@example.com',
returnUrl: 'https://yourapp.com/payment/complete',
webhook: 'https://yourapp.com/api/payment-webhook',
webhookFormat: 'standard',
webhookSecret: process.env.VELAPAY_WEBHOOK_SECRET
});
if (created.status !== 'ok') {
throw new Error(created.message);
}
// 2. Redirect the customer to created.link ...
// 3. Later (webhook or return): verify the result server-side
const info = await client.getPaymentInfo(created.paymentId);
if (info.cartStatus === 'COMPLETED' && info.result.paymentStatus === 'APPROVED') {
console.log('Payment confirmed:', info.result.referenceNumber);
}
}
collectPayment();