VelaPay Manager API

Developer Portal & API Documentation

Version 1.2.01

Introduction

VelaPay provides two distinct APIs for payment processing and terminal management:

  1. Local Terminal API (In-Network): Direct communication with VelaPay terminals on your local network for real-time payment processing, device control, and terminal management.
  2. 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

  1. Request IDs: Always include a requestId for payment transactions to enable request/response logging and retrieval.
  2. Timeout Handling: Implement appropriate timeout handling on the client side, especially for payment and dialog requests that may require customer interaction.
  3. Error Recovery: Always check the status field in responses and handle errors appropriately with proper retry logic.
  4. 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.
  5. Concurrent Requests: Avoid sending multiple payment requests simultaneously as they will be rejected with "Another transaction in process" error.
  6. Signature Generation: Always compute the signature using the exact request body that will be sent (including whitespace and formatting).

Authentication Best Practices

  1. 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.
  2. 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.
  3. 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

  1. 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.
  2. Transaction Management: Store transaction IDs for refund/void operations. Implement idempotency for duplicate prevention. Log all transaction requests and responses.
  3. 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

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_secret and 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

print

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 transaction
  • auth - Authorization only
  • capture - Capture a previous authorization
  • return - Return/refund transaction
  • void - Void transaction
  • adjust - Adjust tip amount
  • batch - Batch settlement

Tender Types

  • credit - Credit card
  • debit - Debit card
  • EBT - Electronic Benefits Transfer
  • gift - Gift card
  • loyalty - 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 signature
  • button - 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" }

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

No request sent yet

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.

API base URL: the endpoint URL is not published in this documentation. It is provided together with your API Key and API Secret by VelaPay Support. Keep it in server-side configuration (e.g. an environment variable) — never hard-code it in browser or mobile client code.
POST <API_URL>          // provided by VelaPay Support
Content-Type: application/json

{
  "requestType": "payment",
  "actionType": "getPaymentLink",
  "transactionType": "sale",
  "amount": "10.00"
}
  • requestType is case-insensitive (payment and PAYMENT are equivalent).
  • actionType is 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 except search) requires the store to have an active payment processor configured.
  • DELIVERY requires an active delivery provider.
  • SMS requires 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 link
  • getPaymentInfo — look up a payment created with getPaymentLink
  • process — 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.

When to use Hosted Payment Page vs direct processing:
  • 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.

Your Server VelaPay Cloud API Customer Browser 1 POST getPaymentLink (amount, returnUrl, webhook, ...) 2 Returns paymentId + link (hosted payment page URL) 3 Redirect customer to payment link 4 Customer enters card 5 Submit payment 6 Process payment PARALLEL (after payment) 7a POST webhook (server-to-server) { paymentId, status, result: { ... } } 7b Redirect to returnUrl {returnUrl}?paymentId=...&status=approved 8 POST getPaymentInfo (verify payment result) 9 Returns full payment details { cartStatus, result: { paymentStatus, ... } }

Flow Summary

  1. Create Payment Link — Your server calls getPaymentLink with the amount, return URL, webhook URL, and optional customer/invoice details. VelaPay creates a payment session and returns a paymentId and a hosted payment page link.
  2. 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.
  3. Payment Processing — VelaPay processes the payment through the configured payment processor. No card data passes through your server.
  4. Dual Notification — After payment completes, two things happen in parallel:
    • Webhook (server-to-server) — VelaPay sends a POST request to your webhook URL 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 returnUrl with paymentId and status as query parameters so you can display the appropriate confirmation page.
  5. Verify Payment — Your server calls getPaymentInfo with the paymentId to retrieve the full payment details and confirm the transaction status. This is recommended as a final verification step.
Important: Do not rely solely on the 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: 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"
}

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: boolean
  • threshold: 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/wrong X-API-Key, X-Timestamp, or X-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

No request sent yet

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();