Events and qualifying event types
The Event types that RefRef accepts, which types can create a referral, how to send signups, purchases, custom Events, subscriptions, and refunds, and how RefRef decides each Event.
An Event is one fact about one participant: a signup, a purchase, or a milestone that you define. Your backend sends Events with POST /v1/track/events, or a connected integration sends them for you. Every Event gets an attribution decision, and benefit rules turn Events into Rewards.
Event types
| Type | Meaning | Key and time |
|---|---|---|
signup | The participant signed up. A participant has one signup. | Optional. Without them, RefRef uses one key per participant and the receipt time. |
purchase | A paid invoice or order, with its amounts | Derived from the invoice or order ID; the time is paidAt |
custom_… | A milestone that you define, such as custom_demo_booked | Required: sourceEventId and occurredAt |
A type that you define must start with custom_. Other names are reserved. RefRef refuses a type with 422 UNKNOWN_EVENT_TYPE when it is not built in, not a qualifying type of the Project, and not named by a benefit rule of the Project. Create the rule or add the qualifying type first.
Qualifying event types
The qualifying event types of a Project are the types that can create a new referral. The default is signup and purchase. A Workspace owner or admin changes the list in the Console.
| The Event's type | The participant has a referral | The participant has no referral |
|---|---|---|
| A qualifying type | The Event uses the referral | The Event can create a referral from an eligible click or code |
| Another type | The Event uses the referral | Unattributed, reason not_qualifying_type |
So an Event of any type can earn referrer and referee Rewards once a referral exists. A loyalty Program's subject rules evaluate every Event of their type, with or without a referral.
A change of the list applies to Events that arrive after the change. The start of a Subscription qualifies when purchase qualifies.
Use a custom qualifying type when the moment that matters is not a signup or a purchase, for example a booked demo for a sales-led product.
Send Events
Every Event names its Project, its source, and its participant:
{
"projectId": "prj_…",
"sourceNamespaceId": "…",
"type": "custom_demo_booked",
"sourceEventId": "demo_8812",
"occurredAt": "2026-10-08T10:00:00.000Z",
"participant": { "kind": "group", "externalId": "acct_7" },
"actor": { "kind": "individual", "externalId": "user_42" },
"refCode": "AB12CD3"
}actoris optional: the person who acted for a group participant.refCodeis optional: a referral code that the person typed. One code per Event. A code that counts wins over every click.- A
signupcan also carryidentify: { handle }. See signup tracking.
Purchases
A purchase sends its facts in purchase:
{
"projectId": "prj_…",
"sourceNamespaceId": "…",
"type": "purchase",
"participant": { "kind": "individual", "externalId": "user_42" },
"purchase": {
"resourceType": "invoice",
"externalResourceId": "in_123",
"paidAt": "2026-10-08T10:00:00.000Z",
"currency": "USD",
"currencyExponent": 2,
"paidAmountMinor": "4900",
"taxMinor": "0",
"commissionableAmountMinor": "4900"
}
}- Amounts are integers in minor units, as strings:
"4900"is 49.00 USD. commissionableAmountMinoris the part that percentage Rewards use. It must fit in the paid amount without tax.- Send only fully paid invoices or orders. A partial payment is not a purchase.
- For a subscription invoice, add
externalSubscriptionId, so the purchase belongs to its Subscription. sourceOrderIndexis optional: the customer's order number from your store, where 1 is the first order. A Project that accepts only new customers needs it.
A purchase carries no browser data. It uses the participant's referral.
Subscriptions
POST /v1/subscriptions/facts records the life of a Subscription: subscription.started (with commencementPhase trial or active), and later status changes such as a pause, a resume, or a cancellation. The start fixes the Subscription's origin. Its renewals use that origin without a new click, also after 90 days. A pause, a resume, or a cancellation does not reset it.
Refunds and disputes
POST /v1/purchase-adjustments records a refund, a lost dispute, or a restoration of a purchase. Rewards that were calculated from the purchase get a proportional adjustment. RefRef does not take back a Reward that was already delivered; void it in the Console if needed.
Paid history
A purchase rule counts a participant's purchases from a fixed start, so that a "first purchase" Reward pays only once and a period limit is correct. To do this, RefRef needs proof of the participant's paid purchases. A billing integration supplies it. Otherwise your backend sends it with POST /v1/evidence/paid-history: a list of every purchase of the participant from its binding time, or earlier, through the evaluated purchase. Until the proof arrives, the Reward evaluation waits. One proof serves every purchase rule.
How RefRef decides an Event
RefRef accepts the Event first, then decides it. An accepted Event can be pending. Its answer has a processingReason, for example awaiting_attribution. A fact dated in the future waits until its time.
| Outcome | Common reasons |
|---|---|
| Attributed | latest_eligible_touch (a click), referral_code_on_fact (a typed code), recorded_lead_preserved (an existing referral) |
| Unattributed | no_eligible_touch, not_qualifying_type, self_touch_excluded, existing_customer_excluded, predates_source_namespace, predates_recorded_lead, participant_limit_paused |
RefRef decides each fact once, with the identifications that it had received when the fact arrived. A fact never waits for an identification, and a later click never replaces a referral. event.recorded webhooks carry each decision.
Retries
Retry with the same body. A retry of an accepted fact returns the first result. A request that reuses a key with different facts answers 409 FACTS_CONFLICT. A participant has one signup: a second signup with another key answers 409 CONFLICT.