Skip to main content

Management API

Besides looking up members and registering transactions (Public API v1), /api/v1 lets you configure the programme without opening the back office: programmes and tiers, campaigns, the rewards catalogue, coupon batches and raffle results.

In the examples, $BASE is your platform's address and $KEY your API key (or an OAuth2 token).

Authentication and scopes​

Every call carries Authorization: Bearer mk_.... The key belongs to a customer account and everything is scoped to that account: a resource from another account answers 404, never 403.

These scopes are explicit: a key with no scopes (the older, full-access ones) does not get them, and they have to be granted to the key expressly.

The back-office scope selector does not offer them

The Scopes selector in Administration › API Keys shows a different list (documents:read, credits:write, webhooks:write…) and does not accept free values. To grant the scopes on this page, use the administration API with an administrator session that holds the API Keys permission: POST /api/services/app/ApiKey/UpdateApiKey (or CreateApiKey for a new key) with the scopes field as a comma-separated string, for example "read,programs:read,programs:write". UpdateApiKey takes the whole key (id, name, isActive, limits…), not just scopes.

ScopeAllows
programs:readRead programmes and tiers
programs:writeCreate, update and delete programmes and tiers
campaigns:readRead campaigns, their custom fields and participation rules
campaigns:writeCreate, update and delete campaigns; activate, pause; fields and rules
rewards:readRead the rewards catalogue
rewards:writeCreate, replace and delete rewards
coupons:readList coupons and their codes
coupons:manageIssue batches and invalidate coupons
raffles:readRead completed raffles and their winners
readRead the member attribute schema (in addition to the Public API reads)

Scopes are matched exactly: campaigns:write does not include campaigns:read, and coupons:write (till redemption) does not let you issue coupons. Give a key only what it needs.

Each block also requires the feature to be included in the account's plan (points programme, tiers, campaigns, rewards, coupons; and Bulk coupon generator to issue batches). If it is not, the response is 403 with code: feature_disabled.

Common conventions​

Pagination. Lists return { "items": [...], "nextCursor": "..." }. Send nextCursor as cursor to get the next page; it is null on the last one. limit goes from 1 to 200 (default 50; raffles: 1 to 100, default 20). A tampered cursor gives 400 with code: invalid_cursor.

Errors. The body always carries a stable code field; program against code, not the text. Codes are never renamed, but new ones may be added. Hourly limits and 429 are those of the key.

Idempotency. POST, PATCH and DELETE accept Idempotency-Key (see Public API). It is mandatory when issuing coupon batches (400 with code: idempotency_key_required if missing).

Enumerations. Programmes, tiers and campaigns use lowercase snake_case text (points, instant_win). Rewards and coupons use the name as is (Physical, PercentageDiscount). Handle unknown values gracefully: new ones may be added.

Partial updates (PATCH). Only the properties you send change. An explicit null clears the fields that allow it; on the rest it gives 400 validation_failed.

Programmes​

An account has at most one live programme. Plan feature: points programme.

Method and pathScopeWhat it does
GET /api/v1/programs?status=&cursor=&limit=programs:readList (by id)
GET /api/v1/programs/{id}programs:readOne programme
POST /api/v1/programsprograms:writeCreate (201)
PATCH /api/v1/programs/{id}programs:writePartial update
DELETE /api/v1/programs/{id}programs:writeDelete (204)

type values: points, cashback, stamps, product_cashback. status values: draft, active, paused, archived. Main fields: name, description, pointsCurrencyName, pointsToMoneyRate (money value of one point, 0 to 1), pointsExpirationDays, pendingPeriodDays, qualifyingPointsWindowMonths, tierGracePeriodDays, cashbackPercentage and modules (points, cashback, stamps, evidence, games, giftCards, courses, challenges, each true/false).

curl -s -X POST "$BASE/api/v1/programs" \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name":"Points programme","type":"points","pointsCurrencyName":"points","pointsToMoneyRate":0.01}'

Validation is the same as in the back office (for example, the pending period must be shorter than points expiry). Specific errors:

StatuscodeWhen
409program_already_existsThe account already has a programme
409program_in_useOn delete: it still has tiers or members
404program_not_foundIt does not exist in your account
422quota_exceededExceeds your plan's programme limit

Tiers​

Plan feature: tiers.

