Skip to main content

Devices API

Manage PAX payment terminals for in-person payment processing. The Devices API enables you to create, configure, activate, and monitor payment terminals used by merchants.

Overview

The Devices API provides:

  • Terminal provisioning - Create and configure PAX terminals
  • Device activation - Activate terminals with activation codes
  • Configuration management - Set surcharges, tipping, and payment preferences
  • Device actions - Activate, deactivate, reboot, and create idle messages
  • Metrics monitoring - Track connection status, battery, signal strength

Supported Device Models

  • PAX_A800 - Countertop terminal
  • PAX_A920PRO - Mobile terminal with cellular connectivity
  • PAX_D135 - Portable terminal

Use Cases

1. Create a New Device

Provision a new PAX terminal for a merchant:

async function createDevice(merchantId, deviceDetails) {
const response = await fetch(
`https://api.ahrvo.network/payments/na/merchants/${merchantId}/devices`,
{
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
description: deviceDetails.description,
model: deviceDetails.model, // PAX_A800, PAX_A920PRO, or PAX_D135
name: deviceDetails.name,
serial_number: deviceDetails.serialNumber,
configuration: {
allow_debit: deviceDetails.allowDebit !== false,
prompt_amount_confirmation: deviceDetails.promptAmount !== false,
prompt_tip_on_screen: deviceDetails.promptTipping || false,
surcharge_basis_points: deviceDetails.surchargeBps || 0,
tipping_details: deviceDetails.tipping || null
}
})
}
);

const device = await response.json();

console.log('Device Created:', {
id: device.id,
name: device.name,
model: device.model,
serial_number: device.serial_number
});

return device;
}

// Example: Create a terminal with tipping enabled
const terminal = await createDevice('MRxxxxxxxxxxxxxx', {
description: 'Front Desk Terminal',
model: 'PAX_A800',
name: 'Terminal #1',
serialNumber: '2210172205',
allowDebit: true,
promptAmount: true,
promptTipping: true,
surchargeBps: 300, // 3% surcharge
tipping: {
percent_options: [15, 18, 20, 25]
}
});

2. Activate a Device

Activate a terminal using an activation code:

async function activateDevice(deviceId, activationCode) {
const response = await fetch(
`https://api.ahrvo.network/payments/na/devices/${deviceId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json',
'Finix-Version': '2022-02-01'
},
body: JSON.stringify({
action: 'ACTIVATE',
activation_code: activationCode
})
}
);

const device = await response.json();

console.log('Device Activated:', {
id: device.id,
enabled: device.enabled,
name: device.name
});

return device;
}

// Example: Activate terminal
await activateDevice('DVxxxxxxxxxxxxxx', '123456');

3. Configure Terminal Settings

Update device configuration (surcharge, tipping, etc.):

async function updateDeviceConfiguration(deviceId, config) {
const response = await fetch(
`https://api.ahrvo.network/payments/na/devices/${deviceId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json',
'Finix-Version': '2022-02-01'
},
body: JSON.stringify({
configuration: {
allow_debit: config.allowDebit,
prompt_amount_confirmation: config.promptAmount,
prompt_tip_on_screen: config.promptTipping,
surcharge_basis_points: config.surchargeBps,
tipping_details: config.tippingDetails
}
})
}
);

const device = await response.json();

console.log('Configuration Updated:', {
id: device.id,
surcharge: `${device.configuration_details.surcharge_basis_points / 100}%`,
tipping_enabled: device.configuration_details.prompt_tip_on_screen
});

return device;
}

// Example: Enable 3% surcharge and tipping
await updateDeviceConfiguration('DVxxxxxxxxxxxxxx', {
allowDebit: true,
promptAmount: true,
promptTipping: true,
surchargeBps: 300, // 3%
tippingDetails: {
percent_options: [15, 18, 20, 25]
}
});

4. Fetch Device Details

Get information about a specific device:

