Build and verify your integration end-to-end before going live — without creating real members or awarding real points. Everything runs against an isolated test environment keyed by your sk_test_* API key.
sk_test_* keys hit a fully isolated environment. Members, campaigns, transactions, and rewards created here never touch production data.sk_live_* keys operate on production. Keep them secret and server-side.60 req/min; live keys to 600. See Authentication.POST /transactions/simulate runs the full earn-rule engine and returns the same shape as evaluate() — but writes nothing, creates no member, and awards no points. Use it for "you would earn X" previews and campaign QA. It is available on test keys only.
# Preview an earn without writing anything — sk_test_* only
curl -X POST ".../tenants/$TENANT_ID/transactions/simulate" \
-H "X-API-Key: sk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "memberRef": "customer_001", "amount": 25000 }'When evaluate() or simulate() returns an empty campaigns array on a test key, the response includes a _debug object explaining why each active campaign skipped. (This field is never returned on live keys.)
{
"pointsAwarded": 0,
"pointsBalance": 450,
"campaigns": [],
"state": "PROCESSED",
"_debug": {
"activeCampaigns": 1,
"campaignChecks": [
{
"campaignId": "5a7b3c9d-...",
"campaignName": "Gold Members Only",
"matched": false,
"reason": "SEGMENT_MISMATCH: member not in target segment"
}
]
}
}Common skip reasons: CAMPAIGN_NOT_ACTIVE, SEGMENT_MISMATCH, BELOW_MIN_SPEND, DATE_RANGE, and STORE_MISMATCH.
sk_test_* key.simulate() a basket to confirm the rule matches and the points/rewards are right.evaluate() the same basket to record it, then fetch the member to confirm the balance and any rewardIds.transaction.processed / reward.issued deliveries.evaluate().