Method and pathScope
GET /api/v1/tiers?programId=&cursor=&limit=programs:read
GET /api/v1/tiers/{id}programs:read
POST /api/v1/tiersprograms:write
PATCH /api/v1/tiers/{id}programs:write
DELETE /api/v1/tiers/{id}programs:write

To create one, programId and name are required. Fields: level, criteriaType (points_accumulated, total_spent, transaction_count), minPoints, maintenancePoints, pointsMultiplier, color, iconUrl, isDefault. A tier cannot move to another programme.

curl -s -X POST "$BASE/api/v1/tiers" \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"programId":7,"name":"Gold","level":3,"minPoints":1000,"maintenancePoints":800,"pointsMultiplier":1.5}'

Deleting a tier that members still hold gives 409 with code: tier_in_use.

Campaigns​

You manage the campaign shell. Type-specific setup (games, prizes, winning moments, the landing builder) stays in the back office. Plan feature: campaigns.

Method and pathScopeNotes
GET /api/v1/campaigns?status=&type=campaigns:readMost recent first
GET /api/v1/campaigns/{id}campaigns:read
POST /api/v1/campaignscampaigns:write201; always starts as draft
PATCH /api/v1/campaigns/{id}campaigns:writetype and loyaltyProgramId do not change
DELETE /api/v1/campaigns/{id}campaigns:write204; drafts only
POST /api/v1/campaigns/{id}/activatecampaigns:writeBecomes active (or scheduled if it has not started)
POST /api/v1/campaigns/{id}/pausecampaigns:writeFrom active or scheduled
GET / POST /api/v1/campaigns/{id}/custom-fieldsread / writeData that participation asks for
PUT / DELETE /api/v1/campaigns/{id}/custom-fields/{fieldId}campaigns:write
GET / PUT /api/v1/campaigns/{id}/participation-rulesread / writeParticipation policy (policyJson, versioned)

type: game, points_multiplier, bonus_points, challenge, cashback, instant_win, pin_code, ugc_contest, sports_prediction, raffle. status: draft, scheduled, active, paused, completed, cancelled, expired_deserted.

To create: name (up to 256 characters), type, startDate and endDate (the end must be after the start). Optional: slug, loyaltyProgramId, brandId, imageUrl, landing texts, pointsMultiplier (0.01 to 100), bonusPoints, cashbackPercentage (0 to 100), maxTotalEntries, maxPrizesPerParticipant, minAgeYears and budget. In PATCH, an empty text clears the text and 0 clears a limit or amount.

curl -s -X POST "$BASE/api/v1/campaigns" \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: create-summer-2027" \
-H "Content-Type: application/json" \
-d '{"name":"Summer bonus","type":"bonus_points","startDate":"2027-06-01","endDate":"2027-08-31","bonusPoints":100}'

curl -s -X POST "$BASE/api/v1/campaigns/17/activate" -H "Authorization: Bearer $KEY"
StatuscodeWhen
400invalid_requestValues out of range or required ones missing
403feature_disabledCampaigns, or cashback when the type/percentage needs it, not included in the plan
404campaign_not_foundIt does not exist in your account
409campaign_not_draftDeleting a campaign that is not a draft (pause it instead)
409invalid_status_transitionActivating or pausing from a state that does not allow it
409budget_pausedPaused by its budget: raise it and reactivate it from the back office
409end_date_frozenChanging the end date of a campaign whose raffle was already drawn, or of a closed campaign
409slug_takenThe slug is already in use
422cannot_go_liveactivate refused (window over, invalid rules, instant-win without winning moments); the message says why
422loyalty_program_not_found, brand_not_foundThe reference is not in your account
422quota_exceededExceeds your plan's campaign limit

Rewards​

The catalogue redeemable with points. Plan feature: rewards.

Method and pathScope
GET /api/v1/rewards?programId=&campaignId=&status=&category=rewards:read
GET /api/v1/rewards/{id}rewards:read
POST /api/v1/rewardsrewards:write
PUT /api/v1/rewards/{id}rewards:write
DELETE /api/v1/rewards/{id}rewards:write

rewardType: Physical, Digital, Experience, Discount, Cashback. fulfillmentType: Digital, Ship, InStore, External. status when writing: Available or Disabled; OutOfStock is derived by the platform from the stock.

