Getting started

From zero to the first purchase that earns points, and from there to redemption, with the exact calls for each step.

This page is the complete integration, end to end. If you are here to implement and want to read a single page before writing code, this is the one.

There are two paths and they are not exclusive: almost every integration starts with the first and adds the second when they want the member to see their own points.

PathWhat it solvesCredential
From your serverMaking purchases earn points and codes get redeemed at the saleAPI key
From your frontendLetting the member see their points, buy rewards and show their codeEnd-user token

Before you start

You need two things, and we give you both: write to us at hola@loybox.com.ar.

LOYBOX_API_KEYsecretrequired

The commerce API key. It goes on your server only.

commerce_idpublicrequired

Your commerce id. This is what travels in the X-Commerce-Id header and it can live in the frontend.

From here on, the examples use the reference's conventions: $LOYBOX_API_KEY for the API key, $ACCESS_TOKEN for the end-user token and 87 as the commerce_id.

Path 1: earning points from your server

This is the minimum integration of a loyalty program: a single call, at the point where your system confirms a sale.

Record the purchase

At an online checkout you already have the email, so by email is the short path: it works even if the member does not have a Loybox account yet.

curl -X POST https://loybox-public-api-752998171300.southamerica-west1.run.app/v1/consumptions/email \
  -H "Authorization: Bearer $LOYBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "client_email": "ana@example.com",
    "amount": 1750
  }'

At a physical point of sale, where the member gives their number, it is the same call by code with client_code.

The amount is an integer

amount is whole units of the commerce's currency. A purchase of 1,750.50 is sent as 1750.

How many points it earns is not decided by your call: it is decided by the commerce's configuration. You send the amount and Loybox applies the money-per-point rule, double points if they are running, and the tier bonus, in that order (the formula).

Show them the balance

The consumption's response does not say how many points it added. The balance is looked up separately:

curl https://loybox-public-api-752998171300.southamerica-west1.run.app/v1/clients/12345 \
  -H "Authorization: Bearer $LOYBOX_API_KEY"

With this you already have a working program: purchases earn and the member has a balance.

Look up the code the member brings

When a member shows up with a redemption code (their client_benefit_code), the first thing is to see what it is. Use v2, which brings the discount's value at the root:

curl https://loybox-public-api-752998171300.southamerica-west1.run.app/v2/benefits/preview/887766 \
  -H "Authorization: Bearer $LOYBOX_API_KEY"

With the response's type and value you apply the discount in your sale:

typeWhat to do
percentage_discountApply the percentage in value.
absolute_discountSubtract the amount in value.
free_productAdd the product in product.

A 400 here means the code was already used: do not apply anything.

Redeem it once the sale closed

Redemption burns the code and there is no going back:

curl -X POST https://loybox-public-api-752998171300.southamerica-west1.run.app/v1/benefits/redeem \
  -H "Authorization: Bearer $LOYBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "client_benefit_code": 887766
  }'

Order matters

Looking up is harmless and can be repeated; redeeming is final. If you redeem before closing the sale and the sale falls through, the member lost the reward and there is no way to give it back over the API.

Path 2: the club in your frontend

Here Loybox works as the loyalty engine under your product: the user signs in with a code emailed to them and from there sees their points, buys benefits and shows their codes. All of this can run in the browser.

The API key has no place on this path

No call on this path carries the API key: it grants access to the data of all your members. What travels is the end-user token, which only sees their own data, plus the X-Commerce-Id, which is not a secret.

Ask for the code

curl -X POST https://loybox-public-api-752998171300.southamerica-west1.run.app/v1/auth/otp/request \
  -H "X-Commerce-Id: 87" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "ana@example.com"
  }'

It always answers 200, even if that email has no account. In the UI you always show the same message ("we sent a code to your email"), because you cannot know whether the account existed.

Verify it and store the session

curl -X POST https://loybox-public-api-752998171300.southamerica-west1.run.app/v1/auth/otp/verify \
  -H "X-Commerce-Id: 87" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "ana@example.com",
    "otp": "418302"
  }'

It returns access and refresh. Store both: the access expires in expires_in seconds and the refresh is what renews it without asking the user for another code. If the email had no account, one is created, and either way the user ends up subscribed to your program.

Paint the screen with a single call

GET /v1/me brings the points, the commerce's branding and the points about to expire, all at once:

const res = await fetch(
  'https://loybox-public-api-752998171300.southamerica-west1.run.app/v1/me',
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'X-Commerce-Id': '87',
    },
  },
);

const me = await res.json();
// me.points -> the big balance on the screen
// me.commerce -> logo, name and color for the program's branding
// me.subscribed -> if it comes back false, show the call to join

A 401 here means the access expired: renew it and retry the call.

Show the catalog and buy

The catalog comes back unfiltered by balance: you compare each benefit's cost with the user's points and decide what to show as reachable and what as "you need N more points".

curl -X POST https://loybox-public-api-752998171300.southamerica-west1.run.app/v1/me/benefits/exchange \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-Commerce-Id: 87" \
  -H "Idempotency-Key: 8f14e45f-ea0f-4d1c-9a1b-2c3d4e5f6a7b" \
  -H "Content-Type: application/json" \
  -d '{
    "benefit_id": "b_9f2a"
  }'

Buying and redeeming are not the same

Buying trades points for a benefit, and the user does it from your frontend. Redeeming uses that benefit in the sale, and your server does it with the API key (step 4 of path 1).

Show them the code

The purchase returns a client_benefit_code. That number is both the redemption code and the coupon code: in an online store it is what the user pastes at checkout, and in a physical store it is what they show at the counter.

Show it large, with a copy button and the due_date next to it. The full list of the ones they hold is in my benefits.

The full circuit

The two paths close like this, and it is the mental model worth having before writing code:

purchase ─▶ POST /v1/consumptions/email        (your server, API key)


          points to the member


        POST /v1/me/benefits/exchange          (your frontend, end-user token)


       client_benefit_code


      GET /v2/benefits/preview/{code}          (your server, API key)


        POST /v1/benefits/redeem               (your server, API key)

Before going to production

  • The API key lives on your server only. If it leaked, write to us and we rotate it.
  • Never retry a 400 blindly: it is the already-used benefit, the expired one or points that fall short. Read the message and stop.
  • Buying a benefit carries an Idempotency-Key. It is what prevents charging the points twice when the network drops.
  • A 401 in My account is the signal to renew the token, not to sign the user out.
  • The 404 from my tier is the normal case for a new member: hide the tiers section, do not show an error.
  • points_expiration does not come back null when points do not expire: it comes with mode: "none". Check the mode before showing the notice.

If you are implementing with an agent

The API is published in machine format:

  • /openapi.json: the complete OpenAPI 3.1 specification, with all 25 endpoints, the schemas and the two credentials. Good for generating a typed client. Its descriptions are in Spanish; the names and schemas are not.
  • /llms.txt and /llms-full.txt: the whole docs as text, meant to be handed over as context. These two are per language, so the links above are the English ones.
  • Any page of the docs as raw Markdown by appending .md to the URL, for example /api-reference/credenciales.md.