async function getDeviceDetails(deviceId, includeConnection = false) {
const params = new URLSearchParams();
if (includeConnection) {
params.append('include_connection', 'true');
}

const url = `https://api.ahrvo.network/payments/na/devices/${deviceId}${params.toString() ? '?' + params.toString() : ''}`;

const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
}
});

const device = await response.json();

console.log('Device Details:', {
id: device.id,
name: device.name,
model: device.model,
serial_number: device.serial_number,
enabled: device.enabled,
connection: device.connection || 'Not included',
merchant: device.merchant
});

return device;
}

// Example: Get device with connection status
const device = await getDeviceDetails('DVxxxxxxxxxxxxxx', true);

5. List All Devices

Retrieve all devices, optionally filtered by model or tags:

async function listDevices(filters = {}) {
const params = new URLSearchParams();

if (filters.limit) params.append('limit', filters.limit);
if (filters.model) params.append('model', filters.model);
if (filters.tagKey) params.append('tags.key', filters.tagKey);

const response = await fetch(
`https://api.ahrvo.network/payments/na/devices?${params.toString()}`,
{
method: 'GET',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
}
}
);

const data = await response.json();
const devices = data._embedded?.devices || [];

console.log(`Found ${devices.length} devices`);

devices.forEach(device => {
console.log({
id: device.id,
name: device.name,
model: device.model,
enabled: device.enabled,
merchant: device.merchant
});
});

return devices;
}

// Example: List all PAX A800 terminals
const a800Terminals = await listDevices({ model: 'PAX_A800', limit: 100 });

6. Deactivate a Device

Deactivate a terminal (disables payment processing):

async function deactivateDevice(deviceId) {
const response = await fetch(
`https://api.ahrvo.network/payments/na/devices/${deviceId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json',
'Finix-Version': '2022-02-01'
},
body: JSON.stringify({
action: 'DEACTIVATE'
})
}
);

const device = await response.json();

console.log('Device Deactivated:', {
id: device.id,
enabled: device.enabled, // Should be false
name: device.name
});

return device;
}

// Example: Deactivate terminal
await deactivateDevice('DVxxxxxxxxxxxxxx');

7. Reboot a Device

Remotely reboot a terminal:

async function rebootDevice(deviceId) {
const response = await fetch(
`https://api.ahrvo.network/payments/na/devices/${deviceId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json',
'Finix-Version': '2022-02-01'
},
body: JSON.stringify({
action: 'REBOOT'
})
}
);

const device = await response.json();

console.log('Reboot Command Sent:', {
id: device.id,
name: device.name,
message: 'Device will reboot shortly'
});

return device;
}

// Example: Reboot terminal
await rebootDevice('DVxxxxxxxxxxxxxx');

8. Create Idle Message

Display a custom message on idle terminal screen:

async function createIdleMessage(deviceId, message) {
const response = await fetch(
`https://api.ahrvo.network/payments/na/devices/${deviceId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json',
'Finix-Version': '2022-02-01'
},
body: JSON.stringify({
action: 'CREATE_IDLE_MESSAGE',
idle_message: message
})
}
);

const device = await response.json();

console.log('Idle Message Set:', {
id: device.id,
message: message
});

return device;
}

// Example: Set welcome message
await createIdleMessage('DVxxxxxxxxxxxxxx', 'Welcome to ABC Coffee Shop!');

9. Get Device Metrics

Retrieve connectivity, battery, and system metrics:

async function getDeviceMetrics(deviceId, filters = {}) {
const params = new URLSearchParams();

if (filters.limit) params.append('limit', filters.limit);
if (filters.connection) params.append('connection', filters.connection); // OPEN or CLOSED
if (filters.isCharging !== undefined) params.append('power_is_charging', filters.isCharging);

const response = await fetch(
`https://api.ahrvo.network/payments/na/devices/${deviceId}/device_metrics?${params.toString()}`,
{
method: 'GET',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json'
}
}
);

