Authentication
All Referral Reactor API endpoints (except GET /api/v1/health) require authentication via a Clerk-issued API key supplied as a Bearer token in the Authorization header.
Bearer Token Usage
Include your API key in every request using the standard HTTP Authorization header:
Authorization: Bearer <api_key>
curl
curl https://app.referralreactor.com/api/v1/referrals \
-H "Authorization: Bearer rr_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json"
TypeScript / JavaScript
const BASE_URL = 'https://app.referralreactor.com';
const API_KEY = process.env.REFERRAL_REACTOR_API_KEY; // never hardcode
async function fetchReferrals() {
const response = await fetch(`${BASE_URL}/api/v1/referrals`, {
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
const error = await response.json();
throw new Error(
`[${response.status}] ${error.error.code}: ${error.error.message}`
);
}
return response.json();
}
Never expose your API key in client-side code or commit it to source control. Store it in an environment variable and access it server-side only.
Key types
There are two kinds of API key:
- Organization keys act as the organization. Their scopes decide which endpoints they can call. Create them in Settings → API Keys.
- Personal keys act as one person. That person's role in the organization decides what the key can do, the same as in the app. Scopes are not checked.
Both kinds are sent the same way, as a Bearer token.
Personal API keys
A personal key acts as its owner. Their role in the organization the request names decides what the key can see and change. Some app actions aren't available through the API.
Choosing the organization
A person can belong to several organizations, so a personal key names the one it acts in with the organization query parameter. It takes the organization's slug, its ID, or its Clerk organization ID:
curl "https://app.referralreactor.com/api/v1/referrals?organization=acme-roofing" \
-H "Authorization: Bearer <personal_api_key>"
- If the person is active in exactly one organization, you can leave
organizationout. - If they are in several and you leave it out, the API returns
400and asks you to name one. - If the organization you name doesn't exist, or they aren't a member of it, the API returns
404(Organization not found). - If they are blocked in that organization, or belong to none, the API returns
403.
GET /api/v1/organizations and the /api/v1/organizations/{id}/api-keys endpoints don't take organization. The first lists the person's organizations (leaving out any where they are blocked); the others take the organization from the path.
The organization must be on the Unlimited plan. Otherwise the API returns 403 with API access requires the Unlimited plan. Please upgrade your subscription. GET /api/v1/organizations is the exception: it lists the person's organizations whatever their plan.
What a personal key can see
Access follows the person's role:
| Endpoint | Admins | Business developers | Referrers |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------- | --------- |
| GET /users | Everyone | Their own referrers, plus themselves | 403 |
| GET /bonus-payments | Super admins and people with Can send bonuses: every payment. Other admins: 403 | Their referrers' payments, plus their own | Their own |
| GET /users/{id}/audit-logs | Yes | 403 | 403 |
| /bd/... (BD portal) | Only with bd_user_id, and only a BD whose portal they may read (otherwise 404). Admins marked as BDRs use their own portal | Their own portal | 403 |
Some endpoints need a personal key, because the change is made by a person:
- Changing a user's permissions (
PUT /users/{id}/permissions). - Managing API keys (
/organizations/{id}/api-keys). - The BD portal (
/bd/...): prospects, activities, tasks, goals and the overview. Reads and writes need the Unlimited plan; writes need a business developer (a marketer, or an admin marked as a BDR). - Drafting a bonus payment (
POST /bonus-payments). Super admins and anyone with Can send bonuses (business developers with the flag included). It creates a draft that a person approves in the app; the API never sends money.
Getting a personal key
Create personal keys in the app: open your avatar menu, choose AI assistants, and use Create key under Personal API keys. You see the key once, so copy it then. Your organization needs the Unlimited plan and AI assistants turned on, and you need to be an admin or business developer. You can revoke your keys there while you have access to that page. If you lose access, for example because your role changed, ask an admin or support to revoke the key.
Scopes
Organization keys are issued with one or more scopes that control which endpoints they can access. Personal keys don't use scopes.
| Scope | Description |
| ------------------------ | ------------------------------------------ |
| referrals:read | Read referrals and their details |
| referrals:write | Create and update referrals |
| referrals:delete | Delete referrals. Not granted by default |
| users:read | Read user profiles and lists |
| users:write | Create and update users |
| organizations:read | Read organization details |
| organizations:write | Update organization settings |
| bonus-payments:read | Read bonus payments |
| companies:read | Read companies and their referrers |
| customers:read | Read CRM customer records |
| invitation-codes:read | Read invitation codes and their QR codes |
| invitation-codes:write | Create, update and delete invitation codes |
| public-links:read | Read public referral links |
| public-links:write | Create public referral links |
| reports:read | Read referral stats and reports |
| conversations:read | Read every conversation and message |
| webhooks:read | Read webhook endpoints and deliveries |
| webhooks:write | Create and manage webhook endpoints |
conversations:read is not granted by default. It lets an organization key read every conversation and message in the organization, so grant it only where that is intended.
referrals:delete is not granted by default either. With it, an organization key can permanently delete referrals.
If a request is made with an organization key that lacks the required scope for an endpoint, the API returns HTTP 403. See Error Shapes below.
Rate Limiting
The API enforces rate limits using a sliding window algorithm per API key. A personal key's limit is checked before its organization is chosen, so it applies across every organization the key is used in. When a request is made, the response includes the following headers:
| Header | Description |
| ----------------------- | -------------------------------------------------------- |
| X-RateLimit-Limit | Maximum number of requests allowed in the current window |
| X-RateLimit-Remaining | Number of requests remaining in the current window |
| X-RateLimit-Reset | Unix timestamp (seconds) when the current window resets |
When the limit is exceeded, the API returns HTTP 429 Too Many Requests. Check X-RateLimit-Reset to know when you can retry.
# Example response headers when approaching the limit
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 3
X-RateLimit-Reset: 1735689600
Error Shapes
All errors return a consistent JSON body with an error object containing code, message, and requestId fields. When the API refuses a request on purpose (invalid input, not allowed, not found), message gives the real reason, and the status is 400, 403 or 404.
401 Unauthorized
Returned when no valid API key is provided.
{
"error": {
"code": "MISSING_AUTHORIZATION",
"message": "No Authorization header was provided.",
"requestId": "req_abc123"
}
}
{
"error": {
"code": "INVALID_API_KEY",
"message": "The provided API key is invalid or has been revoked.",
"requestId": "req_abc124"
}
}
| Code | Cause |
| ----------------------- | ------------------------------------------------- |
| MISSING_AUTHORIZATION | Authorization header is absent from the request |
| INVALID_API_KEY | Key is malformed, expired, or revoked |
400 Bad Request
Returned when a parameter is invalid, or when a personal key's owner is in several organizations and the request doesn't name one with ?organization=.
403 Forbidden
Returned when an organization key lacks the required scope for the endpoint, or when a personal key's owner isn't allowed to do what the request asks. The message says why.
{
"error": {
"code": "SCOPE_LIMITATIONS",
"message": "This API key does not have the 'referrals:write' scope required for this endpoint.",
"requestId": "req_abc125"
}
}
404 Not Found
Returned when the resource doesn't exist, or when a personal key's owner isn't a member of the organization named in ?organization=. The API doesn't say which, so other organizations aren't revealed.
429 Too Many Requests
Returned when the rate limit for the API key has been exceeded.
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Please retry after the reset time.",
"requestId": "req_abc126"
}
}
Use the requestId when contacting support — it uniquely identifies the failed request in our logs.
Next Steps
- Getting Started — Make your first API call
- API Reference — Interactive reference for all REST endpoints
- Webhooks — Event types, payload schemas, and signature verification