Terminal verification API
A reader sends a card or QR observation. BEXO returns a tenant-scoped decision using the server’s current entitlement evaluation.
Bind a key and a registered device
POS v1 requires a scoped tenant integration key with verify:write plus X-Terminal-Serial for a reader registered to the same tenant. An owner token is not accepted as a device key. A revoked or decommissioned reader must not continue verification.
Handle decisions separately from HTTP
| Response | Your client should |
|---|---|
| 200, verified=true, decision=allow | Display the allowed result and perform only the access action approved for this integration. |
| 200, verified=false, decision=deny | Display the supplied reason and a clear assistance path. Do not retry a normal denial as if it were a network error. |
| 401 / 403 | Stop automatic traffic and investigate credential, scope, approval or device state. |
| Timeout / 5xx | Show an unavailable state. Follow your approved recovery procedure; do not infer permission. |
Common reasons include active, grace, expired, suspended, member_not_found, no_membership_for_vendor and card_revoked_or_blocklisted. Handle unrecognized reason strings safely; do not infer access from human-readable text.
Clock and buffered synchronization
timestamp is optional observability input. The server’s clock is the authority when a tap is processed. Buffered requests are evaluated when synchronized; they are not proof that a person had valid access earlier. The batch endpoint processes items individually and reports successes and failures per item.
There is no documented idempotency key for POS taps or batches. Retrying can create duplicate log/usage records. Keep your own correlation record and confirm the supported duplicate-handling policy before automatic replay. Never treat offline buffering as authorization to unlock a gate.
Display only what is appropriate
Treat subscriber as nullable. Member names, photos and validity details are personal data, even when a response includes them. Keep the operational decision separate from disclosure consent; consult current member permissions before display, export or storage. Do not retain raw card identifiers in general logs.
billing records returned usage; it is not a quotation or a statement that your commercial agreement has changed. latencyMs is server processing time and excludes network/UI latency. X-Request-Id and logId help support investigate a request.
Endpoint reference
Paths below include the API prefix. The OpenAPI server URL already includes /api.
/api/pos/v1/pingCheck a registered reader connection
Requires verify:write scope and a registered terminal identifier. Returns vendor, terminal and serverTime; also updates terminal telemetry.
Parameters
| Name | Location | Requirement |
|---|---|---|
X-Terminal-Serial | header | Required. Registered reader serial in the same tenant as the integration key. |
Request template
Set the API base to include /api. Replace path placeholders before running.
curl --fail-with-body -X GET "$BEXO_API_BASE/pos/v1/ping" \
-H "Authorization: Bearer $BEXO_INTEGRATION_KEY" \
-H "X-Terminal-Serial: $BEXO_TERMINAL_SERIAL"Response fields
| Field | Type | Details |
|---|---|---|
status | string | online. |
vendor | object | See the downloadable schema for nested fields. |
terminal | Terminal | See the downloadable schema for nested fields. |
serverTime | string | Server ISO time. |
/api/pos/v1/verifyEvaluate a card or QR tap
Requires verify:write and a registered reader in the same tenant. The server evaluates current entitlement using its clock. HTTP 200 with decision=deny is a valid business result, not a transport failure. Treat all subscriber data as private and enforce the member’s current disclosure permissions in your client.
Parameters
| Name | Location | Requirement |
|---|---|---|
X-Terminal-Serial | header | Required. Registered reader serial in the same tenant as the integration key. |
Request template
Set the API base to include /api. Replace path placeholders and prepare request.json from the body fields below before running.
curl --fail-with-body -X POST "$BEXO_API_BASE/pos/v1/verify" \
-H "Authorization: Bearer $BEXO_INTEGRATION_KEY" \
-H "X-Terminal-Serial: $BEXO_TERMINAL_SERIAL" \
-H "Content-Type: application/json" \
--data @request.jsonRequest body
| Field | Type | Details |
|---|---|---|
cardUid | string | Raw card read, if using a registered NFC reader. Keep private. |
qrPayload | string | Alternative QR payload. At least one of cardUid or qrPayload is required. |
timestamp | string | Optional ISO time reported by the terminal. Server time controls entitlement. |
Example request body
{
"cardUid": "<CARD_UID_READ_BY_REGISTERED_READER>",
"timestamp": "2026-10-19T09:00:00Z"
}Response fields
| Field | Type | Details |
|---|---|---|
verifiedRequired | boolean | Access outcome; use alongside decision. |
decisionRequired | string | Operational outcome.allow · deny |
decisionReasonRequired | string | Reason such as active, expired, no_membership_for_vendor or member_not_found. |
status | string | Membership/lookup status. |
subscriber | object · nullable | See the downloadable schema for nested fields. |
terminal | object · nullable | See the downloadable schema for nested fields. |
billing | object | See the downloadable schema for nested fields. |
latencyMs | integer | Server evaluation time. |
timestamp | string | Server evaluation time. |
logId | string · nullable | Log reference. |
{
"verified": false,
"decision": "deny",
"decisionReason": "member_not_found",
"status": "not_found",
"subscriber": null,
"terminal": {
"id": "11111111-1111-4111-8111-111111111111",
"label": "Reception",
"serialNumber": "<REGISTERED_TERMINAL_SERIAL>",
"hardwareModel": "BEXO-POS-T1"
},
"billing": {
"billedPaise": 50,
"costRupees": 0.5,
"costDisplay": "₹0.50"
},
"latencyMs": 18,
"timestamp": "2026-10-19T09:00:00Z",
"logId": "11111111-1111-4111-8111-111111111111"
}/api/pos/v1/batch-verifySynchronize buffered tap requests
Accepts {taps:[…]}, a raw array, or a single tap. Prefer the taps object. Results are evaluated by the server at synchronization time; this is not offline authorization. Retries can create new log/usage entries because no batch idempotency key is documented.
Parameters
| Name | Location | Requirement |
|---|---|---|
X-Terminal-Serial | header | Required. Registered reader serial in the same tenant as the integration key. |
Request template
Set the API base to include /api. Replace path placeholders and prepare request.json from the body fields below before running.
curl --fail-with-body -X POST "$BEXO_API_BASE/pos/v1/batch-verify" \
-H "Authorization: Bearer $BEXO_INTEGRATION_KEY" \
-H "X-Terminal-Serial: $BEXO_TERMINAL_SERIAL" \
-H "Content-Type: application/json" \
--data @request.jsonRequest body
| Field | Type | Details |
|---|---|---|
tapsRequired | array | See the downloadable schema for nested fields. |
Response fields
| Field | Type | Details |
|---|---|---|
processedCount | integer | Results processed. |
results | array | See the downloadable schema for nested fields. |