How to review and validate Rewards using the Events API (REST)

Platform: Developers - Quick Summary: Use the Events API's sales endpoints to list incoming sales, check the details of an individual sale, correct its line items, and confirm or reject it before a reward is paid out.

šŸ’” Full OpenAPI documentation is available on our Developers' page.

Why use this

Every sale your integration sends to Aklamio is a potential reward for one of your customers. Before that reward get paid out, you need a reliable way to validate it, and decide whether it should go ahead. The Events API gives you that control programmatically: list and inspect sales as they arrive, fix a sale's line items if something's off, and confirm or reject it, all without leaving your own systems or dashboards.

This article walks through the core review and validation flow: list sales, get a single sale, update a sale, confirm a sale, reject a sale, and create a sale directly when you need to submit one on demand.

A quick note on terminology: in the API, a sale moves through the states unmatched, open, confirmed, rejected, and closed. Open is the state you'll review and act on; confirmed and rejected are the outcomes of that review.

Before you start

You'll need:

  • An Api-Secret key, provisioned by Aklamio for your brand. Every request must include it in the Api-Secret header. Ask your AKlamio Integration Manager for your Api-Secre or generate one directly from the PIFC if you have sufficient platform permissions. 
  • Your promotionUid, the identifier for the incentive program whose sales you want to work with. Your API key is scoped to your brand and works across all programs / promotions in that brand, but every request still needs a promotionUid to tell the API which promotion to look at.
  • A tool to send HTTP requests: curl, Postman, or your own backend code all work. The examples below use curl.

All requests go to https://api.aklamio.com/v3/events/sales.

Step-by-step guide

1. List sales to see what's come in

Start by pulling the sales for a promotion so you can see what needs review.

GET /v3/events/sales?promotionUid=<promotion_uid>
curl -X GET \
  -H 'Api-Secret: <api_key>' \
  'https://api.aklamio.com/v3/events/sales?promotionUid=<promotion_uid>'

By default, this returns every sale for the promotion, including ones that haven't been matched to a customer yet (status: unmatched). If you only want sales that are ready for a decision, filter down:

  • Add status=open to see only sales awaiting review.
  • Add hasSalesRequest=true to exclude unmatched, event-only entries entirely.
  • Add createdAtFrom and createdAtTo to narrow to a date range.
  • Add orderId if you're looking for one specific order.

The response is paginated. Use the nextToken returned in meta.page as the pageToken on your next request to keep moving through the list.

{
  "data": [
    {
      "id": "4f91cc9f-6e9f-4d72-b9f7-3f4e2a1d4f90",
      "orderId": "ORD-2026-001",
      "status": "open",
      "hasSalesRequest": true,
      "followerEmail": "joh***@***mple.com",
      "recommenderEmail": "jan***@***mple.com",
      "lineItems": [
        { "sku": "SKU-001", "name": "Premium Widget", "category": "electronics", "price": 9999 }
      ],
      "hasFile": true
    }
  ],
  "meta": { "page": { "nextToken": "nxt_...", "pageSize": 25 } }
}

What to look for: each item's id is what you'll use as the saleId in every other step below. hasFile: true tells you a receipt was attached, useful to know before you decide whether you need to look at it.

2. Get a single sale for the full picture

Once you've spotted a sale that needs a closer look, whether from the list, from an order ID your support team gave you, or from your own records, fetch its full detail.

GET /v3/events/sales/{saleId}?promotionUid=<promotion_uid>
curl -X GET \
  -H 'Api-Secret: <api_key>' \
  'https://api.aklamio.com/v3/events/sales/4f91cc9f-6e9f-4d72-b9f7-3f4e2a1d4f90?promotionUid=<promotion_uid>'

This is the only place you'll get a downloadable receipt file, if one was attached, along with the full line items, request type (referral or cashback), sales channel, and any custom fields you sent at creation. The download URL in file.downloadUrl expires after one hour; if it's expired, just call this endpoint again to get a fresh one.

If the saleId doesn't exist or isn't accessible to your account, you'll get a 404 with resource_not_found.

3. Update a sale if the line items need correcting

If you spot an error, a wrong price, a missing item, before you confirm, you can correct it. Updating replaces the sale's line items entirely.

PATCH /v3/events/sales/{saleId}?promotionUid=<promotion_uid>
{
  "lineItems": [
    { "sku": "SKU-001", "name": "Premium Widget v2", "category": "electronics", "price": 8999 }
  ]
}
curl -X PATCH \
  -H 'Api-Secret: <api_key>' \
  -H 'Content-Type: application/json' \
  -d '{"lineItems":[{"sku":"SKU-001","name":"Premium Widget v2","category":"electronics","price":8999}]}' \
  'https://api.aklamio.com/v3/events/sales/4f91cc9f-6e9f-4d72-b9f7-3f4e2a1d4f90?promotionUid=<promotion_uid>'

The sale keeps the same id, so anything you've already linked to it in your own systems stays valid. Only sales in open or rejected state can be updated; you'll get a 422 if the sale has already moved to confirmed or closed, or if it's an unmatched, event-only sale with nothing to update yet.

4. Confirm a sale to approve the reward

Once a sale checks out, confirm it so the reward can move forward.

