Nōwn Meal Plan System
REST API Guide
Last updated: 2026-05-14
This guide documents the REST API for the Nōwn Meal Plan System (MPS). It is written for external integrators who need to look up customer meal plans and process transactions against them.
Contents
- Getting started
- Conventions
- Patron lookup
- Transactions
- Other endpoints
- End-to-end walkthrough
- Error code reference
- Changelog
Getting started
1. Request credentials
Contact support@nownpos.com to receive credentials for your environment. You will be given two values:
- Host: the base URL for all API requests, e.g.
https://api.example.com. - Auth key: a token used in the
Authorizationheader for every request.
Treat the auth key as a secret. We recommend storing it in a configuration file or secret manager keyed per location, so that transactions can be uniquely attributed to a location in the MPS dashboard.
Migrating from the v1.1 PDF guide? Previous versions of this guide instructed integrators to base64-decode a wrapper blob to extract the host and auth key. The new onboarding flow gives you both values directly. No decoding needed.
2. Make your first request
Replace {HOST} and {AUTH_KEY} with the values you received.
curl '{HOST}/payment/gateway/version' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {AUTH_KEY}'
Expected response:
{
"success": true,
"version": "1.0.0"
}
If you receive 401 Unauthorized, recheck the Authorization header. See Authentication below.
Conventions
Base URL
All endpoints are mounted under /payment/gateway on the host you were issued. Throughout this guide, {HOST} is a placeholder for your assigned host (for example, https://api.example.com).
Authentication
Every request must include an Authorization header with the Bearer scheme:
Authorization: Bearer {AUTH_KEY}
The auth key is a single token. It is not a base64-encoded user:password pair. Send it verbatim.
Note for integrators upgrading from the v1.1 PDF: Earlier versions of this guide instructed using
Authorization: Basic <auth-key>. That instruction was incorrect. The server validates the token against itsAccessTokentable via the Bearer / OAuth auth path; the Basic-auth path expects a different format (user:passwordbase64-encoded) and will reject your token. If you have an existing integration usingBasic, switch toBearer. The token value does not change, only the scheme keyword.
Response envelope
Most endpoints return a JSON envelope of the form:
{
"success": true,
"data": { ... },
"error": null
}
success(boolean):trueif the request was accepted and processed.data(object | null): present on success; shape depends on the endpoint.error(object | null): present on failure; see Error model.
A few endpoints (last-transaction, version) return the payload directly without the envelope. These are noted in their individual sections.
Error model
When success is false, the response body includes an error object:
{
"success": false,
"error": {
"reason": "Insufficient Tender Amount",
"code": 100002
}
}
reason(string): human-readable error message.code(integer): numeric error code. See the Error code reference for the full list.
Endpoints that do not use the envelope (last-transaction, version) return errors as non-2xx HTTP status codes — see HTTP status codes for the body shape.
HTTP status codes
Most operations return HTTP 200 with the response envelope — business-logic failures (validation, patron not found, insufficient balance) still return 200. Check success in the envelope, not the HTTP status.
A few classes of error use real HTTP status codes:
| Status | Cause | Body shape |
|---|---|---|
200 |
Request reached the endpoint. Includes business-logic failures. | {success, data, error} envelope. |
400 |
Server-state precondition failed before business logic ran (e.g. no active cashier shift, no POS station configured at this location). | {error: "<message>", exception: {appCode: <code>, message: "<reason>"}} |
401 |
Missing or invalid Authorization header. |
Same as 400 for most endpoints; {code: <code>, message: "<reason>"} for /last-transaction. |
500 |
Uncaught server error. | {error: "<message>"} |
Recommended client behavior:
- Check the HTTP status code first.
- On
200, inspect the envelope'ssuccessfield. - On non-2xx, parse the appropriate error body. The numeric error code lives at
exception.appCode(most endpoints) orcode(/last-transaction).
Note: The error body shape on non-2xx responses differs from the envelope's
errorobject. The envelope uses{reason, code}; non-2xx responses use{error, exception: {appCode, message}}(or{code, message}for/last-transaction). Plan your error-parsing code accordingly.
Idempotency
POST /payment/gateway/direct-transaction is idempotent on transactionUuid. If a request with the same transactionUuid (and same authenticated user) has already been processed, the server returns the original response without re-executing the transaction. There is no double-charge and no error.
You should:
- Generate a fresh
transactionUuid(RFC 4122 v4, 36 characters) for every new transaction. - On a network error or timeout where you do not have a confirmed response, retry with the same
transactionUuidrather than generating a new one. The server will return the original result if the first attempt actually completed.
Field naming gotchas
A few details that catch integrators by surprise:
itemPriceanditemTotalPriceare the canonical field names on receipt items, notpriceandtotal. Older clients may have usedprice/total; those names still deserialize but fail validation ondirect-transaction. UseitemPriceanditemTotalPrice.
Patron lookup
GET /payment/gateway/patron
Looks up a patron by their Nōwn customer-app identifier (QR code value). This endpoint also decrypts the rotating QR format used by the Nōwn customer app.
Side effect: This endpoint may write to the patron's meal plan service period if one is active. This is intentional and ensures subsequent
direct-transactioncalls do not contend on the patron row.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
patronKey |
string | Yes | The patron identifier scanned from the customer's app. URL-encode this if it contains +, /, =, or spaces. |
Request
curl '{HOST}/payment/gateway/patron?patronKey={URL_ENCODED_PATRON_KEY}' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {AUTH_KEY}'
Response: success
{
"success": true,
"data": {
"valid": true,
"patronId": 10001,
"companyId": 1,
"patronKey": "PATRON-0001",
"patronCompanyKey": "PATRON-0001",
"emailAddress": "patron@example.com",
"firstName": "Pat",
"lastName": "Patron",
"password": "",
"status": 0,
"birthDate": null,
"gender": null,
"homeTown": null,
"homeState": null,
"homeCountry": null,
"academicYear": null,
"major": null,
"athlete": null,
"athleteSport": null,
"dorm": null,
"source": 0,
"hasExpiredMealPlans": false,
"photoUrl": null,
"lucovaUserName": "abc123def4567890",
"hasCreditCard": true,
"applicableMealEquivalency": null,
"fiitLocationId": null,
"fiitPosStationId": null,
"fiitCashierShiftId": null,
"fiitLocationOverrideAllowed": false,
"fiitLastTransaction": null,
"overridePatronCompanyKey": 0,
"mealPlans": [
{
"mealPlanId": 1,
"companyId": 1,
"name": "Unlimited Coffee 100",
"mealPlanDescription": "",
"mealPlanType": "DCB",
"enabled": true,
"mealPlanRunning": true,
"mealPlanApplicable": true,
"isMealPlanApplicable": true,
"mealPlanAvailable": true,
"isAvailable": true,
"unavailablityReasons": {
"unavailableAtLocation": false,
"expired": false,
"noPriority": false,
"disabled": false,
"noBalance": false,
"active": true,
"ruleApplied": false
},
"patronMealRuleDeductions": [],
"rules": [],
"initialBalance": 100,
"currentDcbBalance": 95.5,
"currentMealPlanBalance": 0,
"currentChargeBalance": 0,
"remainingServicePeriodDcb": 95.5,
"remainingServicePeriodMeals": 0,
"remainingServicePeriodCharge": 0,
"mealsPerPeriod": 0,
"mealsPerServicePeriod": 0,
"priority": 2,
"taxFree": false,
"guestPlan": false,
"mealEquivalencyEnabled": true,
"maxFlexAmountPerTransaction": 100,
"maxTransactionServicePeriod": 0,
"chargeBalance": 0,
"flexAmount": 0,
"mealPlanCyclePeriod": "WEEKLY",
"mealPlanResetDay": "SATURDAY",
"mealPlanResetMonth": null,
"resetPeriodEnd": true,
"forceReset": false,
"isForceReset": false,
"importExclusionEnabled": false,
"archivedBalanceDeducted": false,
"expired": null,
"startDateTime": 1691539200000,
"endDateTime": 1782863999000,
"mealPlanResetTimeMillis": 1778043600000,
"mealPlanResetDateMillis": 1736553600000,
"servicePeriodEndMillis": 1778817540000,
"nextResetDateMillis": 1778907600000,
"lastResetDateMillis": 1778302800000,
"patronMealPlanId": 10001,
"patronId": 10001,
"patronKey": null,
"patronCompanyKey": null,
"emailAddress": "",
"firstName": "",
"lastName": ""
}
]
}
}
Response: patron not found
{
"success": false,
"error": { "reason": "Invalid Patron: Not Found", "code": 101 }
}
Notes
- Use
mealPlanAvailable/isAvailableto decide whether a meal plan can be charged. Iffalse, inspectunavailablityReasonsfor why. Common reason:active: falsemeans there is no current active service period for the plan (e.g. a breakfast-only plan looked up at dinner time). lucovaUserNameis the patron's app-side identifier. Save it from this response and pass it todirect-transactionwhen charging the patron.hasCreditCard: trueindicates the patron has a credit card on file in the Nōwn app. Use this to decide whether to fall back to a credit-card charge when no meal plan is available.mealPlanTypevalues includeDCB(declining cash balance) andMEAL(meal-swipe plan).- Some fields are legacy aliases that mirror each other:
mealPlanApplicable/isMealPlanApplicable,mealPlanAvailable/isAvailable. Either name works; new code should prefer the non-is-prefixed form. - Many patron-profile fields (
birthDate,gender,homeTown, etc.) are typicallynullfor integration use cases.
Transactions
POST /payment/gateway/transaction/estimate
Calculates what a transaction would look like without committing it. Useful for showing the cashier a tender breakdown (DCB applied, remaining balance to charge to credit card, taxes) before confirming the sale.
Request body: same shape as direct-transaction.
Request
curl '{HOST}/payment/gateway/transaction/estimate' \
-X POST \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {AUTH_KEY}' \
--data-raw '{
"requestAmount": 4.50,
"request": {
"patronId": 10001,
"subtotal": 4.50,
"totalTaxes": 0,
"totalSales": 4.50,
"receiptItems": [
{ "quantity": 1, "name": "Blueberry Yogurt Parfait", "itemPrice": 4.50, "itemTotalPrice": 4.50 }
]
}
}'
Response: success
{
"success": true,
"data": {
"approvedAmount": 4.50,
"approvedDCBAmount": 4.50,
"approvedMealEqAmount": 0,
"approvedMeal": 0,
"outstandingAmount": 0,
"partiallyApproved": false,
"items": [ /* per-item calculation breakdown */ ]
}
}
Response fields
| Field | Type | Description |
|---|---|---|
approvedAmount |
number | Total amount the patron's plans can cover. |
approvedDCBAmount |
number | Amount covered by Declining Cash Balance (DCB) plans. |
approvedMealEqAmount |
number | Amount covered by meal equivalency. |
approvedMeal |
integer | Count of meal-swipe redemptions. |
outstandingAmount |
number | Amount not covered by any plan. Must be settled out-of-band (cash, credit card, etc.). |
partiallyApproved |
boolean | true if some but not all of the request is covered. |
items |
array | Per-item calculation results. |
Notes
estimaterequires an active cashier shift at the location's default POS station. If none exists, the response isNO_ACTIVE_SHIFT(code 407).estimateis read-only and does not consume any balance.
POST /payment/gateway/direct-transaction
Charges a transaction directly to the patron's account. This is the primary transaction endpoint for most integrations.
Required headers
Content-Type: application/json
Authorization: Bearer {AUTH_KEY}
Request body
| Field | Type | Required | Description |
|---|---|---|---|
requestAmount |
number | Yes | Total amount to charge, in dollars. Must be > 0. |
request.transactionUuid |
string | Yes | RFC 4122 v4 UUID. Used as an idempotency key; retry the same UUID on network failures. |
request.lucovaUserName |
string | Yes | Patron's app username, taken from the patron-lookup response (lucovaUserName field). |
request.patronId |
long | No (see below) | Patron's numeric ID. Omit or set to -1 to charge a credit card on file instead of a meal plan. |
request.dcbAmount |
number | No | Amount to draw from DCB. If omitted, the server calculates the split for you. |
request.creditCardAmount |
number | No | Amount to charge to a credit card on file. |
request.debitCardAmount |
number | No | Amount to charge to a debit card. |
request.subtotal |
number | Yes | Subtotal of the order. |
request.totalTaxes |
number | Yes | Total taxes. Required even if zero, especially for taxable DCB or credit-card-on-file transactions. |
request.totalSales |
number | Yes | Total sales (subtotal + taxes). |
request.receiptItems |
array | Yes | Non-empty list of items. See below. |
Receipt item fields
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Non-empty. |
quantity |
number | Yes | Must be > 0. |
itemPrice |
number | Yes | Unit price. Must be > 0. Note: use itemPrice, not price. |
itemTotalPrice |
number | Yes (if line total > 0) | Line-item total. Use itemTotalPrice, not total. |
Charging a credit card on file
To charge a customer's credit card stored in the Nōwn app:
- Confirm the patron has one:
hasCreditCard: truein the patron-lookup response. - Submit
direct-transactionwithout apatronId(or withpatronId: -1). - Set
creditCardAmountto the amount to charge. - Still include
lucovaUserNameandtransactionUuid.
Request
curl '{HOST}/payment/gateway/direct-transaction' \
-X POST \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {AUTH_KEY}' \
--data-raw '{
"requestAmount": 4.50,
"request": {
"patronId": 10001,
"dcbAmount": 4.50,
"creditCardAmount": 0,
"debitCardAmount": 0,
"subtotal": 4.50,
"totalTaxes": 0,
"totalSales": 4.50,
"receiptItems": [
{
"quantity": 1,
"name": "Blueberry Yogurt Parfait",
"itemPrice": 4.50,
"itemTotalPrice": 4.50
}
],
"lucovaUserName": "abc123def456",
"transactionUuid": "11111111-2222-4333-8444-555555555555"
}
}'
Response: success
{
"success": true,
"data": {
"success": true,
"transaction": {
"preauth": false,
"amount_cents": 450,
"tax_cents": 0,
"discount_cents": 0,
"transaction_type": "direct",
"status": "completed",
"transaction_opened": 1747227024,
"transaction_closed": 1747227024,
"transaction_opened_obj": "2026-05-14T13:30:24Z",
"transaction_opened_hr": "2026-05-14 13:30:24 +0000",
"user_name": "abc123def456",
"short_id": "12345678",
"merchant_id": "...",
"node_id": "...",
"node_name": "Example Cafeteria",
"pos_order_id": "...",
"summary": "$4.50"
},
"message": null,
"error_code": 0
}
}
Response fields (top-level under data.transaction)
| Field | Type | Notes |
|---|---|---|
status |
string | "completed" on success, otherwise see message and error_code. |
amount_cents |
integer | Transaction total in cents. |
tax_cents |
integer | Tax in cents. |
discount_cents |
integer | Discount in cents. |
transaction_type |
string | "direct" for this endpoint. |
transaction_opened / transaction_closed |
integer | Epoch seconds. |
transaction_opened_obj |
string | ISO-8601 UTC. Prefer this for parsing. |
transaction_opened_hr |
string | Human-readable. |
user_name |
string | Patron's app username. |
short_id |
string | Short human-friendly transaction identifier. |
Response: failure
If the request is rejected before reaching the payment processor (validation, no POS station, no active shift), the envelope returns success: false with an error object. If the payment processor rejects the transaction, you'll get success: true at the envelope level but data.success: false and data.message / data.error_code set.
Common validation errors
error.reason |
Cause |
|---|---|
"A unique Transaction UUID is required" |
transactionUuid missing or empty. |
"lucovaUserName is required" |
lucovaUserName missing or empty. |
"requestAmount must be greater than zero" |
requestAmount <= 0. |
"receiptItems must not be empty" |
receiptItems missing or empty. |
"Each receipt item must have a name" |
One of the items has no name. |
"Receipt item '<name>' must have a quantity greater than zero" |
Item quantity <= 0. |
"Receipt item '<name>' is missing itemPrice" |
Item itemPrice <= 0. |
"Receipt item '<name>' is missing itemTotalPrice" |
Item has total > 0 but itemTotalPrice <= 0. (Most likely cause: you sent price/total instead of itemPrice/itemTotalPrice.) |
POST /payment/gateway/transaction/refund
Refunds a previously processed transaction.
Required headers: same as direct-transaction.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
transactionId |
long | Yes | The MPS transaction ID to refund. Obtain it via GET /payment/gateway/last-transaction?patronId={patronId}. |
description |
string | No | Optional note attached to the refund. |
transactionUuid |
string | No | Idempotency key for the refund itself. |
toRefundServicePeriodBalance |
boolean | No | Whether to restore the original service-period balance. |
refundType |
string | No | Reserved. |
fiitLocationId |
long | No | Override the location. |
paymentsToRefund |
array | No | List of specific payment tenders to refund (partial refunds). Each entry: { "tenderType": string, "amount": number }. |
nownRefund |
boolean | No | Set true if the refund originates from the Nōwn POS side (different processing path). |
Request
curl '{HOST}/payment/gateway/transaction/refund' \
-X POST \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {AUTH_KEY}' \
--data-raw '{
"transactionId": 5001,
"description": "Customer return: item out of stock",
"transactionUuid": "99999999-2222-4333-8444-555555555555"
}'
Response: success
{
"success": true,
"data": {
"transactionId": 5001,
"patronName": "Pat Patron",
"userName": "abc123def456",
"servicePeriodName": "Week 19",
"posStationName": "Station 1",
"dcbAmount": 4.50,
"cashAmount": 0,
"changeAmount": 0,
"creditCardAmount": 0,
"totalTaxes": 0,
"totalSales": 4.50,
"totalDiscount": 0,
"time": "2026-05-14T14:02:11Z"
}
}
Other endpoints
GET /payment/gateway/last-transaction
Returns the most recent transaction for a patron. Returns the LastTransactionDTO directly (no envelope).
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
patronId |
string | Yes | Patron's numeric ID, accepted as a string for graceful error reporting. |
Response
{
"transactionId": 5001,
"locationName": "Example Cafeteria",
"paymentMethod": "DCB",
"transactionDateTime": "2026-05-14T13:30:24Z",
"elapsedTime": 12,
"elapsedTimeUnit": "minutes",
"tenders": { "DCB": 450 }
}
tenders is a map of tender-type → amount in cents.
GET /payment/gateway/location
Returns the location associated with the auth key.
Request
curl '{HOST}/payment/gateway/location' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {AUTH_KEY}'
Response: success
{
"success": true,
"data": {
"locationId": 1,
"companyId": 1,
"nodeId": "node-example",
"locationName": "Example Cafeteria",
"mealPlanIds": [1, 2],
"mealPlans": [ /* MealPlanDTO list */ ],
"servicePeriodIds": [10, 11],
"servicePeriods": [ /* ServicePeriodDTO list */ ],
"tenderTypeIds": [1, 2, 3],
"overrideAllowed": false
}
}
Useful for confirming which location a given auth key is provisioned for, and which meal plans are configured at that location.
GET /payment/gateway/version
Returns the MPS calculation API version. Useful as a health check.
Response
{ "success": true, "version": "1.0.0" }
This endpoint returns the payload directly (no {success, data} envelope around it).
End-to-end walkthrough
The typical integration flow for a single transaction:
Look up the patron with
GET /payment/gateway/patron?patronKey=<URL_ENCODED_PATRON_KEY>.Check whether to charge a meal plan or a credit card. Look at the response:
- If any meal plan has
mealPlanAvailable: true, charge the patron's meal plan. Note thelucovaUserNamefrom the response. - If no plan is available but
hasCreditCard: true, charge their credit card on file. - If neither is available, prompt the cashier for an alternative payment method.
- If any meal plan has
(Optional) Preview the tender split with
POST /payment/gateway/transaction/estimateto show the cashier what will be charged to DCB vs. credit card.Charge the patron with
POST /payment/gateway/direct-transaction:- Generate a fresh
transactionUuid(UUID v4). - Include the
lucovaUserNamefrom the patron-lookup response. - Include
itemPriceanditemTotalPrice(notprice/total) on each receipt item. - On network errors, retry with the same
transactionUuid. The server is idempotent.
- Generate a fresh
Persist
short_idfrom the response for reconciliation and customer-facing receipts.Refund later if needed: look up the
transactionIdviaGET /payment/gateway/last-transaction?patronId={patronId}, then callPOST /payment/gateway/transaction/refundwith it.
Error code reference
Error codes fall into four groups:
- Common errors (codes
400–413): general operational and validation errors that any endpoint may return. - MPS-specific errors (codes
1000–1001): returned by/last-transaction. - Gateway calculation errors (codes
100001–100002): issues talking to the meal-plan calculation server. - Validation errors (no numeric code): request-shape failures on
direct-transaction. Thecodefield is0or omitted; thereasonfield carries the human-readable message.
In addition, the direct-transaction endpoint may surface passthrough error codes from the downstream payment processor under data.error_code when data.success is false. These are not MPS codes. Contact engineering@nownpos.com for guidance on specific codes.
Common errors
| Code | Name | Reason |
|---|---|---|
| 101 | (legacy, no enum name) | Invalid Patron: Not Found. Returned by /patron (v1) when the QR/key does not resolve to a known patron. |
| 400 | NOT_FOUND |
Unable to find object |
| 401 | INVALID |
Invalid State. Generic invalid-state error; the accompanying message usually explains what was invalid (e.g. Insufficient tender amount, Invalid transaction state. Transaction is a patron transaction without a patron id.). |
| 402 | ACTIVE_SHIFT_FOUND |
User already has an active shift |
| 403 | INSUFFICIENT_FUNDS |
Insufficient Funds |
| 404 | GENERIC |
Generic Error |
| 405 | TRANSACTION_REFUND_INVALID |
Transaction Refund Failed. The refund could not be processed. |
| 406 | FORBIDDEN |
Forbidden |
| 407 | NO_ACTIVE_SHIFT |
User does not have an active shift. The location has no open cashier shift; required by estimate and other endpoints that compute service-period state. |
| 408 | UNAUTHENTICATED |
User is not authenticated |
| 409 | PATRON_RELOAD_DISABLED |
Patron is not allowed to reload dcb |
| 410 | NO_POS_STATION |
Location does not have a pos station. The location has no default POS station configured. |
| 411 | NO_PATRON_FOUND |
Patron not found |
| 412 | NO_SERVICE_PERIOD |
No active FIIT service period |
| 413 | PATRON_STATUS_INACTIVE |
Patron status is not active |
MPS-specific errors
Returned by /last-transaction. These arrive as a non-2xx HTTP status with the body { "reason": "...", "code": ... } (no envelope).
| Code | Name | Reason |
|---|---|---|
| 1000 | MPS_UNEXPECTED_ERROR |
Unexpected Error. Contact system administrator |
| 1001 | MPS_UNAUTHORIZED |
Unauthorized |
Gateway calculation errors
| Code | Name | Reason |
|---|---|---|
| 100001 | GATEWAY_CALCULATION_SERVER_DISCONNECTED |
Meal Plan Service server is disconnected. Transient; retry after a short backoff. |
| 100002 | GATEWAY_INSUFFICIENT_TENDER_AMOUNT |
Insufficient Tender Amount. The patron's available plans plus any supplied tender cannot cover the request. |
Validation errors (no code)
Returned with code: 0 (or omitted) and a reason describing the problem. These are emitted by request-shape validation before any business logic runs.
| Reason | Cause |
|---|---|
Unauthorized |
The request reached an endpoint but no authenticated user could be resolved. Recheck the Authorization header. See Authentication. |
Unknown Location |
The auth key is not associated with a location. Contact support. |
A unique Transaction UUID is required |
direct-transaction request missing or empty request.transactionUuid. |
lucovaUserName is required |
direct-transaction request missing or empty request.lucovaUserName. |
requestAmount must be greater than zero |
direct-transaction request with requestAmount <= 0. |
receiptItems must not be empty |
direct-transaction request with no receiptItems. |
Each receipt item must have a name |
A receipt item has no name. |
Receipt item '<name>' must have a quantity greater than zero |
A receipt item has quantity <= 0. |
Receipt item '<name>' is missing itemPrice |
A receipt item has itemPrice <= 0. Most common cause: client sent price instead of itemPrice. |
Receipt item '<name>' is missing itemTotalPrice |
A receipt item has total > 0 but itemTotalPrice <= 0. Most common cause: client sent total instead of itemTotalPrice. |
Insufficient tender amount |
The provided tender amounts do not cover the request. |
Payment processor passthrough errors
When a direct-transaction request authenticates and validates successfully but the downstream payment processor rejects the charge, the response envelope returns success: true with data.success: false, data.message set to a human-readable message, and data.error_code set to a processor-specific numeric code. These codes are not enumerated in this guide. Contact engineering@nownpos.com for guidance on specific codes.
Support
- Credentials and environment access:
support@nownpos.com - API and integration questions:
engineering@nownpos.com
When reporting an integration issue to engineering@nownpos.com, include:
transactionUuidif the problem involves a transaction (direct-transaction,refund).- Request timestamp (UTC) and the endpoint path that was called.
- The full request payload and the full response body (headers and JSON). For non-2xx responses, include the HTTP status code.
Changelog
2026-05-14
- Replaced the v1.1 PDF guide. New format is a single living Markdown document.
- Auth correction:
Authorizationheader now correctly documented asBearer, notBasic. Existing integrations usingBasicshould switch the scheme keyword. The token value does not change. - Credentials format: Host and auth key are now issued as two separate values. No more base64-decoding the comma-separated wrapper.
- Receipt item fields: Canonical names are
itemPriceanditemTotalPrice. The legacyprice/totalnames deserialize but fail validation; integrators should migrate. - Idempotency: Documented
transactionUuididempotency behavior ondirect-transaction. - New sections: Error code reference (AppCode enum, MPS errors, gateway errors, validation errors, payment processor passthrough notes), end-to-end walkthrough, field-naming gotchas.
- Endpoint coverage: Added
transaction/estimate,transaction/refund,last-transaction,location,version.
v1.1 (2024-10-08)
- Previous PDF revision.