Programs and metrics

Organization

Your organization holds your programs and participants. It can have one organization budget, in one asset, that tracks rewards across all of your programs.

Get the organization

GET /organization → 200

{
  "id": "324a2411-d244-4bc1-bc4e-4ef439baa61d",
  "name": "Partner Network",
  "budget": { "amount": "1000000.000000", "asset": "USDPT" },
  "budgetUsage": {
    "amount": "1000000.000000",
    "used": "160.885888",
    "remaining": "999839.114112",
    "over": false,
    "programCount": 7
  }
}
Field Description
budget { amount, asset }, or null with no budget
budgetUsage Budget usage, plus programCount: the number of programs counted. null with no budget.

used adds up the reward totals of every period, estimated and finalized, of each program whose rewardAsset is the budget's asset. Test programs (isTest) don't count.

Admin keys only. A read-only key gets 404 Organization not found.

Update the organization budget

PATCH /organization → 200 Organization

Body field Type Required Description
budget object or null Yes { amount, asset }: amount greater than zero, asset an uppercase asset symbol. null removes the budget.
PATCH /api/v1/rewards/organization
Idempotency-Key: set-org-budget-2027

{ "budget": { "amount": "1000000", "asset": "USDPT" } }

The change applies immediately and only affects budget reporting. There's no expectedVersion: the last write wins.

Programs

The Program object

Field Type Description
id UUID Program ID
name string Display name, 1–200 characters
rewardAsset string The asset rewards are calculated in, for example USDPT
cadence string monthly
periodAnchorDay integer Day of the month periods start, 1–28
graceDays integer Days after a period ends before it can be finalized, 0–31
isTest boolean Marks a test program
programBudget amount or null Budget for the whole program; null for none. For tracking only.
status string active or ended
startsOn date First day of the program
endsOn date or null Last day, set when the program is ended
version integer Increases with every change to the program or its rules. Send it as expectedVersion when you update the program.
currentPeriodId string The period the program is in today. Before the program starts, its first period; after it ends, its last.
rules ProgramRule[] For each rule key, the rule in force in the current period
scheduledRules ProgramRule[] For each rule key, the next scheduled change (attach, upgrade or detach)
periods object count, byStatus (OPEN, FINALIZING, FINALIZED counts) and current (the current period: periodId, startsOn, endsOn, status, graceEndsAt)
createdAt, updatedAt timestamp

ProgramRule

Field Description
ruleKey The program’s name for the rule
ruleId Library rule ID
version Library version in use; null for a detach
action attach, upgrade or detach
effectivePeriodId The period this change applies from
name The library rule’s current name
definition The rule definition; null for a detach
summary The rule in one sentence
template, params, cap The template form, when the rule matches one; otherwise null

List programs

GET /programs → 200 { programs: Program[] }

Returns all your programs, oldest first. A read-only key sees only its own program.

Get a program

GET /programs/{id} → 200 Program

{
  "id": "2e369621-ef27-499f-88c0-15032f767c54",
  "name": "Partner Rewards",
  "rewardAsset": "USDPT",
  "cadence": "monthly",
  "graceDays": 5,
  "periodAnchorDay": 1,
  "isTest": false,
  "programBudget": "150000.000000",
  "status": "active",
  "startsOn": "2026-08-01",
  "endsOn": null,
  "version": 6,
  "createdAt": "2026-10-13T16:03:31.676Z",
  "updatedAt": "2026-10-13T16:03:35.815Z",
  "currentPeriodId": "2026-10",
  "rules": [
    {
      "ruleKey": "usdpt_holdings",
      "version": 1,
      "effectivePeriodId": "2026-08",
      "ruleId": "1e154cbc-0202-4504-84f7-a478d3f99b54",
      "name": "USDPT holdings",
      "action": "attach",
      "definition": {
        "measure": { "type": "daily_balance", "metricKey": "usdpt_balance" },
        "condition": { "type": "always" },
        "payout": { "type": "annual_rate", "rate": "0.035" },
        "limits": []
      },
      "summary": "3.5% a year on the daily balance of usdpt_balance",
      "template": "holdings",
      "params": { "metricKey": "usdpt_balance", "annualRate": "0.035" },
      "cap": null
    }
  ],
  "scheduledRules": [
    {
      "ruleKey": "settlement_growth",
      "version": 2,
      "effectivePeriodId": "2026-11",
      "ruleId": "695c549d-9e03-4818-ba1f-701c64d9b3cc",
      "name": "Settlement growth",
      "action": "upgrade",
      "definition": {
        "measure": { "type": "sum", "metricKey": "settlement_volume" },
        "condition": { "type": "growth_vs_previous_period", "minGrowthPercent": "10" },
        "payout": { "type": "percent_of_measure", "of": "growth", "rate": "0.001" },
        "limits": [{ "type": "cap_per_period", "amount": "900" }]
      },
      "summary": "0.1% of settlement_volume growth when it beats the previous period by at least 10%, at most 900 per period",
      "template": null,
      "params": null,
      "cap": null
    }
  ],
  "periods": {
    "count": 3,
    "byStatus": { "OPEN": 2, "FINALIZING": 0, "FINALIZED": 1 },
    "current": {
      "periodId": "2026-10",
      "startsOn": "2026-10-01",
      "endsOn": "2026-10-31",
      "status": "OPEN",
      "graceEndsAt": "2026-11-06T00:00:00.000Z"
    }
  }
}