const data = await response.json();
const metrics = data._embedded?.device_metrics || [];

console.log(`Found ${metrics.length} metrics entries`);

// Show latest metric
if (metrics.length > 0) {
const latest = metrics[0];
console.log('Latest Metrics:', {
timestamp: latest.created_at,
connection: latest.connection,
network: `${latest.connectivity?.network_type} - ${latest.connectivity?.signal_strength}`,
battery: `${latest.power?.battery_level_percent}% ${latest.power?.is_charging ? '(Charging)' : ''}`,
android: latest.system?.android_version,
firmware: latest.system?.firmware_version
});
}

return metrics;
}

// Example: Get device metrics
const metrics = await getDeviceMetrics('DVxxxxxxxxxxxxxx', { limit: 10 });

10. Monitor Device Health

Check if device is online and healthy:

async function checkDeviceHealth(deviceId) {
// Get device details with connection
const device = await getDeviceDetails(deviceId, true);

// Get latest metrics
const metrics = await getDeviceMetrics(deviceId, { limit: 1 });
const latestMetric = metrics[0];

const health = {
device_id: deviceId,
name: device.name,
model: device.model,
is_enabled: device.enabled,
connection_status: device.connection,
is_online: device.connection === 'Open',
battery_level: latestMetric?.power?.battery_level_percent || null,
is_charging: latestMetric?.power?.is_charging || null,
signal_strength: latestMetric?.connectivity?.signal_strength || null,
network_type: latestMetric?.connectivity?.network_type || null,
last_seen: latestMetric?.created_at || null
};

// Determine health status
if (!health.is_enabled) {
health.status = 'DISABLED';
health.alert = 'Device is disabled';
} else if (!health.is_online) {
health.status = 'OFFLINE';
health.alert = 'Device is offline';
} else if (health.battery_level !== null && health.battery_level < 20 && !health.is_charging) {
health.status = 'WARNING';
health.alert = 'Low battery (below 20%)';
} else if (health.signal_strength === 'POOR') {
health.status = 'WARNING';
health.alert = 'Poor signal strength';
} else {
health.status = 'HEALTHY';
health.alert = null;
}

console.log('Device Health:', health);

return health;
}

// Example: Monitor terminal health
const health = await checkDeviceHealth('DVxxxxxxxxxxxxxx');
if (health.status !== 'HEALTHY') {
console.warn('⚠️ Device health issue:', health.alert);
}

11. Bulk Device Management

Manage multiple devices at once:

