Conventions and errors

Conventions

  • JSON. Request and response bodies are JSON with camelCase fields, except CSV uploads (multipart/form-data) and the rewards export (CSV).
  • IDs. Programs, rules, imports and adjustments have UUIDs. Participants are identified by your own externalId (1–128 characters), URL-encoded in paths. Periods are YYYY-MM. Rule keys and metric keys are lowercase snake_case (1–63 characters).
  • Amounts are decimal strings, never JSON numbers: up to 6 decimal places in requests, exactly 6 in responses ("17.500000"). Rates are fractions with up to 18 decimal places ("0.035" = 3.5%).
  • Dates and times are UTC. Dates are YYYY-MM-DD; timestamps are ISO 8601 (2026-10-13T16:03:31.676Z).
  • Strict validation. Unknown body fields and unknown query parameters are rejected with 400.
  • Wallet addresses are Solana addresses (base58, 32–44 characters).

Idempotency

Every POST, PATCH and DELETE requires an Idempotency-Key header (1–255 characters), so a retried request never runs twice.

  • Repeating a request with the same key, method, URL and body within 24 hours returns the original response, with the header Idempotent-Replayed: true.
  • Reusing a key with a different request returns 422 idempotency_key_reused.
  • Repeating a request while the first is still running returns 409 idempotency_request_in_progress. Retry shortly.
  • Successful responses and 4xx errors are stored and replayed. After fixing a request that failed with a 4xx, send it with a new key. 5xx errors are not stored, so you can retry them with the same key.
  • Keys are scoped to your API key.

Versioning

PATCH /programs/{id} and PATCH /rules/{ruleId} require expectedVersion, the version you last read. If the record has changed since, the request returns 409; read it again and retry.

Pagination

Style Endpoints Parameters Response
Offset Participants, period rewards, adjustments limit (default 50, max 500), offset (default 0) The collection plus total, the number of matching records
Cursor Activity limit (default 100, max 1000), cursor rows plus nextCursor (null on the last page)

Other lists return everything in one response.

Background processing

  • Reward calculation runs in the background, usually within seconds. pendingRecalculation on rewards and pendingRecalculations on periods show work in progress.
  • CSV imports are queued: the upload returns 202 with an importId, which you poll for status and results.
  • Finalizing a period waits for its calculations to finish, so it can take up to about 60 seconds. Use a client timeout above that.
  • There are no webhooks. Poll the read endpoints for changes; activity supports updatedSince for incremental sync.

Limits

Limit Value
Request body 1 MiB (activity: 4 MiB)
Activity rows per request 1,000
Activity metadata 2,048 bytes per row
CSV upload One file, up to 20 MiB
Rule keys per program 50
Rules per library 500
Participant tags 20, each up to 64 characters
Rule preview 10,000 participants per period

Errors

Errors return a JSON body with an error field. Many also carry a message, and rule-definition errors carry a path pointing into your request.

{ "error": "Program not found" }
{ "error": "version_conflict", "message": "Rule is at version 2, not 1" }
{ "error": "tiers_gap", "message": "tier 1 must start at 10", "path": "definition.payout.tiers[1].from" }
  • Validation errors (400) describe the first problem found, as "<field>: <problem>", for example "limit: Too big: expected number to be <=500".
  • Some 409 errors start with a code followed by details, such as "grace_window_open: ends at 2026-11-06T00:00:00.000Z". Match on the text before the first colon.
  • Batch endpoints (activity and imports) report problems per row in a 200 response instead of failing the whole request. See Activity.
HTTP Meaning
400 Invalid request: validation, a missing or too-long Idempotency-Key, or a bad CSV header
401 Missing or invalid API key
403 A read-only key tried to change something
404 The resource doesn’t exist in your account, or isn’t visible to your key
409 Conflict with the current state: version conflict, period not ready, rule archived, program ended
413 Request body or file too large
422 A rule definition that isn’t valid, a reused idempotency key, or an atomic activity batch that was rejected
500 Unexpected error. Retry with the same Idempotency-Key

The full list of codes is in Error codes.

Error codes

