ECR Integration
Connect an existing ECR or POS system to a VINR terminal using the VINR Cashless API.
The VINR Cashless API lets existing ECR (Electronic Cash Register) and POS systems drive a VINR terminal as a payment peripheral — without building a full custom integration. Your ECR calls a simple HTTP API on the terminal over your local network. The terminal handles all card interactions and returns the result inline. Your ECR software never touches card data.
This is the right integration if:
- You already run a third-party POS, hospitality, retail management, or service desk system
- Your existing software can make outbound HTTP requests to an IP address on the local network
- You want the terminal to act as a self-contained payment peripheral rather than something your server code drives end-to-end
| Cloud API | Local API | Cashless API (this page) | |
|---|---|---|---|
| Who drives the terminal | Your server, via VINR cloud | Your custom POS app, direct LAN | Your existing ECR/POS, direct LAN |
| Requires VINR SDK | No (optional) | Recommended | No |
| Custom integration code | Required | Required | Minimal — call HTTP endpoints |
| Best for | Cloud POS, backend-driven | Custom POS, kiosk | Third-party POS/ECR, hospitality, retail |
How it worksAsk
The terminal runs a local HTTP server — the Cashless API — on port 8443. Your ECR calls it over the LAN. All calls are synchronous: the connection stays open while the cardholder interacts with the terminal, and the outcome (approved, declined, cancelled) is returned inline.
ECR/POS system ──HTTPS──▶ Terminal (LAN, port 8443)
│
Card interaction
│
──▶ Card network (authorization)
│
ECR/POS system ◀────────── Terminal (synchronous response)SetupAsk
1. Enable Cashless API mode
Go to Dashboard → Hardware → Terminals → [device] → Integrations → Cashless API and toggle it on. The terminal restarts into Cashless API mode.
2. Assign a static IP
The Cashless API requires a stable address. On your network router, create a DHCP reservation for the terminal's MAC address (printed on the device label and visible in the Dashboard under Hardware → Terminals → [device] → Network).
3. Generate a shared key
In the same Integrations → Cashless API panel, click Generate shared key. Copy it — it will not be shown again. Store it in your ECR configuration. To rotate the key, click Regenerate; the previous key is invalidated immediately.
Base URL and authenticationAsk
https://{terminal-ip}:8443/cashless/v1Include the shared key on every request:
POST /cashless/v1/sale HTTP/1.1
Host: 192.168.1.51:8443
Content-Type: application/json
X-VINR-Cashless-Key: ck_live_xxxxxxxxxxxxThe terminal presents a VINR-issued TLS certificate. Your ECR must trust VINR's root CA — download the CA bundle from Dashboard → Developers → Certificates and install it in your ECR's trust store. Requests over plain HTTP are rejected.
All Cashless API requests are POST with a JSON body. A successful response always contains "status": "approved". Error responses contain an "error" object with a "code" and "message".
Request timeoutAsk
Set your ECR's HTTP client timeout to 120 seconds. Card interactions can take up to 90 seconds (chip+PIN entry plus network authorization). A shorter timeout closes the connection before the transaction finishes, leaving the terminal in an indeterminate state. If your ECR times out, call /cashless/v1/status before retrying to confirm whether the previous transaction completed.
StatusAsk
Check whether the terminal is ready to accept a new payment request.
POST /cashless/v1/statusRequest body: {}
Response — ready:
{
"status": "ready",
"terminalId": "term_CT20_0042",
"model": "nexgo_ct20",
"softwareVersion": "3.14.2",
"lastTransaction": {
"reference": "POS-ORDER-0820",
"status": "approved",
"completedAt": "2026-06-11T10:14:00Z"
}
}Response — busy:
{
"status": "busy",
"activeTransaction": {
"reference": "POS-ORDER-0821",
"startedAt": "2026-06-11T10:15:03Z"
}
}Call this before starting a transaction if your ECR needs to confirm the terminal is idle. If status is busy and your ECR has no record of the active transaction, use /cashless/v1/cancel to abort it before starting a new one.
SaleAsk
Initiate a card-present payment. The terminal presents the amount to the customer, handles card entry and authorization, and returns the result synchronously.
POST /cashless/v1/saleRequest:
{
"amount": 2500,
"currency": "USD",
"reference": "POS-ORDER-0821",
"tipAmount": 300,
"printReceipt": true
}Prop
Type
Response — approved:
{
"status": "approved",
"reference": "POS-ORDER-0821",
"transactionId": "tpay_01HZ5QA7BK",
"amount": 2500,
"tipAmount": 300,
"totalAmount": 2800,
"currency": "USD",
"authCode": "A12345",
"entryMethod": "contactless",
"cardScheme": "visa",
"maskedPan": "************4242",
"cardholderName": "J SMITH",
"receiptData": {
"merchantReceipt": "VINR PAYMENTS\n...",
"customerReceipt": "VINR PAYMENTS\n..."
},
"completedAt": "2026-06-11T10:15:11Z"
}Response — declined:
{
"status": "declined",
"reference": "POS-ORDER-0821",
"declineCode": "card_declined",
"declineMessage": "Transaction not approved",
"completedAt": "2026-06-11T10:15:14Z"
}Pre-authorisationAsk
Reserve funds on the customer's card without capturing them immediately. Use this for hotel check-in, car rental, tab authorizations, and any scenario where the final amount is not known at card presentation.
Initiate a pre-auth
POST /cashless/v1/preauthRequest:
{
"amount": 10000,
"currency": "USD",
"reference": "HOTEL-STAY-1193"
}Response:
{
"status": "approved",
"reference": "HOTEL-STAY-1193",
"transactionId": "tpay_01HZ5QB3GH",
"amount": 10000,
"currency": "USD",
"authCode": "PA9821",
"entryMethod": "chip",
"cardScheme": "mastercard",
"maskedPan": "************5678",
"expiresAt": "2026-06-18T10:15:00Z",
"completedAt": "2026-06-11T10:15:22Z"
}Authorizations expire after 7 days. The expiresAt field shows the exact expiry. Complete or cancel the pre-auth before it expires — expired authorizations cannot be captured and the held funds are released back to the cardholder.
Complete a pre-auth
Capture the reserved funds when the final amount is known. The capture amount must be less than or equal to the original authorized amount.
POST /cashless/v1/preauth/completeRequest:
{
"originalReference": "HOTEL-STAY-1193",
"captureAmount": 8750,
"currency": "USD",
"reference": "HOTEL-STAY-1193-FINAL"
}Response:
{
"status": "approved",
"reference": "HOTEL-STAY-1193-FINAL",
"transactionId": "tpay_01HZ5QB4JK",
"captureAmount": 8750,
"currency": "USD",
"authCode": "CP4456",
"completedAt": "2026-06-15T11:45:00Z"
}CancelAsk
Abort the currently active transaction on the terminal. Use this when the cashier presses a cancel button on the ECR before the customer presents a card.
POST /cashless/v1/cancelRequest:
{
"reference": "POS-ORDER-0821"
}reference is optional. If omitted, the terminal cancels whatever transaction is currently active.
Response:
{
"status": "cancelled",
"reference": "POS-ORDER-0821",
"cancelledAt": "2026-06-11T10:16:00Z"
}Cancel only works while the terminal is displaying the payment prompt — before the customer presents a card. Once a card has been presented and authorization is in flight, the cancel request returns cancel_not_allowed. Wait for the transaction result and issue a refund if needed.
RefundAsk
Refund a previously completed sale. The refund routes back to the original card automatically — no card re-presentation required.
POST /cashless/v1/refundRequest:
{
"originalReference": "POS-ORDER-0821",
"amount": 2500,
"currency": "USD",
"reference": "POS-REFUND-0821",
"printReceipt": false
}Prop
Type
Response:
{
"status": "approved",
"reference": "POS-REFUND-0821",
"transactionId": "tpay_01HZ5QB5LM",
"amount": 2500,
"currency": "USD",
"originalTransactionId": "tpay_01HZ5QA7BK",
"completedAt": "2026-06-11T10:30:00Z"
}Partial refunds are supported — submit multiple refund requests against the same originalReference until the total refunded amount equals the original captured amount.
Batch managementAsk
Under normal operation, VINR settles your batch automatically at the end of each business day. If your ECR system manages its own end-of-day procedure and needs to trigger settlement explicitly, use the batch close endpoint.
POST /cashless/v1/batch/closeRequest:
{
"reference": "EOD-BATCH-20260611"
}Response:
{
"status": "closed",
"batchId": "batch_01HZ5QC6NO",
"reference": "EOD-BATCH-20260611",
"transactionCount": 47,
"totalAmount": 128450,
"currency": "USD",
"closedAt": "2026-06-11T23:59:00Z"
}Calling batch/close while automatic daily settlement is enabled is safe — VINR will not double-settle. A manual close mid-day settles all transactions up to that point; subsequent transactions open a new batch.
ReceiptsAsk
Get last receipt
Retrieve receipt data for the most recent transaction — useful if receiptData was not captured from the original sale response, or if you need to reformat it for printing through your ECR system.
POST /cashless/v1/receipt/lastRequest body: {}
Response:
{
"transactionId": "tpay_01HZ5QA7BK",
"reference": "POS-ORDER-0821",
"status": "approved",
"receiptData": {
"merchantReceipt": "VINR PAYMENTS\nDate: 11/06/2026\nAmount: $25.00\n...",
"customerReceipt": "VINR PAYMENTS\nDate: 11/06/2026\nAmount: $25.00\n...",
"signatureRequired": false
},
"completedAt": "2026-06-11T10:15:11Z"
}Reprint on terminal
Trigger the terminal's built-in printer to reprint the last receipt. Only available on models with a built-in printer: Nexgo N92, Nexgo CT20, Nexgo CT20P.
POST /cashless/v1/receipt/reprintRequest:
{
"type": "customer"
}type accepts "merchant", "customer", or "both". Defaults to "customer".
Response:
{
"status": "printed",
"type": "customer"
}Error codesAsk
All error responses use this shape:
{
"error": {
"code": "terminal_busy",
"message": "Another transaction is currently active on this terminal."
}
}| Code | HTTP status | Cause | Action |
|---|---|---|---|
invalid_key | 401 | Shared key missing or incorrect | Check the key in Dashboard → Integrations → Cashless API |
terminal_busy | 409 | Another transaction is active | Wait for it to complete, or call /cancel |
transaction_not_found | 404 | originalReference does not match a known transaction | Verify the reference value |
cancel_not_allowed | 422 | Card already presented; authorization in flight | Wait for the result, then refund if needed |
amount_exceeds_original | 422 | Refund amount exceeds the original captured amount | Reduce the refund amount |
preauth_expired | 422 | Pre-auth authorization has expired | Cannot capture; the held funds have been released |
printer_unavailable | 422 | Reprint requested on a model without a built-in printer | Use receipt/last to retrieve data and print via your ECR |
card_declined | 402 | Issuer declined the transaction | Ask the customer to try a different card |
communication_error | 503 | Terminal lost connectivity during processing | Retrieve status, then retry if still pending |
internal_error | 500 | Unexpected error | Retry once; contact support if it persists |
TestingAsk
A Cashless API simulator is available in every sandbox account:
https://cashless-simulator.sandbox.vinr.com/cashless/v1
X-VINR-Cashless-Key: ck_test_simulatorThe simulator supports all endpoints including batch close and receipt reprint. Use the same test card outcomes as the Cloud API:
| Card number | Outcome |
|---|---|
4242 4242 4242 4242 | Approved |
4000 0000 0000 0002 | card_declined |
4000 0000 0000 9995 | insufficient_funds |
4000 0000 0000 0069 | expired_card |
To test against a physical terminal in sandbox mode, provision it from Dashboard → Hardware → Add test terminal and generate a ck_test_... shared key.
Next stepsAsk
Cloud API
Drive terminals from your server through VINR's cloud — no local networking required.
Local API
Build a custom POS app with direct LAN communication and offline resilience.
Terminal management
Activate, assign, and monitor your terminal fleet.
Last updated on