Returns 404 Program not found if the program doesn’t exist in your account.

Create a program

POST /programs → 201 Program

Body field Type Required Description
name string Yes 1–200 characters
rewardAsset string Yes Uppercase asset symbol, for example USDPT
startsOn date Yes First day of the program
periodAnchorDay integer No Day of the month periods start, 1–28. Default 1.
graceDays integer No 0–31. Default 5.
isTest boolean No Default false
programBudget amount or null No Budget for the whole program. Must be greater than zero. Default null (no budget).
rules array Yes 0–50 rules, each either an attach item or a template item. [] is allowed.
  • Attach item: { ruleKey, ruleId, ruleVersion } attaches a version of a library rule.
  • Template item: { ruleKey, template, params, cap? } creates a library rule from a template and attaches it. cap is an optional per-period cap.

Rules in a new program apply from its first period. Creating a program also creates its built-in <asset>_balance metric.

POST /api/v1/rewards/programs
x-api-key: <admin key>
Idempotency-Key: create-partner-rewards-2027

{
  "name": "Partner Rewards 2027",
  "rewardAsset": "USDPT",
  "startsOn": "2027-01-15",
  "periodAnchorDay": 15,
  "graceDays": 7,
  "programBudget": "150000",
  "rules": [
    {
      "ruleKey": "holdings",
      "template": "holdings",
      "params": { "metricKey": "usdpt_balance", "annualRate": "0.035" },
      "cap": "2500"
    },
    { "ruleKey": "kyb_bonus", "ruleId": "de3b5ba5-fc7f-479a-8df8-7009c50da521", "ruleVersion": 1 }
  ]
}

The response is the new Program, with version: 1 and both rules in rules, effective from 2027-01.

Errors:

  • 400 invalid fields, or a rule key listed twice
  • 404 Rule not found, 404 rule_version_not_found
  • 409 rule_archived, 409 library_full
  • 422 a rule doesn’t fit the program, with path pointing to it, for example {"error":"metric_not_found","message":"Rule holdings: metric eurc_balance does not exist","path":"rules[0].definition.measure.metricKey"}

End a program

POST /programs/{id}/end → 200 Program

Ends the program at the end of its current period: status becomes ended and endsOn is set to the period’s last day. The current period still earns and can be finalized as usual. After this, the program’s rules can’t change. Send no body.

Errors: 404 Program not found; 409 Program has already ended.

Update a program

PATCH /programs/{id} → 200 Program

Body field Type Required Description
expectedVersion integer Yes The program’s current version
name string No Applies immediately
graceDays integer No Applies to periods created from now on
periodAnchorDay integer No Only before the program starts
programBudget amount or null No Sets or changes the budget; null removes it. Applies immediately and only affects budget reporting. It can be changed after a program has ended.
rules array No 1–50 template items. Each takes effect from the next period. Rule keys you don’t list are unchanged.

To attach, upgrade or detach library rules, use the program rules endpoints.

