Documentation
IntegrationsPOS Integration

POS Integration

Connect your point-of-sale system to Xtarly Rewards to automatically award points or stamps when customers make a purchase.

The POS integration lets your point-of-sale system register transactions directly via a REST API. Every time a customer pays, your POS calls the Xtarly endpoint and the customer instantly receives their points or stamps — no manual scanning required.

Don't want to write code? There are 15 automatic connectors — Loyverse, Poster, Square, Clover, Toast, Bsale, Lightspeed, Odoo, Shopify, Alegra, CleanCloud, Epos Now, Mercado Pago, Clip and SumUp — that you connect in minutes from the POS integrations panel, no code required.

The POS REST API is a Premium plan feature: integrating your own system with Xtarly through code. It is not the same as connecting a POS from the catalog, which is available from the Shared plan and requires no programming — see the POS integrations dashboard. At the register you can always record sales from the cashier app, on any plan.

Getting your API key

  1. Open your organization dashboard and go to Settings → General.
  2. Scroll down to the POS Integration section.
  3. Click Generate API key. The key is shown once — copy it immediately and store it securely (e.g. in your POS system's environment configuration).
  4. If the key is ever compromised, click Rotate to invalidate the old key and generate a new one. If you already have a live key, you can rotate it even if your plan changes: you never lose the way to replace a leaked credential.

The API key is shown only once after generation. If you lose it, you will need to rotate and update all integrations using the old key.

Base URL

All POS endpoints are served from your Xtarly domain:

https://www.xtarly.com/api/integrations/pos

Authentication

Include your API key in every request via the X-API-Key header:

X-API-Key: xtarly_pos_<your_key>

Requests without a valid key return 401 Unauthorized.


Endpoints

Register a transaction

POST /api/integrations/pos/transaction

Call this endpoint when a customer completes a purchase. Xtarly calculates the points or stamps earned based on your active reward program and credits them to the customer.

Request body

FieldTypeRequiredDescription
amountnumber✓Purchase total in the smallest currency unit (e.g. cents for MXN/USD).
customerIdstring✓ (or customerEmail)Xtarly customer ID.
customerEmailstring✓ (or customerId)Customer's registered email.
metadataobject—Optional free-form object attached to the transaction (e.g. { "ticket": "1234" }).

Provide either customerId or customerEmail — at least one is required.

Example request

curl -X POST https://www.xtarly.com/api/integrations/pos/transaction \
  -H "Content-Type: application/json" \
  -H "X-API-Key: xtarly_pos_<your_key>" \
  -d '{
    "amount": 35000,
    "customerEmail": "cliente@ejemplo.com",
    "metadata": { "ticket": "T-00412" }
  }'

Successful response 200 OK

{
  "success": true,
  "data": {
    "transactionId": "txn_abc123",
    "earnedPoints": 35,
    "earnedStamps": 0,
    "customerPoints": 210,
    "customerStamps": 0
  }
}
FieldDescription
transactionIdUnique ID of the recorded transaction.
earnedPointsPoints credited in this transaction.
earnedStampsStamps credited in this transaction (stamp programs).
customerPointsCustomer's total points after this transaction.
customerStampsCustomer's total stamps after this transaction.

Look up a customer

GET /api/integrations/pos/customer/:id

Returns the current balance and tier of a customer. Useful to display loyalty status at the point of sale before or after a transaction.

Example request

curl https://www.xtarly.com/api/integrations/pos/customer/cus_abc123 \
  -H "X-API-Key: xtarly_pos_<your_key>"

Successful response 200 OK

{
  "success": true,
  "data": {
    "id": "cus_abc123",
    "name": "María García",
    "email": "cliente@ejemplo.com",
    "points": 210,
    "stamps": 0,
    "tier": null
  }
}

Point-of-sale endpoints

The same API key gives you access to the endpoints a connected point of sale uses: identifying the customer at the register, seeing what they can apply today, and redeeming coupons or rewards at checkout.