curl -s -X POST "$BASE/api/v1/rewards" \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"programId":5,"name":"Free coffee","rewardType":"Physical","pointsCost":200,"stock":500,"fulfillmentType":"InStore"}'
  • programId, name and rewardType are required. imageUrl must be an absolute http(s) URL.
  • PUT replaces the fields: anything you leave out takes its default.
  • stock is only read on create; afterwards it moves with redemptions and the back-office stock adjustment.
  • Gift-card fulfilment and mixed points-plus-cash settings can only be edited in the back office.
  • DELETE is a soft delete (204): past redemptions keep the reference.
  • Errors: reward_not_found, program_not_found, campaign_not_found (404), feature_disabled (403) and validation_failed (400).

Coupons​

To redeem at the till, use the Public API endpoints. This section manages the coupons that exist.

Method and pathScope
GET /api/v1/coupons?programId=&campaignId=&batchId=&status=&memberId=&code=coupons:read
GET /api/v1/coupons/{id}coupons:read
POST /api/v1/coupons/batchescoupons:manage
POST /api/v1/coupons/{id}/invalidatecoupons:manage
POST /api/v1/coupons/batches/{batchId}/invalidatecoupons:manage

Statuses: Active, Used, Expired, Cancelled. Types (couponType): FixedDiscount, PercentageDiscount, FreeProduct, FreeShipping, Custom.

Issue a batch​

curl -s -X POST "$BASE/api/v1/coupons/batches" \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"programId":5,"count":500,"couponType":"PercentageDiscount","discountValue":10,"prefix":"SUMMER","codeLength":10,"maxUses":1,"validUntil":"2027-09-30"}'
# 201 {"batchId":"3F9A1C7E0B42","issued":500,"coupons":[{"id":812,"code":"SUMMER-K7Q2M9XH4T"}, ...]}
  • Up to 1,000 coupons per batch. Codes are random and unique.
  • codeFormat: Alphanumeric (default), Numeric or Alpha. codeLength from 6 to 20 (10 by default). If it is too short for count it is refused so codes stay hard to guess. prefix: up to 10 letters or digits.
  • maxUses empty = unlimited uses; 1 for single-use codes. maxUsesPerMember, pointsCost, minTierLevel, validFrom and validUntil are optional.
  • Coupons in a batch do not appear in the member portal unless you send isListed: true.
  • A retry with the same Idempotency-Key and the same body returns the same batch.
  • Errors: 409 code_space_exhausted (not enough free codes: lengthen codeLength or change the prefix), 409 conflict (clash with a simultaneous request: retry), 404 program_not_found / campaign_not_found, 403 feature_disabled.

Invalidate​

An Active coupon becomes Cancelled and can no longer be redeemed. Repeating the call is harmless (invalidated: 0). A coupon already Used or Expired gives 409 with code: coupon_not_active. Invalidating a batch answers { "invalidated": N }; an unknown batch gives 404 with code: batch_not_found.

Raffles​

GET /api/v1/campaigns/{id}/raffles (scope raffles:read) returns the campaign's completed raffles, oldest first, each with its winners and reserves. It does not depend on the public winners page being enabled.

curl -s "$BASE/api/v1/campaigns/17/raffles?limit=20" -H "Authorization: Bearer $KEY"

Each raffle includes drawId, drawnAt, scope, storeId, slotKey, the requested and drawn counts, eligiblePoolSize and the audit hashes printed on the certificate (poolHashSha256, outputHashSha256, seedSha256; never the seed itself). Each winner carries role, position, status, prizeId, notified and only your own references (externalParticipantRef, memberExternalId, claimId) plus the masked name and email (maskedName, maskedEmail). There is no contact data. A campaign with no raffles returns an empty items.

Errors: 404 campaign_not_found, 403 feature_disabled (campaigns), 400 invalid_cursor / invalid_request.

Member attribute schema​

GET /api/v1/member-attributes (scope read) returns the custom attributes your account has defined for members, ordered by displayOrder, so you know which keys and types a member's attributes object accepts.

curl -s "$BASE/api/v1/member-attributes" -H "Authorization: Bearer $KEY"

Each item carries key, displayName, description, dataType, isRequired, allowedValues, displayOrder, isActive and creationTime. dataType is numeric: 1 text, 2 number, 3 boolean, 4 date (yyyy-MM-dd) and 5 select (the value must be in allowedValues). Only active attributes are returned. Definitions are managed in the back office, not through this API; key and dataType do not change once a definition exists.

Next steps​