LoyumiDeveloper docsOpen Sandbox
TASK GUIDES

Integrate the complete customer lifecycle.

Follow tested request shapes for earning, reading balances, returns, engagement events, offers, and signed outcomes.

Developer docs · no accountSandbox firstAPI 1.13.0 · 121 operations · 106 paths

Identity first. Permission second. Value third.

A member identity is not permission to join every loyalty program. Follow these four steps from a trusted backend so consent, value, and exit remain explainable.

  1. 1

    Create identity

    POST the stable customer ID and identity fields to /members. This creates no program profile or points account. A legacy nonempty programIds body receives 422 explicit_program_enrollment_required.

  2. 2

    Enroll one program

    PUT the exact program path with consentStatus: opted_in, the notice version shown, a nonempty evidence object, and one stable idempotency key. 201 means a profile or account was created; 200 means it already existed or the result was replayed. Existing balances are never reset.

  3. 3

    Move value only after enrollment

    Earn, reward, redemption, credit-adjustment, and partner-exchange entry paths check active program enrollment at the database write boundary.

  4. 4

    Unenroll only at zero

    DELETE the same program path with a reason, evidence, and idempotency key. Available, pending, and reserved balances must all be zero and no reservation may remain. The command expires scoped choice awards and cancels scoped quotes, but retains ledger, transaction, consent, audit, and reward history. It remains available while Production is locked.

1 · Create identity

Use the customer ID your own identity system already trusts.

POST /api/v1/members
{
  "externalCustomerId": "<external-customer-id>",
  "displayName": "<display-name>",
  "email": "<customer-email>",
  "consentStatus": "opted_in",
  "noticeVersion": "<identity-notice-version>"
}
2 · PUT explicit enrollment
PUT /api/v1/members/<external-customer-id>/programs/<program-id>
Authorization: Bearer <server-api-key>
X-Environment: <environment-id>
Idempotency-Key: <stable-enrollment-key>
Content-Type: application/json

{
  "consentStatus": "opted_in",
  "noticeVersion": "<rewards-notice-version>",
  "evidence": { "source": "<consent-capture-channel>" }
}
4 · DELETE governed unenrollment
DELETE /api/v1/members/<external-customer-id>/programs/<program-id>
Authorization: Bearer <server-api-key>
X-Environment: <environment-id>
Idempotency-Key: <stable-unenrollment-key>
Content-Type: application/json

{
  "reason": "<approved-withdrawal-reason>",
  "noticeVersion": "<rewards-notice-version>",
  "evidence": { "source": "<withdrawal-channel>" }
}

Award a completed purchase.

Create the program, active member, and events:write key in the same environment before sending the event.

POST /api/v1/events
curl -X POST "https://app.loyumi.com/api/v1/events" \
  -H "Authorization: Bearer <sandbox-key>" \
  -H "X-Environment: <sandbox-environment-id>" \
  -H "Idempotency-Key: order-10042" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "purchase.completed",
    "programId": "<program-id>",
    "externalCustomerId": "customer-123",
    "amountMinor": 2599,
    "channel": "web",
    "currency": "USD",
    "sourceReference": "order-10042",
    "items": [{
      "lineReference": "order-10042-line-1",
      "sku": "sku-…",
      "quantity": 1,
      "unitPriceMinor": 2599
    }]
  }'

Choose the evaluation clock deliberately

By default, Loyumi evaluates a purchase when it is received, using the current published configuration and current member tier. That is the simplest choice for live commerce. For a late or replayed event that must use historical policy, explicitly pin the event to the configuration and tier evidence that were valid at occurredAt.

Historical evaluation evidence
{
  "type": "purchase.completed",
  "occurredAt": "2026-08-01T18:42:00.000Z",
  "context": {
    "attributes": {
      "loyumiEvaluation": {
        "mode": "event_time_pinned_configuration",
        "configurationVersion": 7,
        "memberTier": "gold"
      }
    }
  }
}
receipt_time_current_configuration

The default. Uses the configuration and tier known when Loyumi receives the event.

event_time_pinned_configuration

Requires occurredAt, a positive immutable configurationVersion that was published and effective then, and historical memberTier evidence.

Response evidence

Keep evaluationMode, evaluationAt, configurationVersion, loyaltyUnitPrecision, and ruleDecisions with your order evidence.

Use exact earn math when economics require it

An earn rule can use integer numerator and denominator values, explicit currency and loyalty precision, and a named rounding policy. This avoids floating-point drift and makes every decision reproducible.

Rule definition example
{
  "match": "all",
  "conditions": [
    { "field": "channel", "operator": "equals", "value": "app" }
  ],
  "earn": {
    "currencyExponent": 2,
    "loyaltyUnitPrecision": 0,
    "rateNumerator": 3,
    "rateDenominator": 2,
    "rounding": "floor",
    "tierRounding": "nearest_half_up"
  }
}