EndpointWhat it's for
GET /pingChecks that the API key is valid and returns the program name.
GET /programActive program type (POINTS, STAMPS or TIERS) and what the unit is called ("puntos" or "sellos").
POST /accrueCredits a POS sale. Idempotent by saleId.
GET /customer/by-phone?phone=Looks up the customer by phone number.
GET /customer/by-qr?payload=Looks up the customer from their card QR scanned at the register.
GET /customer/by-email?email=Looks up the customer by email (case-insensitive).
GET /customer/benefits?customerId=Everything that customer can apply today: available coupons and rewards with a verdict.
GET /coupon/by-qr?payload=Resolves a scanned coupon and says whether it can be redeemed.
GET /coupon/by-code?code=Same, with the code typed by hand.
POST /coupon/redeemApplies a coupon to the sale. Idempotent by saleId.
POST /reward/redeemRedeems a reward and deducts the points or stamps. Idempotent by saleId.

All four customer lookup endpoints return the same shape: id, name, email, phone, points, stamps and tier (the tier name, or null).

Crediting a sale from the POS

POST /api/integrations/pos/accrue
FieldTypeRequiredDescription
saleIdstring✓The sale's id in your POS. It's the idempotency key: the same sale never credits twice.
customerPhonestring✓The customer's phone number.
amountCentavosnumber✓Sale total in cents (integer).
occurredAtstring✓Date and time of the sale.
branchNamestring—Name of the branch where the sale was charged.
itemsarray—Ticket line items; stored verbatim on the transaction.

The response carries a status: ACCRUED when credited (with pointsAccrued and stampsAccrued), SKIPPED if that sale was already processed or there's no active program, and CUSTOMER_NOT_FOUND if no customer has that phone number.

branchName is resolved against your active Xtarly branch names (case-insensitive), so the stamp or points land in the right branch instead of showing up with no branch in the activity feed. If it doesn't match exactly one branch, the transaction is saved without a branch.

What the customer can apply today

GET /api/integrations/pos/customer/benefits?customerId=cus_abc123

Returns, in a single call, the program type, the customer, their available coupons and every reward with the verdict already computed: attainable says whether they can redeem it right now, and if not, blockedReason and blockedMessage explain why (not enough points, tier not reached, already used this cycle…).

Each reward also includes a benefit object with the executable semantics of the discount — its type, its value and the menu products it applies to — so the POS can apply it on its own instead of leaving the math to the cashier.

Redeeming a coupon or a reward

POST /api/integrations/pos/coupon/redeem
POST /api/integrations/pos/reward/redeem

For a coupon, send couponId, saleId, amountCentavos and, optionally, customerId and branchName. A coupon flagged as walk-in can be redeemed without a customerId.

For a reward, send rewardId, customerId, saleId and, optionally, amountCentavos and branchName.

Both answer status: "REDEEMED" with the redemptionId, and DUPLICATE if that same sale had already redeemed. The reward redemption also returns pointsSpent, stampsSpent and the customer's balance after the redemption.

Every condition is validated on the server: minimum purchase, required tier, expiry, per-customer redemption cap and balance. The POS doesn't decide whether a coupon applies — it asks and obeys the answer.


Points calculation

The points or stamps credited per transaction depend on your active program type:

Program typeCalculation
Pointsfloor(amount / currencyUnit × pointsPerCurrency)
TiersSame as Points, then multiplied by the customer's tier multiplier.
Stamps1 stamp per transaction, regardless of amount.

You configure currencyUnit and pointsPerCurrency in Settings → Program.

Error codes

CodeHTTPDescription
UNAUTHORIZED401Missing or invalid API key.
VALIDATION_ERROR400amount or customer identifier not provided.
NOT_FOUND404No customer found with the given ID or email in your organization.
BAD_REQUEST400Your organization has no active reward program.