PATCH /api/v1/rewards/programs/220a893a-6072-49ee-b71f-ccd06e6d9343
Idempotency-Key: rename-partner-rewards-2027

{ "expectedVersion": 2, "name": "Partner Rewards 2027 (pilot)", "programBudget": "200000" }

Errors:

  • 404 Program not found
  • 409 Program is at version 6, not 2: read the program again and retry
  • 409 Program has ended; its rules cannot change
  • 400 programBudget: must be greater than zero
  • 409 period_anchor_locked: …: the anchor day can’t change once the program has started
  • 409 period_anchor_moves_first_period: …: the new anchor day would move the program’s first period
  • 422 a template rule doesn’t fit the program, or too_many_rules

Delete a program

DELETE /programs/{id} → 204

Permanently deletes a program that hasn’t started, for example one created with the wrong settings. Rules in your library are kept. Once a program has started, end it instead.

Errors: 404 Program not found; 409 program_started: started on <date>; 409 program_in_use: has <participants, activity, …>.

Metrics

A metric is a data series you report for a program. Rules read metrics to calculate rewards.

Property Values Meaning
kind snapshot One value per day (date), such as a daily balance
event One value per occurrence (occurredAt plus your eventId), such as a settlement
total One value per period (periodId), such as a monthly count
subject participant The value belongs to the participant
address The value belongs to one of the participant’s wallet addresses (Solana)
unit count or an asset code count values are whole numbers; asset values (for example USDPT) have up to 6 decimal places

Every program comes with a built-in metric, <asset>_balance (for example usdpt_balance): a daily snapshot of each wallet’s balance in the reward asset.

The Metric object

Field Type Description
key string Lowercase snake_case, unique in the program
kind string snapshot, event or total
subject string participant or address
unit string count, or an uppercase asset or currency code (2–12 characters)
name string or null Display name
description string or null
builtIn boolean true for the program’s <asset>_balance metric
deprecatedAt timestamp or null Set when the metric is deprecated

List metrics

GET /programs/{id}/metrics → 200 { metrics: Metric[] }

All of the program’s metrics, including deprecated ones, sorted by key.

{
  "metrics": [
    {
      "key": "settlement_volume",
      "kind": "event",
      "subject": "participant",
      "unit": "USD",
      "name": "Settlement volume",
      "description": "USD value of each completed settlement",
      "builtIn": false,
      "deprecatedAt": null
    },
    {
      "key": "usdpt_balance",
      "kind": "snapshot",
      "subject": "address",
      "unit": "USDPT",
      "name": null,
      "description": null,
      "builtIn": true,
      "deprecatedAt": null
    }
  ]
}

Add a metric

POST /programs/{id}/metrics → 201 Metric

Body field Type Required Description
key string Yes Lowercase snake_case, 1–63 characters
kind string Yes snapshot, event or total
subject string Yes participant or address
unit string Yes count or an uppercase code such as USD or USDPT
name string No 1–200 characters
description string No 1–2,000 characters
POST /api/v1/rewards/programs/2e369621-ef27-499f-88c0-15032f767c54/metrics
Idempotency-Key: add-settlement-volume

{ "key": "settlement_volume", "kind": "event", "subject": "participant", "unit": "USD", "name": "Settlement volume" }

Errors: 409 Metric settlement_volume already exists (including a deprecated metric with that key).

Deprecate a metric

POST /programs/{id}/metrics/{key}/deprecate → 200 Metric

New activity for the metric is rejected and new rules can’t use it. Existing rules and activity keep working. You can’t deprecate a metric that a current or scheduled rule reads: 409 Metric <key> is read by rule <ruleKey>. Deprecating a deprecated metric changes nothing.

Update a metric

PATCH /programs/{id}/metrics/{key} → 200 Metric

Changes name and/or description (null clears them). A metric’s key, kind, subject and unit can’t change; add a new metric instead.

Delete a metric

DELETE /programs/{id}/metrics/{key} → 204

Only for a metric you added that no rule or activity has ever used. Otherwise deprecate it.

Errors: 409 Metric <key> is built in and cannot be deleted; 409 Metric <key> is read by rule <ruleKey>; deprecate it instead; 409 Metric <key> has activity rows; deprecate it instead.