Read a member and their program balances.

URL-encode the external customer ID. The response includes identity status, consent state, tier, qualification points, and account balances by program.

  • Requires members:read.
  • Uses the same X-Environment as the award.
  • Returns Cache-Control: no-store.
Member request
GET /api/v1/members/customer-123
Authorization: Bearer <sandbox-key>
X-Environment: <sandbox-environment-id>

Claw back a full or partial order.

Reference the original order, then select the original lineReference and returned quantity. Loyumi derives the exact attributable amount and points from the stored purchase lines.

POST /api/v1/returns
curl -X POST "https://app.loyumi.com/api/v1/returns" \
  -H "Authorization: Bearer <sandbox-key>" \
  -H "X-Environment: <sandbox-environment-id>" \
  -H "Idempotency-Key: return-10042-1" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceReference": "order-10042",
    "returnReference": "return-10042-1",
    "items": [
      { "lineReference": "order-10042-line-1", "quantity": 1 }
    ]
  }'

Advance a challenge with a registered event.

The event definition must already be active in the selected environment and the member must exist. Use a source reference that identifies the real behavior once.

  • Requires events:write.
  • Requires an idempotency key.
  • Returns any completed challenge awards in awards.
Request body
{
  "type": "profile.completed",
  "externalCustomerId": "customer-123",
  "sourceReference": "profile-customer-123-v1",
  "occurredAt": "2026-08-02T08:00:00.000Z",
  "attributes": { "source": "account_page" }
}

Evaluate a cart without mutating it.

Use an offers:evaluate credential and send positive integer minor-unit prices plus the member, channel, currency, market, store, tag, and line context relevant to the decision. The result explains subtotal, discount, total, eligible effects, matched lines, and coupon validity.

POST /api/v1/offers/evaluate
{
  "programId": "prg_…",
  "externalCustomerId": "customer-123",
  "couponCode": "WELCOME10",
  "channel": "web",
  "currency": "USD",
  "market": "US",
  "items": [
    {
      "lineReference": "cart-line-1", "sku": "SKU-42",
      "category": "coffee", "quantity": 2, "unitPriceMinor": 650
    }
  ]
}
NEXT
Inspect every field

Use the rendered reference for exact requirements and status codes.

Open API reference →

Find the business fact, then inspect its balanced entries.

  1. 1

    Search by evidence

    Use the commerce sourceReference, customer ID, event type, state, or bounded time window.

  2. 2

    Open the transaction

    Copy the returned transaction ID into the detail endpoint.

  3. 3

    Reconcile the entries

    Confirm program, event, source reference, state, reversal link, timestamps, and balanced ledger entries before deciding on a correction.

Search and inspect
GET /api/v1/transactions?sourceReference=<source-reference>
Authorization: Bearer <server-api-key>
X-Environment: <environment-id>

GET /api/v1/transactions/<transaction-id>
Authorization: Bearer <server-api-key>
X-Environment: <environment-id>

Check progress, then prove who changed what.

/operations answers “what is this long-running job doing?” /audit-events answers “which credential or operator acted on which target?” Keep the scopes separate.

operations:read

List jobs by type or status, then open one operation for progress and related import evidence.

audit:read

Filter environment-attributed audit evidence by action, target type, target ID, or actor ID.

Follow a job and its audit trail
GET /api/v1/operations?status=<operation-status>&limit=50
GET /api/v1/operations/<operation-id>
GET /api/v1/audit-events?targetId=<target-id>&limit=50

Authorization: Bearer <server-api-key>
X-Environment: <environment-id>

Ask a bounded ledger question—not a finance-close question.

The ledger summary accepts a positive window of at most 90 days and an optional program. It returns actual ledger and operational aggregates as a best-effort request view.

Bounded ledger summary
GET /api/v1/analytics/ledger-summary?programId=<program-id>&from=<ISO-timestamp>&to=<ISO-timestamp>
Authorization: Bearer <server-api-key>
X-Environment: <environment-id>
USE IT FOR

Operational description

  • Posted and reversed transaction counts
  • Available point credits, debits, and net movement
  • Current member balances and redemption states
  • Environment-wide webhook delivery status
DO NOT USE IT AS

Financial close or causal proof

  • Not a locked accounting snapshot
  • Not revenue attribution
  • Not incremental lift
  • Not a replacement for reconciliation or BI

Turn one program and one period into a checkable roll-forward.

