API overview
Authenticate with a Workspace API key, choose the Sandbox or Live environment, read the error envelope, and find every API route.
The RefRef API serves your trusted backend: it accepts Events and identifications, enrolls participants, creates referral codes, and reads Rewards and webhook events. The base URL is https://api.refref.ai.
The running API publishes its OpenAPI document at GET https://api.refref.ai/openapi. It is the reference for every request and response schema, and its webhooks section has a schema and a sample for each webhook event.
Authentication
Send a Workspace API key in the X-Api-Key header:
curl https://api.refref.ai/v1/rewards?projectId=prj_… \
-H "X-Api-Key: $REFREF_API_KEY"Owners and admins create API keys in the Console. A key belongs to the Workspace, not to the person who created it, so it keeps working after that person leaves.
Keep the key on your server. Never put it in browser code, mobile apps, prompts, or logs.
Two routes do not take an API key, because the browser calls them:
| Route | Credential |
|---|---|
POST /v1/capture | The handoff of a referral click. The landing script calls it. |
POST /v1/widget/init | A short-lived Widget token that your backend signs. See Widget. |
Environments
Each key belongs to one environment: Sandbox or Live. Each Project is a pair of a Live Project and its Sandbox Project, and a key reaches only the Projects of its own environment.
| The key calls | Answer |
|---|---|
| A Project of its Workspace and environment | The operation runs |
| The other environment's Project | 403 ENVIRONMENT_MISMATCH |
| A Project of another Workspace | 404 NOT_FOUND |
Use a Sandbox key and the Sandbox Project ID while you build. Change both when you go live.
API key scopes
Each API key has a reach and a set of permissions.
Reach. A key is owned by its Workspace and bound to Sandbox or Live. It reaches either all Projects of its environment, also Projects that you add later, or a list of Projects that you select. A Project outside the key answers 404 NOT_FOUND. A key never reaches another Workspace. If you run Projects in two Workspaces, create one key in each.
Permissions. Choose a preset when you create the key, or set custom permissions:
| Preset | Use it for |
|---|---|
| Integration | Your application backend: send Events and identifications, manage participants, enrollments, and codes, fulfill Rewards, and manage webhook endpoints |
| Read-only | Reports and dashboards: every read, no writes |
| Full access | Every API operation |
Custom permissions combine these resources and actions:
| Resource | Actions | Covers |
|---|---|---|
intake | write | Events, subscription facts, purchase adjustments, paid history, identifications |
participants | read, write | Participants, enrollments, referral codes and links |
programs | read, write | Programs, benefit rules, reward terms, referee offers, delivery settings, join pages, custom units, qualifying event types |
referrals | read, write | Referrals, Events, purchases, evaluations, adjustments; write records a referral that you know |
rewards | read, fulfill, void | Rewards; fulfill records a fulfillment or a revocation; void voids a Reward with a reason |
partners | read, write | Partners, their payout history, invitations, applications, bans |
integrations | read | Connections, diagnostics, webhook deliveries |
webhooks | read, write | Webhook endpoints and the event feed |
An operation outside the key's permissions is refused. A key without permissions is refused.
These are never available through an API key: payout runs and payout settings (a partner's payout history is a partners read), RefRef billing, Project secrets, Workspace members and invitations, API key management, and provider credentials. Use the Console for them.
Routes
| Area | Routes |
|---|---|
| Facts | POST /v1/track/events, POST /v1/subscriptions/facts, POST /v1/purchase-adjustments, POST /v1/evidence/paid-history |
| Identification | POST /v1/identify, POST /v1/capture (browser) |
| Participants | PUT /v1/participants, POST /v1/participants/rekey |
| Enrollment and codes | POST /v1/enrollments, POST /v1/enrollments/{enrollmentId}/end, POST /v1/referral-codes, POST /v1/referral-links |
| Programs | GET /v1/programs, POST /v1/programs, GET /v1/programs/{programId}, PATCH /v1/programs/{programId}, GET /v1/reward-rules, POST /v1/benefit-rules, POST /v1/benefit-rules/{ruleId}/terms, referee offer and delivery settings, POST /v1/attribution/policies |
| Referrals | POST /v1/referrals: record a referral that you know, for example from a sales call |
| Data | GET /v1/projects, GET /v1/participants, GET /v1/referrers, GET /v1/referrals, GET /v1/events, GET /v1/events/{eventId}/diagnosis, GET /v1/purchases, GET /v1/connections, GET /v1/webhook-deliveries |
| Partners | GET /v1/partners, GET /v1/partners/{participantId}, GET /v1/partners/{participantId}/payouts, GET /v1/partner-applications, POST /v1/partner-invitations, POST /v1/partner-invitations/{partnerRequestId}/revoke, POST /v1/partner-applications/{partnerRequestId}/approve and /decline, POST /v1/partners/{partnerId}/ban and /unban |
| Operations | POST /v1/rewards/{rewardId}/void, POST /v1/rewards/{rewardId}/delivery/retry |
| Custom units | GET /v1/custom-units, POST /v1/custom-units, PATCH /v1/custom-units/{key} |
| Rewards | GET /v1/rewards, GET /v1/rewards/{rewardId}, POST /v1/rewards/{rewardId}/fulfillment, POST /v1/rewards/{rewardId}/revocation |
| Webhooks | GET /v1/webhook-events, POST /v1/webhook-endpoints, GET /v1/webhook-endpoints, DELETE /v1/webhook-endpoints/{endpointId} |
| Widget (browser) | POST /v1/widget/init, GET /v1/widget/payout-details, PUT /v1/widget/payout-details |
The API reference lists every route with its permission. Each body or query names its Project with projectId. A body names a participant with your own ID: { "kind": "individual" | "group", "externalId": "…" }. A call that names an unknown participant creates it.
Successful answers
A successful answer has success: true and the result of the operation.
Retries
Retry a request with the same body. RefRef derives a key from each fact, so a retry of an accepted fact returns the first result and records nothing twice. A request that reuses a fact's key with different business facts answers 409 FACTS_CONFLICT. Do not change the facts to make a retry pass.
Errors
Every error has the same body:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "…",
"details": [{ "path": "participant.externalId", "message": "…" }],
"requestId": "req_…"
}
}| Code | Status | Meaning |
|---|---|---|
VALIDATION_ERROR | 400 | The request does not match its schema. details lists each field. |
INVALID_REQUEST | 400 | RefRef refuses the request for another reason, for example an expired handoff |
AUTH_REQUIRED | 401 | No valid API key or Widget token |
FORBIDDEN | 403 | The Widget token lacks the scope of the route |
ENVIRONMENT_MISMATCH | 403 | The key's environment differs from the Project's environment |
NOT_FOUND | 404 | The resource does not exist or belongs to another owner |
CONFLICT | 409 | The request conflicts with accepted state, for example a second signup with another key |
FACTS_CONFLICT | 409 | The fact's key already names different business facts |
UNKNOWN_EVENT_TYPE | 422 | The Event type is not built in, not a qualifying type of the Project, and no benefit rule names it |
RATE_LIMIT_EXCEEDED | 429 | Too many requests. Wait the seconds of the Retry-After header, then retry |
INTERNAL_SERVER_ERROR | 500 | An unexpected error. Retry with the same body. |
message is for people; do not parse it. Handle an unknown code by its HTTP status. Give the requestId when you contact us about a request.
The API accepts a fact before RefRef decides it. An accepted Event can still
be pending: its answer has a processingReason. Read the result later from
webhooks or the Console.