async function bulkUpdateDevices(deviceIds, updates) {
const results = {
successful: [],
failed: []
};

for (const deviceId of deviceIds) {
try {
const response = await fetch(
`https://api.ahrvo.network/payments/na/devices/${deviceId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json',
'Finix-Version': '2022-02-01'
},
body: JSON.stringify(updates)
}
);

if (response.ok) {
const device = await response.json();
results.successful.push({ device_id: deviceId, name: device.name });
console.log(`✓ Updated device ${deviceId}`);
} else {
const error = await response.json();
results.failed.push({
device_id: deviceId,
error: error._embedded?.errors?.[0]?.message || 'Unknown error'
});
console.error(`✗ Failed to update device ${deviceId}`);
}

// Rate limiting
await new Promise(resolve => setTimeout(resolve, 500));

} catch (error) {
results.failed.push({ device_id: deviceId, error: error.message });
}
}

console.log(`Bulk update complete: ${results.successful.length} successful, ${results.failed.length} failed`);
return results;
}

// Example: Update surcharge for all terminals
const deviceIds = ['DV1xxxx', 'DV2xxxx', 'DV3xxxx'];
await bulkUpdateDevices(deviceIds, {
configuration: {
surcharge_basis_points: 300 // Set 3% surcharge
}
});

12. Device Provisioning Workflow

Complete workflow for setting up a new terminal:

async function provisionTerminal(merchantId, terminalInfo) {
console.log('Step 1: Creating device...');

// 1. Create device
const device = await createDevice(merchantId, {
description: terminalInfo.description,
model: terminalInfo.model,
name: terminalInfo.name,
serialNumber: terminalInfo.serialNumber,
allowDebit: true,
promptAmount: true,
promptTipping: terminalInfo.enableTipping,
surchargeBps: terminalInfo.surchargeBps || 0,
tipping: terminalInfo.enableTipping ? {
percent_options: [15, 18, 20, 25]
} : null
});

console.log('Device created:', device.id);

// 2. Set idle message
if (terminalInfo.idleMessage) {
console.log('Step 2: Setting idle message...');
await createIdleMessage(device.id, terminalInfo.idleMessage);
}

// 3. Activate device
if (terminalInfo.activationCode) {
console.log('Step 3: Activating device...');
await activateDevice(device.id, terminalInfo.activationCode);
} else {
console.log('Step 3: Skipped - No activation code provided');
console.log('Device will need manual activation');
}

// 4. Verify setup
console.log('Step 4: Verifying setup...');
const verifiedDevice = await getDeviceDetails(device.id, true);

const setupSummary = {
device_id: verifiedDevice.id,
name: verifiedDevice.name,
model: verifiedDevice.model,
serial_number: verifiedDevice.serial_number,
enabled: verifiedDevice.enabled,
connection: verifiedDevice.connection,
configuration: verifiedDevice.configuration_details,
ready_for_use: verifiedDevice.enabled && verifiedDevice.connection === 'Open'
};

console.log('Provisioning Complete:', setupSummary);

return setupSummary;
}

// Example: Provision a new terminal
const newTerminal = await provisionTerminal('MRxxxxxxxxxxxxxx', {
description: 'Main Counter Terminal',
model: 'PAX_A800',
name: 'Terminal #1',
serialNumber: '2210172205',
enableTipping: true,
surchargeBps: 300,
idleMessage: 'Welcome! Tap to pay.',
activationCode: '123456'
});

API Endpoints

Device Management

EndpointMethodDescription
/merchants/\{merchant_id}/devicesPOSTCreate a new device
/devicesGETList all devices
/devices/\{device_id}GETFetch device details
/devices/\{device_id}PUTUpdate device or perform action

Device Metrics

EndpointMethodDescription
/devices/\{device_id}/device_metricsGETGet device metrics (battery, signal, etc.)

Device Actions

When updating a device with PUT, you can perform these actions:

ActionDescriptionRequired Fields
ACTIVATEActivate the deviceactivation_code
DEACTIVATEDeactivate the deviceNone
CANCELCancel the deviceNone
REBOOTReboot the deviceNone
CREATE_IDLE_MESSAGESet idle screen messageidle_message

Configuration Options

FieldTypeDescription
allow_debitbooleanAllow debit card transactions
prompt_amount_confirmationbooleanAsk customer to confirm amount
prompt_tip_on_screenbooleanEnable tip prompts
surcharge_basis_pointsintegerSurcharge amount (e.g., 300 = 3%)
tipping_details.percent_optionsarrayTip percentage options

Device Metrics

Metrics provide real-time device health information:

Connection

  • OPEN - Device is online
  • CLOSED - Device is offline

Connectivity

  • network_type - WIFI, CELLULAR, or ETHERNET
  • signal_strength - POOR or GOOD

Power

  • battery_level_percent - Battery percentage (0-100)
  • is_charging - Whether device is charging

System

  • android_version - Android OS version
  • firmware_version - Device firmware version

Best Practices

1. Always Include Error Handling

async function safeDeviceAction(deviceId, action) {
try {
const response = await fetch(
`https://api.ahrvo.network/payments/na/devices/${deviceId}`,
{
method: 'PUT',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/json',
'Finix-Version': '2022-02-01'
},
body: JSON.stringify({ action })
}
);

if (!response.ok) {
const error = await response.json();
throw new Error(`Device action failed: ${error._embedded?.errors?.[0]?.message || 'Unknown error'}`);
}

return await response.json();

} catch (error) {
console.error(`Error performing ${action} on device ${deviceId}:`, error);
throw error;
}
}

