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
| Endpoint | Method | Description |
|---|---|---|
/merchants/\{merchant_id}/devices | POST | Create a new device |
/devices | GET | List all devices |
/devices/\{device_id} | GET | Fetch device details |
/devices/\{device_id} | PUT | Update device or perform action |
Device Metrics
| Endpoint | Method | Description |
|---|---|---|
/devices/\{device_id}/device_metrics | GET | Get device metrics (battery, signal, etc.) |
Device Actions
When updating a device with PUT, you can perform these actions:
| Action | Description | Required Fields |
|---|---|---|
ACTIVATE | Activate the device | activation_code |
DEACTIVATE | Deactivate the device | None |
CANCEL | Cancel the device | None |
REBOOT | Reboot the device | None |
CREATE_IDLE_MESSAGE | Set idle screen message | idle_message |
Configuration Options
| Field | Type | Description |
|---|---|---|
allow_debit | boolean | Allow debit card transactions |
prompt_amount_confirmation | boolean | Ask customer to confirm amount |
prompt_tip_on_screen | boolean | Enable tip prompts |
surcharge_basis_points | integer | Surcharge amount (e.g., 300 = 3%) |
tipping_details.percent_options | array | Tip percentage options |
Device Metrics
Metrics provide real-time device health information:
Connection
OPEN- Device is onlineCLOSED- Device is offline
Connectivity
network_type- WIFI, CELLULAR, or ETHERNETsignal_strength- POOR or GOOD
Power
battery_level_percent- Battery percentage (0-100)is_charging- Whether device is charging
System
android_version- Android OS versionfirmware_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;
}