NNarya Developers

Send a heartbeat

POST
/device/v1/heartbeats

Reports the terminal's state and returns what it should run. The response lists the applications and parameters the terminal should hold. Send a heartbeat on a regular interval; Android and Linux terminals send the same request.

Only reported_at, platform and device_state are required. Send every other section the terminal can collect. Omitting software_inventory or parameter_state keeps the last one reported; it never clears them.

Narya keeps the snapshot with the latest reported_at. A snapshot collected earlier than one already received is acknowledged with 200 but not stored, even if it arrives later.

Authorization

DeviceAuth
headerAuthorizationBearer <dvc_<prefix>_<secret>>

The terminal's device token, returned once by POST /device/v1/activations. Send it as Authorization: Bearer <token> on every request except activation.

Request Body

application/json
  1. body

A snapshot of the terminal's state. Only reported_at, platform and device_state are required. Send every other section the terminal can collect, and omit any it cannot: an omitted section records nothing and never clears earlier data.

reported_at*string

When the terminal collected this snapshot, by its own clock. Narya keeps the most recent snapshot: one collected earlier than a snapshot already received is acknowledged but not stored.

Formatdate-time
sequence_number?integerDeprecated

Accepted and ignored. Do not send it.

Formatint64
Range0 <= value
agent?

The status of your terminal agent. Optional.

platform*
hardware?
device_state*
resources?
battery?
network?
location?
operations?
peripherals?
security?
diagnostics?
software_inventory?

The software installed on the terminal. Omitting software_inventory and sending an empty one mean opposite things. Omit it when the inventory was not collected this time, and Narya keeps the last one. Send {"complete": true, "components": []} only when nothing is installed, because a complete inventory replaces the stored list.

parameter_state?array<>

The parameter documents the terminal has applied, one entry per package. If you omit this field, Narya keeps the state last reported. Send an empty array to report that the terminal holds none.

collection?
extensions?

Additional diagnostics of your own, up to 16 KiB once encoded as JSON. Must not contain full IMEI, ICCID, IMSI, subscriber number or MAC address values.

Response Body

Received. The response lists what the terminal should hold and run; both lists are empty arrays, never null, when nothing is assigned.

application/json
  1. response

What the terminal should hold and run. The response identifies files but does not contain them: download a parameter document with GET /device/v1/parameters/{application_id}, and an application release with POST /device/v1/artifact-downloads.

parameters*array<>

One entry per application that has a parameter document for this terminal. If an application is absent, leave its current parameters as they are; never clear them.

applications*array<>

One entry per application with a decision for this terminal. If an application is absent, leave whatever is installed as it is; never uninstall software because it is absent.

These entries describe the state the terminal should reach, not commands to act on at once. Apply them at a time the terminal chooses.

curl -X POST 'https://device-narya-staging.pmadvisors.my/device/v1/heartbeats' \  -H 'Content-Type: application/json' \  -d '{  "reported_at": "2026-08-24T08:00:00Z",  "platform": {    "os_name": "Android",    "os_version": "14",    "api_level": 34,    "kernel_version": "5.15.123",    "security_patch": "2026-08-01",    "build_id": "UP1A.231005.007",    "baseband_version": "NL001.01.006",    "timezone_offset": "+08:00"  },  "hardware": {    "manufacturer": "NEXGO",    "model": "N950S",    "firmware_version": "Cr 3.55.02.49",    "unattended": false,    "cameras": [      {        "position": "back",        "model": "gc02m1",        "available": true      },      {        "position": "front",        "model": "gc030a",        "available": true      }    ]  },  "device_state": {    "uptime_seconds": 14924,    "booted_at": "2026-08-24T03:51:16Z",    "locked": false,    "screen_timeout_ms": 300000  },  "resources": {    "storage": {      "total_bytes": 8000000000,      "available_bytes": 4028211200    },    "memory": {      "total_bytes": 4294967296,      "available_bytes": 1800000000    }  },  "battery": {    "level_percent": 68,    "charging_status": "discharging",    "power_source": "battery",    "health_status": "good",    "cycle_count": 6  },  "network": {    "connected": true,    "active_transport": "wifi",    "internet_available": true,    "local_ip_addresses": [      "192.168.0.207"    ],    "wifi": {      "enabled": true,      "connected": true,      "signal_strength_dbm": -54    },    "bluetooth": {      "enabled": false    },    "cellular": {      "data_connected": false,      "sim_slots": [        {          "slot_index": 2,          "active": true,          "state": "ready",          "operator_code": "50216",          "operator_name": "Digi",          "access_technology": "4g",          "signal_strength_dbm": -94,          "signal_quality_percent": 61,          "roaming": false,          "apn": "myevo",          "iccid": "898828103456789012345",          "imei": "350077523237513",          "observed_at": "2026-08-24T08:00:00Z"        }      ]    }  },  "location": {    "latitude": 3.19786,    "longitude": 101.607937,    "altitude_meters": 56.3,    "accuracy_meters": 12.5,    "observed_at": "2026-08-24T07:59:58Z"  },  "operations": {    "contact_transaction_count": 10,    "contactless_transaction_count": 5,    "swipe_transaction_count": 3,    "print_count": 237,    "pending_upload_count": 0  },  "security": {    "adb_enabled": false,    "developer_mode_enabled": false,    "root_status": "not_detected",    "tamper_status": "none",    "hardware_attack_count": 1,    "secure_app_version": "MAPP_V9.55.20.66",    "secure_firmware_version": "Cr 3.55.02.49",    "secure_boot_version": "V03.020.011-006.002"  },  "diagnostics": {    "power_operation_count": 9,    "usb_operation_count": 6,    "screen_touch_count": 160,    "camera_open_count": 132  }}'
Example Response
{  "parameters": [    {      "application_id": "48ac72d0-a829-4896-a067-dcb1c2b0f30c",      "install_identifier": "string",      "digest": "string",      "schema_revision": 1,      "updated_at": "2019-08-24T14:15:22Z"    }  ],  "applications": [    {      "application_id": "48ac72d0-a829-4896-a067-dcb1c2b0f30c",      "application_name": "string",      "outcome": "assigned",      "reason": "string",      "desired_release_id": "58e42f39-089b-4d58-8622-78397444a592",      "desired_release_version": "string",      "artifact": {        "artifact_id": "b7bd6bbb-c7f1-4314-8742-2aeeeece9d12",        "size_bytes": 1,        "sha256": "string",        "file_name": "string",        "media_type": "string",        "install_identifier": "string",        "install_version": "string",        "version_code": 1      },      "uninstall": {        "install_identifier": "string"      },      "updated_at": "2019-08-24T14:15:22Z"    }  ]}