Get started

Quickstart

Five calls against your sandbox, from an empty project to a reward earned and redeemed.

You need two things, and you make both yourself. In your Trovy dashboard open Integrations → Developer API and press Create sandbox; then, on the sandbox's own Developer API page, create a test secret key (trv_test_…). The Sandbox guide says what you get. Everything below runs against api.sandbox.trovy.ca. No text reaches a real phone, and a sandbox cannot touch a real customer.

export TROVY_SECRET_KEY="trv_test_…"

1. Confirm which business you are

curl -X GET https://api.sandbox.trovy.ca/v1/business \
  -H "Authorization: Bearer $TROVY_SECRET_KEY"

environment reads sandbox. The currency in that response is the unit every …Cents field in this API is counted in. Read it once at setup: a business has one currency and it cannot change once it has transacted. Trovy does no conversion. Check currency and timezone now; if either is wrong for your business, email developers@trovy.ca before you record anything.

program holds the rules every order earns under: the rate (rewardRateBps), the most a reward can grow to (capCents), the order a reward needs (minRedemptionPurchaseCents) and how many days a new reward waits (redemptionDelayDays). If it is null, no rewards program is linked to the Developer API, and every earn answers 409 NO_ACTIVE_PROGRAM (see Going live).

2. Find a customer to act on

Your sandbox starts with one customer already linked, so you can skip the sign-up form for now. Their id is on the sandbox's Developer API page, under A customer to test with.

In production, a customer id comes from one place: the sign-up widget hands your page a linkToken, and your server exchanges it.

curl -X POST https://api.sandbox.trovy.ca/v1/customers/link \
  -H "Authorization: Bearer $TROVY_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "linkToken": "lt_7f3a9c1e4b8d2a6f"
  }'

That id is yours alone. It is not the customer's phone number and it means nothing at any other business.

3. Earn on an order

ORDER_ID=ORD-10233   # your own order id: the key and the body name the same order

curl -X POST https://api.sandbox.trovy.ca/v1/earn \
  -H "Authorization: Bearer $TROVY_SECRET_KEY" \
  -H "Idempotency-Key: $ORDER_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "cus_01HX8Z9K2M4N6P8Q",
    "orderId": "ORD-10233",
    "amountCents": 5000
  }'

At a 5% rate, a $50 order earns $3: rewards are whole dollars, rounded to the nearest, and 50¢ rounds up ($2.50 becomes $3, $2.49 becomes $2). An order that earns less than 50¢ is still recorded, with earnedCents: 0. Your sandbox earns at its own program.rewardRateBps from step 1, and a reward never grows past capCents.

Now send the same request again, with the same Idempotency-Key. You get the first result's amounts back, not a second reward, and replayed: true; no second text is sent, so sms.status is not_sent. That is the single most important habit in this API: a key names one event, so a retry of that event can never happen twice.

Send it a third time with a new key and the same orderId, and you get 409 DUPLICATE_ORDER: an order is recorded once, whatever the key.

4. See what they can spend

curl -X GET https://api.sandbox.trovy.ca/v1/customers/cus_01HX8Z9K2M4N6P8Q/rewards \
  -H "Authorization: Bearer $TROVY_SECRET_KEY"

Look at usable. A reward with usable: true is one to offer now; it lists first. One with usable: false is being held back until usableFrom, a local midnight redemptionDelayDays after it was earned, and cannot be applied yet. A reward also has a minimumOrderCents, and the order has to reach it.

5. Redeem it

ORDER_ID=ORD-10456   # a new order, so a new key: one key names one event

curl -X POST https://api.sandbox.trovy.ca/v1/redeem \
  -H "Authorization: Bearer $TROVY_SECRET_KEY" \
  -H "Idempotency-Key: $ORDER_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "cus_01HX8Z9K2M4N6P8Q",
    "rewardId": "rwd_01HX8Z9K2M4N6P8Q",
    "orderId": "ORD-10456",
    "amountCents": 12000
  }'

Take the reward off the order yourself, capture the remainder, and then tell Trovy, exactly as you earn only after payment succeeds. A redeemed reward is never handed back, so redeeming ahead of a capture that then fails would cost your customer their reward.

The order has to reach the reward's minimumOrderCents, so use an amount at least that large. If the reward says usable: false, your program holds new rewards back: to try this step today, set the sandbox program's redemption delay to 0 days in the dashboard.

You call earn or redeem for an order, never both. On a recurring program the redeem itself earns on what the customer paid, and returns it as newReward. An earn for a customer with a usable reward waiting answers 409 ACTIVE_REWARD_EXISTS, with the reward in details. Rewards has the whole decision, including an order below the reward's minimum.

Not using TypeScript?

Every sample has a curl tab, and each is one plain HTTP request. The whole API is one OpenAPI 3.1 document, /openapi/v1.json: import it into Postman or Insomnia, or generate a client from it. Check the generated models against the reference: an earn's reward and a redeem's newReward can be null. The link route is yours to write in any stack; Exchange the token says what it must check.