Skip to content

Loyalty Events

Your backend sends business events (purchase, user_registered, refund) to POST /ingest/events. Each event is queued and then evaluated against the project’s active rules, and the actions of every matching rule run (reward, redemption, integration, …). Every processed event leaves a workflow record, whose id is the event id, showing what matched and what each action did.

POST https://api.storelayer.io/ingest/events
X-API-Key: <project API key>
Content-Type: application/json
{
"type": "purchase",
"userId": "cust_8421",
"payload": { "orderId": "ord_1001", "amount": 7450, "currency": "USD" }
}
FieldRequiredNotes
typeyesNon-empty string. Rules bind to it.
userIdnoString. Your stable external customer ID. Wallet actions default to it.
payloadyesJSON object (can be {}). Rules read it as $('event').payload.<field>.
  • Authentication: a project API key in X-API-Key or Authorization: Bearer <key>. The project comes from the key, so the path has no projectId.
  • Unknown top-level fields are silently dropped. They are not rejected. {"type":"purchase","payload":{},"amount":7450} is accepted with amount discarded, and no rule ever sees it. Put all business data inside payload.
  • A missing type/payload or a wrong type (for example userId: 123) returns 400.
  • There is no idempotency key. Every call creates a new event id. See Sending business events.
  • payload._meta is reserved for event chaining (emit_event). Do not send it.

Response (201): { "success": true, "data": { "id": "evt_…", "status": "accepted" } }.

  1. Ingest validates the body, assigns evt_…, enqueues it and returns 201. Nothing has been evaluated yet.
  2. A worker loads the active rules whose event binding matches type (plus rules with no event-type binding), resolves only the resources each rule declares, and evaluates its conditions.
  3. The actions of every matched rule run in order. Rules are sorted by priority (highest first), then by creation time. Rules bound to a specific type run before unbound ones. There is no stop-processing flag: every matching rule fires.
  4. One workflow is saved with status completed or failed. See Workflows.

A 201 only means the event was queued. A reward is confirmed only by the workflow’s action result and the wallet.

{
"name": "100 points for purchases of $50+",
"conditions": {
"combinator": "AND",
"conditions": [
{ "leftValue": "{{ $('event').payload.amount }}", "operator": "gte", "rightValue": 5000, "rightType": "number" }
]
},
"resources": [{ "id": "<event resource id>", "key": "purchase", "type": "event", "value": {} }],
"actions": [{ "type": "reward", "config": { "assetType": "points", "amount": 100 } }],
"priority": 0
}
PartRule
conditionsAt least one condition. combinator is AND or OR (a single flat group). Operators and expression syntax are covered in Conditions and resources.
resourcesMust include at least one event resource. Every key must be registered on the project first (POST /projects/{projectId}/resources) with the same type.
actionsOne action or an array. Types: reward, redemption, modify, integration, apply_referral, complete_referral, mark_code_used, emit_event, update_user, custom_table. Config fields are listed in the loyalty program guide.
priorityInteger, default 0. It sets the order only. It never suppresses other rules.
validFrom / validToOptional ISO timestamps. Outside the window the rule is skipped.
isActivetrue on create. Change it with PATCH /projects/{projectId}/rules/{ruleId}.

Choose one of two ways:

BindingHowImportant distinction
Event resource key = event typeRegister an event resource with key: "purchase" and declare it on the ruleKeys must match ^[a-zA-Z][a-zA-Z0-9_]*$. Use order_completed, not order.completed.
Generic key event + type conditionDeclare { "key": "event", "type": "event" } and add {"leftValue": "{{ $('event').type }}", "operator": "equals", "rightValue": "purchase"}Works for any type string.

A rule with neither runs for every event type. Event data is always read as $('event'), whatever the event resource is called. $('purchase') is undefined.

During live processing a rule sees only event plus the resources listed in its own resources array. If a condition uses $('wallet') and the rule does not declare wallet, the value is undefined and the condition is false, with no error. The REST API does not infer or auto-create resources for rules.