The close report reads posted ledger entries, uses decimal strings so large balances stay exact, checks both the member roll-forward and double-entry totals, and hashes the evidence payload with SHA-256.

  1. 1

    Choose one program

    Copy its program ID. A close never mixes programs or currencies.

  2. 2

    Set exact UTC edges

    Use a half-open period: from is included and to is excluded. Omit from to start at the first day of the UTC month.

  3. 3

    Run the report

    Call the endpoint with an analytics:read server credential. The window may be up to 366 days.

  4. 4

    Read the four lights

    Confirm ledger controls pass, a finance policy exists, that policy is approved, and its journal mapping is complete.

  5. 5

    Save and review

    Retain the report ID, request ID, SHA-256 hash, boundaries, and response. Your finance owner or independent auditor still signs off outside Loyumi.

Generate exact close evidence
GET /api/v1/reports/ledger-close?programId=<program-id>&from=<ISO-timestamp>&to=<ISO-timestamp>
Authorization: Bearer <server-api-key>
X-Environment: <environment-id>
X-Request-Id: <close-run-id>
STATUS

What the light means

  • points_only: no valuation policy
  • provisional: policy or journal approval remains
  • ready_for_external_review: internal controls and policy evidence are ready
  • control_exception: stop and reconcile
BOUNDARY

What it does not claim

  • Not an independent audit
  • Not a contractual financial statement
  • Not revenue attribution or incremental lift
  • Not proof of an external backup or DR exercise

Create → save secret → activate → test → inspect → retry.

  1. 1

    Choose only the events you need

    Select one to twenty public lifecycle events, then create the endpoint disabled. Existing points.earned-only subscriptions keep working unchanged.

  2. 2

    Save the signing secret now

    The successful create response returns signingSecret once. Put it in your secret manager. An idempotent replay does not reveal it again.

  3. 3

    Activate only after installation

    Patch { "status": "active" } after the receiver can verify signatures. Changing the URL forces the endpoint back to disabled.

  4. 4

    Send a safe test

    POST an exact empty object to /test. The internal test event contains no customer PII and cannot be selected as a business subscription.

  5. 5

    Inspect evidence

    Filter deliveries by endpoint, event type, or status. Public delivery views omit stored payloads.

  6. 6

    Retry deliberately

    Retry only a failed or dead-letter delivery with a stable key. Retry re-queues the same record; it does not contact your server synchronously or create a duplicate record.

Endpoint setup
POST /api/v1/webhook-endpoints
Idempotency-Key: <stable-create-key>
{ "url": "<public-https-endpoint>",
  "eventTypes": ["points.earned", "redemption.committed", "adjustment.posted"] }

PATCH /api/v1/webhook-endpoints/<endpoint-id>
Idempotency-Key: <stable-activate-key>
{ "status": "active" }

POST /api/v1/webhook-endpoints/<endpoint-id>/test
Idempotency-Key: <stable-test-key>
{}

GET /api/v1/webhook-deliveries?endpointId=<endpoint-id>&status=failed

POST /api/v1/webhook-deliveries/<delivery-id>/retry
Idempotency-Key: <stable-retry-key>
{}
Membership + program

member.enrolled, member.unenrolled, and program.published.

Redemption

Reserved, committed, released, expired, reversed, fulfillment update, and dispute opened/resolved/rejected.

Governed operations

Adjustment requested/rejected/posted/reversed and import completed.

Partner clearing

Exchange settled and reversed, alongside the original points.earned event.

Keep the app thin and the loyalty secret on your backend.

iOS / Android appSends the merchant login and customer-safe request
Merchant backendAuthenticates the customer and calls Loyumi with the secret
Loyumi APIReturns only the result the merchant chooses to expose
  • The Loyumi API credential stays server-side and never enters the app binary, device storage, logs, crash reports, or analytics.
  • Your backend maps the signed-in merchant customer to externalCustomerId and returns only merchant-defined, customer-safe data.
  • The official iOS and Android 1.1.0 source SDKs call your merchant backend gateway. Each contains a 1.0.0-beta.1 native rewards UI kit; neither SDK has a Loyumi API-key setting or connects a customer device directly to the Loyumi API.
  • Web Widget session codes are browser-origin-bound handoff material for the hosted frame. Do not repurpose them as native-app tokens.
GET /loyalty/v1/member

Return the signed-in customer's member, available points, and tier summary.

PUT /loyalty/v1/programs/{programId}/enrollment

Enroll the authenticated customer with affirmative consent evidence; identity is derived by the backend, never supplied by the app.

DELETE /loyalty/v1/programs/{programId}/enrollment

Request zero-balance unenrollment while retaining governed history.

GET /loyalty/v1/activity

Return a bounded customer-safe activity page and opaque next cursor.

GET /loyalty/v1/catalog

Return only rewards this customer surface may display.

POST /loyalty/v1/redemptions/quote

Price and validate the selected reward without spending points.

POST …/reserve → commit

Hold value first, then commit only after merchant checkout authorization succeeds.

POST …/release or reverse

Release an unused hold or request a governed reversal under merchant policy.