POST /v3/events/sales/confirm
{
  "promotionUid": "<promotion_uid>",
  "ids": ["4f91cc9f-6e9f-4d72-b9f7-3f4e2a1d4f90"]
}
curl -X POST \
  -H 'Api-Secret: <api_key>' \
  -H 'Content-Type: application/json' \
  -d '{"promotionUid":"<promotion_uid>","ids":["4f91cc9f-6e9f-4d72-b9f7-3f4e2a1d4f90"]}' \
  'https://api.aklamio.com/v3/events/sales/confirm'

You can confirm up to 1,000 sales in a single call by listing multiple IDs. If some succeed and others don't, you'll still get a 200 OK; check the failures array for anything that didn't go through, and summary for the overall count. IDs not listed under failures were confirmed successfully.

Before confirming, the API checks that your account has enough balance to cover the reward. If it doesn't, the whole batch is rejected with a 422 and the response tells you how much you need versus what's available.

5. Reject a sale to decline the reward

If a sale doesn't hold up, a duplicate, invalid, doesn't meet program terms, reject it instead.

POST /v3/events/sales/reject
{
  "promotionUid": "<promotion_uid>",
  "ids": ["4f91cc9f-6e9f-4d72-b9f7-3f4e2a1d4f90"]
}
curl -X POST \
  -H 'Api-Secret: <api_key>' \
  -H 'Content-Type: application/json' \
  -d '{"promotionUid":"<promotion_uid>","ids":["4f91cc9f-6e9f-4d72-b9f7-3f4e2a1d4f90"]}' \
  'https://api.aklamio.com/v3/events/sales/reject'

This works the same way as confirming: batch up to 1,000 IDs, get partial success back in failures and summary.

6. Create a sale directly, when you're not relying on tracking

Most sales arrive automatically through your tracking integration. But if you need to submit one directly, from a backoffice tool, a CSV import, or a manual entry, you can create it yourself.

POST /v3/events/sales
{
  "mode": "direct",
  "promotionUid": "<promotion_uid>",
  "orderId": "ORD-2026-002",
  "trackedAt": "2026-03-18T14:00:00Z",
  "recommenderEmail": "recommender@example.com",
  "followerEmail": "buyer@example.com",
  "lineItems": [
    { "sku": "SKU-001", "name": "Premium Widget", "category": "electronics", "price": 9999 }
  ]
}
curl -X POST \
  -H 'Api-Secret: <api_key>' \
  -H 'Content-Type: application/json' \
  -d '{"mode":"direct","promotionUid":"<promotion_uid>","orderId":"ORD-2026-002","trackedAt":"2026-03-18T14:00:00Z","recommenderEmail":"recommender@example.com","followerEmail":"buyer@example.com","lineItems":[{"sku":"SKU-001","name":"Premium Widget","category":"electronics","price":9999}]}' \
  'https://api.aklamio.com/v3/events/sales'

Set mode to "direct" and it's created and returned immediately as 201 Created, ready to review through the same steps above. You'll need either recommenderEmail and followerEmail, or a tracking identifier like aid or coupon to resolve them automatically. If you're submitting tracking data instead and want Aklamio to match it to a recommender for you, use mode: "tracking" instead; that returns a 200 OK acknowledgment while matching happens in the background, and the sale shows up in your next list call.

šŸ’”Best practices

  • Filter your list calls. Pulling every sale for a large promotion is slower and noisier than it needs to be. Use status=open for your day-to-day review queue, and reach for the full unfiltered list only when you need visibility into unmatched, event-only sales.
  • Use orderId to reconcile. If you created a sale in tracking mode, the fastest way to check whether it landed is to list sales filtered by orderId, rather than polling GET on a saleId you don't have yet.
  • Batch your confirms and rejects. If you're processing a queue of reviewed sales, send them in one call (up to 1,000 IDs) instead of one request per sale. It's faster and it's what partial success in the response is designed for.
  • Check failures even on a 200 OK. A successful response for confirm and reject doesn't mean every sale in your batch went through; it means the batch was processed. Always check summary.failed before assuming everything succeeded.
  • Log the requestId. Every response includes an X-Request-Id header, echoed as requestId in the body. Hold onto it; it's the fastest way for Aklamio support to trace a specific request if something looks wrong.

Troubleshooting / FAQ

Why can't I update or confirm a sale I just fetched? Only sales in the open state can be updated, and only open sales can be confirmed or rejected. If a sale is unmatched (no linked sales request yet), it isn't ready for any of these actions until matching has completed. Check the status field on the sale before acting on it.

I got a 422 when confirming, what does "insufficient balance" mean? Your account doesn't have enough available balance to cover the rewards for the sales in that batch. The error response includes requiredAmount and availableBalance so you can see the gap. Top up your balance or reduce the batch, then retry.

Why did my confirm or reject request return partial results? Confirm and reject support partial success by design. If some IDs in your batch are already confirmed, rejected, or otherwise not in a valid state, those specific IDs appear in failures while the rest still go through. You don't need to resubmit the whole batch, just retry the failed IDs after checking why they failed.

Where do I find the receipt file for a sale? Only on the single-sale GET endpoint, not on the list. The list only tells you hasFile: true/false to keep list calls fast. Call GET /v3/events/sales/{saleId} to get the actual downloadUrl.

What's the difference between confirming and rejecting? Confirming approves the sale, which moves the linked reward toward payout. Rejecting declines it, no reward will be issued for that sale. Both are final actions on an open sale, so review the sale's details before deciding.

šŸ”— Related articles

Find your way around Reward Management

How to validate Rewards