HTTP error Meaning
400 idempotency_key_required Idempotency-Key is missing
400 idempotency_key_too_long Idempotency-Key is over 255 characters
401 Missing API key / Invalid API key No key, or a key that isn’t valid
403 A read-only API key cannot modify resources A read-only key tried to change something
404 Program not found, Participant not found, Period not found, Metric not found, Rule not found, Import not found, Organization not found Doesn’t exist in your account
404 participant_not_found No participant with that externalId
404 rule_not_attached The rule key isn’t attached to the program
404 rule_version_not_found The rule has no such version
409 idempotency_request_in_progress The same request is still running
409 version_conflict The rule changed since you read it
409 Program is at version <n>, not <m> The program changed since you read it
409 participant_already_exists The externalId is already taken
409 participant_already_enrolled The participant is already in the program, or was removed from it
409 participant_removed The participant has been removed
409 rule_archived The rule is archived
409 rule_key_taken The rule key is linked to a different rule
409 rule_already_attached The rule key is already attached
409 program_ended The program has ended, so its rules can’t change
409 library_full Your library already has 500 rules
409 preview_too_large The period has more than 10,000 participants
409 period_not_finalized Export needs a finalized period
409 period_already_finalized, grace_window_open, previous_period_not_finalized, recalculation_pending, recalculation_failed See Finalize a period
409 no_open_period No open current period for an adjustment
409 program_started, program_in_use The program can’t be deleted
409 period_anchor_locked, period_anchor_moves_first_period The anchor day can’t change
400 field_moved_to_org_participant KYB status or tags sent to a program enrollment endpoint
413 File exceeds 20971520 bytes CSV over 20 MiB
422 idempotency_key_reused The key was used for a different request
422 too_many_rules Over 50 rule keys in the program
422 invalid_definition, condition_not_allowed, payout_not_allowed, limit_not_allowed, duplicate_limit, growth_payout_needs_growth_condition, tiers_overlap, tiers_gap The rule definition isn’t valid
422 metric_not_found, metric_deprecated, metric_kind_mismatch, metric_subject_mismatch, unit_mismatch The rule doesn’t fit the program’s metrics
422 Batch rejected; no rows were applied An atomic activity batch had a rejected row
500 Internal server error Unexpected error; retry with the same Idempotency-Key

Endpoint index

Paths are relative to /api/v1/rewards. Read-only endpoints can be called with either key type; Admin endpoints need an admin key.

Method and path Key role Purpose
Organization
GET /organization Admin Get your organization and its budget usage
PATCH /organization Admin Set or clear the organization budget
Programs
GET /programs Read-only List programs
GET /programs/{id} Read-only Get a program with its current and scheduled rules
POST /programs Admin Create a program
POST /programs/{id}/end Admin End a program
PATCH /programs/{id} Admin Update a program’s name, grace days, anchor day or template rules
DELETE /programs/{id} Admin Delete a program that hasn’t started
Rule library
GET /rule-blocks Read-only Catalog of measures, conditions, payouts and limits
GET /rule-templates Read-only Catalog of rule templates
GET /rules Read-only List library rules
GET /rules/{ruleId} Read-only Get a rule at its latest version
GET /rules/{ruleId}/versions Read-only List every version of a rule
POST /rules Admin Create a rule
POST /rules/validate Admin Check a rule definition without saving it
POST /rules/{ruleId}/archive Admin Archive a rule
PATCH /rules/{ruleId} Admin Update a rule; a new definition creates a new version
Program rules
GET /programs/{id}/rules Read-only Rules in force and scheduled changes
POST /programs/{id}/rules Admin Attach a rule version from the next period
POST /programs/{id}/rules/preview Admin Preview what a rule would pay, without saving
PATCH /programs/{id}/rules/{ruleKey} Admin Move a rule key to another version from the next period
DELETE /programs/{id}/rules/{ruleKey} Admin Detach a rule from the next period
Metrics
GET /programs/{id}/metrics Read-only List a program’s metrics
POST /programs/{id}/metrics Admin Add a metric
POST /programs/{id}/metrics/{key}/deprecate Admin Stop accepting activity for a metric
PATCH /programs/{id}/metrics/{key} Admin Update a metric’s name or description
DELETE /programs/{id}/metrics/{key} Admin Delete a metric that has never been used
Participants
GET /participants Admin List participants across your organization
GET /participants/{externalId} Admin Get a participant, with their programs and wallets
POST /participants Admin Create a participant
PATCH /participants/{externalId} Admin Update KYB status or tags
DELETE /participants/{externalId} Admin Remove a participant from every program
Program enrollment
GET /programs/{id}/participants Read-only List participants enrolled in a program
GET /programs/{id}/participants/{externalId} Read-only Get an enrolled participant with their current-period reward
POST /programs/{id}/participants Admin Enroll a participant in a program
PATCH /programs/{id}/participants/{externalId} Admin Update eligibility in a program
DELETE /programs/{id}/participants/{externalId} Admin Remove a participant from a program
Activity
GET /programs/{id}/activity Read-only List activity rows
GET /programs/{id}/activity/summary Read-only Totals per participant for one metric and period
POST /programs/{id}/activity Admin Report up to 1,000 activity rows
POST /programs/{id}/activity/deletions Admin Delete up to 1,000 activity rows
CSV imports
GET /participants/imports/{importId} Admin Status and results of a participants import
POST /participants/imports Admin Upload a participants CSV
GET /programs/{id}/participants/imports/{importId} Read-only Status and results of an enrollment import
GET /programs/{id}/activity/imports/{importId} Read-only Status and results of an activity import
POST /programs/{id}/participants/imports Admin Upload an enrollment CSV
POST /programs/{id}/activity/imports Admin Upload an activity CSV
Periods and rewards
GET /programs/{id}/periods Read-only List periods
GET /programs/{id}/periods/{periodId}/rewards Read-only Rewards per participant for a period
GET /programs/{id}/periods/{periodId}/rewards/export Read-only Download a finalized period’s rewards as CSV
POST /programs/{id}/periods/{periodId}/finalize Admin Finalize a period
Adjustments
GET /programs/{id}/adjustments Read-only List adjustments
POST /programs/{id}/adjustments Admin Add an adjustment to the current period
Statistics
GET /programs/{id}/stats Read-only Program totals and a per-period series