2. Monitor Device Health Regularly

// Check device health every 5 minutes
setInterval(async () => {
const devices = await listDevices({ limit: 100 });

for (const device of devices) {
const health = await checkDeviceHealth(device.id);

if (health.status !== 'HEALTHY') {
await sendAlert({
type: 'device_health',
device_id: device.id,
status: health.status,
alert: health.alert
});
}
}
}, 5 * 60 * 1000);

3. Validate Configuration Before Updates

function validateConfiguration(config) {
const errors = [];

if (config.surcharge_basis_points !== undefined) {
if (config.surcharge_basis_points < 0 || config.surcharge_basis_points > 1000) {
errors.push('Surcharge must be between 0 and 1000 basis points (0-10%)');
}
}

if (config.tipping_details?.percent_options) {
const options = config.tipping_details.percent_options;
if (!Array.isArray(options) || options.length === 0) {
errors.push('Tipping percent_options must be a non-empty array');
}
if (options.some(opt => opt < 0 || opt > 100)) {
errors.push('Tip percentages must be between 0 and 100');
}
}

if (errors.length > 0) {
throw new Error('Configuration validation failed: ' + errors.join(', '));
}
}

4. Tag Devices for Organization

// When creating device, add tags
const device = await createDevice(merchantId, {
// ... other fields
tags: {
location: 'front_desk',
terminal_number: '1',
department: 'sales'
}
});

// Later, filter by tags
const frontDeskTerminals = await listDevices({
'tags.location': 'front_desk'
});

5. Cache Device Information

const deviceCache = new Map();

async function getCachedDevice(deviceId) {
if (deviceCache.has(deviceId)) {
const cached = deviceCache.get(deviceId);
if (Date.now() - cached.timestamp < 60000) { // 1 minute cache
return cached.device;
}
}

const device = await getDeviceDetails(deviceId);
deviceCache.set(deviceId, { device, timestamp: Date.now() });

return device;
}

Common Workflows

Terminal Replacement

async function replaceTerminal(oldDeviceId, newDeviceInfo) {
// 1. Get old device config
const oldDevice = await getDeviceDetails(oldDeviceId);

// 2. Deactivate old device
await deactivateDevice(oldDeviceId);

// 3. Create new device with same config
const newDevice = await createDevice(oldDevice.merchant, {
...newDeviceInfo,
// Copy configuration from old device
allowDebit: oldDevice.configuration_details.allow_debit,
promptAmount: oldDevice.configuration_details.prompt_amount_confirmation,
promptTipping: oldDevice.configuration_details.prompt_tip_on_screen,
surchargeBps: oldDevice.configuration_details.surcharge_basis_points,
tipping: oldDevice.configuration_details.tipping_details
});

console.log('Terminal replaced:', {
old_device: oldDeviceId,
new_device: newDevice.id
});

return newDevice;
}

Daily Health Check Report

async function dailyDeviceHealthReport() {
const devices = await listDevices({ limit: 1000 });

const report = {
total: devices.length,
online: 0,
offline: 0,
low_battery: 0,
poor_signal: 0,
devices_needing_attention: []
};

for (const device of devices) {
const health = await checkDeviceHealth(device.id);

if (health.is_online) {
report.online++;
} else {
report.offline++;
}

if (health.battery_level < 20) {
report.low_battery++;
}

if (health.signal_strength === 'POOR') {
report.poor_signal++;
}

if (health.status !== 'HEALTHY') {
report.devices_needing_attention.push({
id: device.id,
name: device.name,
issue: health.alert
});
}
}

// Send report to operations team
await sendEmail('ops@company.com', {
subject: 'Daily Device Health Report',
template: 'device_health_report',
data: report
});

return report;
}

Interactive API Reference