Letstamp makes a GET request to your configured Products API URL when import is triggered. This is your POS-hosted URL; there is no fixed Letstamp product-upload route. Businesses map imported products to earning triggers, reward products, and required products. Reward mappings can vary by customer class.
The importer does not send the integration accessToken header or define custom authentication headers. Confirm product-endpoint access requirements and the refresh schedule during onboarding.
STEP 02 / ONE-TIME SETUP
Configure your branch in Letstamp
In Letstamp Partners, open the branch integration settings and connect your POS.
Required and unique. Generate an ID or define your own, such as IST-01. Use it as branch_id.
API Key
Automatically generated by Letstamp. Send it in the accessToken request header.
Products API URL
Required. Your hosted URL returning the menu JSON from Step 1.
Checkout Completion
Complete after POS checkout confirmation (Recommended) or Complete when the order is submitted.
Reward Selection
Customer selects from the app, or business selects from the partner panel.
Status
Active / Inactive. Activate the integration before sending requests.
Setup is done.Steps 1 and 2 are not repeated for each sale. The integration, business, and branch must be active with a valid membership.
Authentication for every request
Send requests from your POS backend or trusted runtime. Match the API key with the configured branch identifier in the JSON body. The branch identifier is not a customer identifier.
Fewer requests. Loyalty completes on selection, so later POS cancellations cannot be synchronized through a documented reversal endpoint.
In both modes, no available redemption completes loyalty on submission. Declining redemption in Letstamp also completes loyalty without a checkout confirmation.
TAILOR THIS GUIDE
Poll for selection, apply the discount, complete the sale, then confirm the selected redemption.
Capture the customer's current Letstamp short code. Send it with the order and products. Letstamp calculates eligibility and handles customer or cashier selection.
Your order/check identifier. Non-empty; retain the same value for status and checkout. Recommendation: use globally unique IDs across locations. Re-submission is not a safe update or retry mechanism; see the retry notes below.
branch_id
String
Configured integration branch identifier, validated together with the token.
option
String
Customer short code. Keep it as a string to preserve any leading zeros. It must be unused, current, and valid for the business's Letstamp app context.
items
Array of objects
Products on the check. An empty array returns no_item. The body must contain this collection.
items[].product_id
String
Exact product ID used in the product mapping. Items with an empty product ID are skipped.
items[].name
String
Product name. The handler reads this field; include it. Whether omission is supported needs confirmation.
items[].qty
Number, whole units
Quantity. The current handler floors numeric quantities to whole units and processes each unit separately. Recommendation: send positive integers; fractional/weighted sales need confirmation.
items[].unit_price
Number
Price per unit, not the line total. Decimals are supported. Use an unformatted JSON number, without a currency symbol or thousands separator.
The total is derived from quantities × unit prices (380.00 in this example). No total or currency field is read.
Stop and inspect the order ID and items. Do not assume the write had no effect or retry blindly.
{"status":false,"message":"Unexpected Error"}
Missing or empty access token. Check the accessToken header.
{"status":false,"message":"Branch membership is not active"}
Check the token/branch pair, integration status, business/branch status, and membership.
{"status":false,"message":"Invalid user code"}
Stop this attempt and resolve the invalid or expired code. Submission and checkout also reject a consumed code. Do not replace the code on an in-flight order without reconciliation.
Validation, errors, and safe retries
Inspect both status and check_status: status: true alone is not success. Messages are localized; do not branch on English text. Missing items or an empty order ID can return unexpected_error only if parsing and earlier checks pass. Empty/malformed JSON has no guaranteed JSON error response.
Never resubmit an order to poll or update it: re-submission can replace a pending selection. A consumed code can be rejected before the already-processed check. Use globally unique order IDs across locations and deployments. On network, HTTP, empty-body, or JSON errors, stop and reconcile before another write. No automatic retry contract is defined.
Field lengths, item-count limits, weighted quantities, tax, currency, and rounding policies require onboarding confirmation.
STEP 04 / ONLY IF REDEMPTION IS PENDING
Handle rewards
YOU SEND→LETSTAMP RESPONDS→YOUR POS DOES
Only start polling after status: true and check_status: "pending_redemption" from the order. Keep checkout on hold while the customer or cashier makes a choice through Letstamp.
Your POS → Stop polling. Apply the monetary discount once, then release checkout.
With Checkout: after applying the selected discount and successfully completing the POS sale, continue to Step 5.
Without Checkout: finish the sale normally. Letstamp processes loyalty on selection; do not send a checkout confirmation.
Letstamp response
What your POS should do
true + pending_redemption
Continue polling; keep checkout on hold until your local deadline.
true + redemption_selected
Stop polling → apply discount_amount exactly once → release checkout. Supported selection actions are spendReward and spendCashback.
true + completed
Stop polling → release checkout without a discount or confirmation.
status: false, unexpected state, or transport error
Stop polling → release the automated hold into an operator recovery flow. Do not infer a discount or silently finish an uncertain sale.
Letstamp response · English examples
What your POS should do
{"status":false,"message":"Order contents not found."}
No matching order was found. Stop polling and verify the original identifiers; reconcile instead of re-submitting automatically.
{"status":false,"message":"Unexpected Error"}
Missing or empty access token. Check the accessToken header.
{"status":false,"message":"Branch membership is not active"}
Check the token/branch pair, integration status, business/branch status, and membership.
{"status":false,"message":"Invalid user code"}
Stop this attempt and resolve the invalid or expired code. Submission and checkout also reject a consumed code. Do not replace the code on an in-flight order without reconciliation.
Never wait indefinitely.Polling every 1–3 seconds, with one request in flight, is a client recommendation. Choose a finite local deadline and request timeout. At that limit, stop polling and let the operator resolve the sale. The API does not specify a server polling timeout, automatic cancellation, retry schedule, or reversal operation. A local timeout does not cancel loyalty.
Discounts, short codes, and lifecycle details
discount_amount is a monetary amount, not a percentage. Amounts above are illustrative. No reward ID, product allocation, point-conversion rate, or currency is returned. Apply the order-level amount once using decimal-money handling; do not recompute eligibility. A reward may be a partial discount.
The status lookup supports both modes. It accepts a used code while the code remains within its validity window. Order and checkout requests still require an unused code. The documented acceptance window is 15 minutes from code creation; confirm the configured lifetime during onboarding. Code expiry can interrupt a long checkout.
A saved selection can continue returning redemption_selected even after loyalty completion. Repeated reads do not spend rewards or award earnings. Persist whether you have already applied the discount. The completed status response means completed with no saved selection.
Stop and seek operator recovery for an unknown action or an invalid amount; never apply an unvalidated response.
STEP 05 / WITH CHECKOUT ONLY
Confirm checkout Optional mode
YOU SEND→LETSTAMP RESPONDS→YOUR POS DOES
When the POS successfully completes the sale, notify Letstamp. This step applies to a selected redemption in With Checkout mode.
Successful POS payment comes first.Apply the selected discount → release checkout → successfully tender/close the sale → send this request. Never confirm on selection, payment initiation, failed payment, or cancellation.
Send the accessToken header and the same identifiers. Letstamp reads the saved selection; do not send the action or discount.
The saved selection action is unsupported. Stop and reconcile with Letstamp.
{"status":false,"message":"Checkout failed"}
The checkout transaction could not start. Record the failure and reconcile before retrying.
{"status":false,"message":"Unexpected error."}
A transaction or commit error occurred. Treat the result as uncertain and reconcile before retrying.
{"status":false,"message":"Unexpected Error"}
Missing or empty access token. Check the accessToken header.
{"status":false,"message":"Branch membership is not active"}
Check the token/branch pair, integration status, business/branch status, and membership.
{"status":false,"message":"Invalid user code"}
Stop this attempt and resolve the invalid or expired code. Submission and checkout also reject a consumed code. Do not replace the code on an in-flight order without reconciliation.
Confirmation, concurrency, and uncertain outcomes
Do not send parallel checkout confirmations. Repeating a successful request may return an invalid-code error because completion consumes the customer code. If a request times out, check the outcome with Letstamp before retrying.
There is no documented cancellation, refund, or reversal operation. Agree recovery after failed payment or uncertain confirmation with Letstamp. This step is not required in Without Checkout mode, or when loyalty already completed without a selected redemption.
PUT IT TOGETHER
See How Simple It Is
One order. Poll only when needed. Apply the result. Confirm a selected redemption after the sale.No checkout confirmation request.
Replace YOUR_ACCESS_TOKEN and the sample order data with your branch credentials and sale details. Each snippet sends one request; run it at the step described. PHP uses cURL. Node.js uses built-in fetch (run as an .mjs file).
1. Send the order
Send the current order and customer code. If the response is pending_redemption, hold checkout and start the status requests below. If it is check_processed, continue normally.
Make this request only after pending_redemption. Repeat while pending, with a finite waiting limit. On redemption_selected, stop polling, apply discount_amount once, and release checkout. On completed, continue without a discount. Stop on errors.
Run this request only after a selected discount has been applied and the POS sale has successfully completed. Require status: true and check_status: "completed"; otherwise resolve the error before retrying.
Send the current order and customer code. If the response is pending_redemption, hold checkout and start the status requests below. If it is check_processed, continue normally.
Make this request only after pending_redemption. Repeat while pending, with a finite waiting limit. On redemption_selected, stop polling, apply discount_amount once, and release checkout. On completed, continue without a discount. Stop on errors.
Run this request only after a selected discount has been applied and the POS sale has successfully completed. Require status: true and check_status: "completed"; otherwise resolve the error before retrying.
Send the current order and customer code. If the response is pending_redemption, hold checkout and start the status requests below. If it is check_processed, continue normally.
Make this request only after pending_redemption. Repeat while pending, with a finite waiting limit. On redemption_selected, stop polling, apply discount_amount once, and release checkout. On completed, continue without a discount. Stop on errors.
Node.js · status request
const response = await fetch('https://default.hooks.api.letstamp.com/v100/?q=status', {
method: 'POST',
headers: {
accessToken: 'YOUR_ACCESS_TOKEN',
'Content-Type': 'application/json'
},
signal: AbortSignal.timeout(10000),
body: JSON.stringify({
"order_id": "AD-45821",
"branch_id": "IST-01",
"option": "123123"
})
});
if (!response.ok) throw new Error('HTTP ' + response.status);
const result = await response.json();
console.log(result); // Follow the response actions described above.
3. Confirm after successful payment
Run this request only after a selected discount has been applied and the POS sale has successfully completed. Require status: true and check_status: "completed"; otherwise resolve the error before retrying.
Node.js · checkout request
const response = await fetch('https://default.hooks.api.letstamp.com/v100/?q=checkout', {
method: 'POST',
headers: {
accessToken: 'YOUR_ACCESS_TOKEN',
'Content-Type': 'application/json'
},
signal: AbortSignal.timeout(10000),
body: JSON.stringify({
"order_id": "AD-45821",
"branch_id": "IST-01",
"option": "123123"
})
});
if (!response.ok) throw new Error('HTTP ' + response.status);
const result = await response.json();
console.log(result); // Follow the response actions described above.
The examples use a 10-second request timeout as a client setting, not an API guarantee. Polling, applying the returned discount, and completing payment follow your existing POS flow, as described in Steps 3–5.
WHEN YOU NEED IT
Advanced notes
Rewards, cashback, and customer classes
Letstamp owns selection and eligibility. Reward eligibility includes the check products and configured mappings; a positive cashback wallet can also trigger pending redemption. Availability is not a reservation guarantee.
Wallet redemption uses spendCashback, even when the app calls it Letstamp Points. The selected amount is limited by wallet balance and order total at selection time. JSON 5 and 5.00 are equivalent; fractional amounts such as 5.25 are supported. Use decimal-money handling without integer truncation.
Product mappings and reward benefits may differ by customer class. Letstamp preserves the reward's class context and uses it to resolve the applicable product mapping. Your POS does not calculate classes or send a class identifier.
Use the redemption and discount returned by Letstamp rather than recalculating the reward from the customer's current class. Class-specific mapping takes precedence when configured; otherwise the standard mapping is used.
Transport and routing
Use POST with a JSON body for all three operations. Use only the documented URLs: an unrecognized q value is treated as order submission.
Read status and check_status in the response body; HTTP status alone does not identify every API error. Also handle network failures and empty or non-JSON responses. Message text may be localized.
Confirm during onboarding
Sandbox availability and URL; rate limits and browser/CORS support.
Product-list authentication and refresh schedule.
Request sizes, field lengths, weighted quantities, tax, currency, and rounding policy.
Configured short-code lifetime and local polling deadline.
Recovery for expired codes, uncertain writes, payment failures, cancellations, and refunds.
Integration acceptance checklist
Use this list during your integration acceptance test.
Checklist selections are local to this page and are not saved or submitted.