Cardy docs

Quickstart — your first points in 15 minutes

By the end of this page you will have awarded points on a real sandbox card and seen them land. Everything here is copy-paste runnable.

What you need

Two things, both from us:

Your test key Looks like cardy_pk_test_…. It only ever reaches the sandbox — there is no way for a test key to touch live customer balances.
A test customer A phone number we have already set up with cards. The examples below use 96599901001.

The sandbox lives at:

https://connect-staging.getcardy.net/api/v1

Every request needs your key in the Authorization header. Set it once:

export CARDY_KEY="cardy_pk_test_replace_me"
export CARDY_URL="https://connect-staging.getcardy.net/api/v1"

1. Find the customer

This is what your till does when a customer presents their phone number or scans their card.

curl -s -X POST "$CARDY_URL/customers/identify" -H "Authorization: Bearer $CARDY_KEY" -H "Content-Type: application/json" -d '{"phone":"96599901001"}'

You get the customer and every card they hold:

{
  "customer_id": "01a03fca-ffa8-70b4-b850-428496841903",
  "cards": [
    {
      "card_id": "947706-732-680",
      "template_name": "Demo Restaurant (points per KWD)",
      "balances": { "points": 99, "stamps": null, "visits": null },
      "allowed_operations": ["earn_amount", "redeem_points", "redeem_reward"]
    }
  ]
}

allowed_operations is the field to build your buttons from. It lists what this card can actually do. If redeem_points is not in the list, do not show a redeem button — the card cannot do it, and the call would be refused.

Balances are null for dimensions a card does not have. A stamp card has no points; that is different from having zero points.

2. Award points for a sale

curl -s -X POST "$CARDY_URL/transactions/earn" -H "Authorization: Bearer $CARDY_KEY" -H "Content-Type: application/json" -d '{"identifier":{"phone":"96599901001"},"external_ref":"receipt-1001","amount_minor":12500,"currency":"KWD"}'
{ "transaction_id": "cmtc1kl6w0000qs01bt4prksp", "status": "pending", "duplicate": false }

Two fields deserve attention.

external_ref is your receipt number. Send your own — whatever identifies this sale in your system. It is how you look the transaction up later, how you refund it, and how we stop a retry from paying twice.

amount_minor is an integer. KWD has three decimals, so 12.500 KWD is 12500. Never send a decimal; never assume two places.

status: "pending" means we have accepted the sale and are applying it. The customer's balance updates within a second or two. Your till does not need to wait — the sale is already recorded.

3. Prove it landed

Look the sale up by the receipt number you sent:

curl -s "$CARDY_URL/transactions?external_ref=receipt-1001" -H "Authorization: Bearer $CARDY_KEY"
{
  "data": [
    {
      "transaction_id": "cmtc1kl6w0000qs01bt4prksp",
      "intent": "earn",
      "status": "success",
      "external_ref": "receipt-1001",
      "card_id": "144389-894-749",
      "amount_minor": 12500,
      "currency": "KWD",
      "points": null
    }
  ]
}

status has moved from pending to success — the sale has been applied.

Note card_id: you did not name a card, so we picked the one this sale should land on. And points is null while amount_minor is set, because we record what you sent us. The card's own balance moved in whatever unit that card keeps — run step 1 again and you will see it.

If you want the sale on a particular card, send card_id from the identify response.

4. Now retry it

Send the exact same earn request from step 2 a second time.

{ "transaction_id": "cmtc1kl6w0000qs01bt4prksp", "status": "success", "duplicate": true }

Same transaction, duplicate: true, and the customer was not paid twice.

This is the most important thing to understand about this API. Your till will lose connectivity mid-sale, and it will not know whether we got the request. The answer is always the same: send it again with the same external_ref. It cannot double-award. You never need to reconcile, ask us, or write logic to guess what happened.

The one way to break this is to generate a new external_ref on retry. Don't. The receipt number is the same receipt number.

Where to go next

  • Error handling — every code, what the cashier should see, and what your code should do.
  • Certification — what we check before you go live, and how to run those checks yourself.
  • Postman collection — every endpoint, ready to import.
Cardy Loyalty API Questions your integration cannot answer belong in an email, not a retry loop.