API overview
A REST API over every object in your program, with an interactive reference generated from the API itself.
The AffiliateRail API is a REST API at https://api.affiliaterail.com. You send JSON and get JSON back, and the resource URLs are predictable. It covers every object in your program, from partners and links to commissions, payouts and webhook endpoints.
Anything the dashboard shows you, the API can read. It can also do the daily work of running a program: adding partners, recording sales and approving commissions.
An OpenAPI document is a file that describes an API in a format tools can read. The API publishes its own, and the interactive reference is generated from it. So the reference can't drift from the running product. Machines can read the same document at /v1/openapi.json. Models can read it as plain text at /llms.txt and /llms-full.txt.
The contract in one screen
| Concern | Rule |
|---|---|
| Auth | Authorization: Bearer rail_live_…. Keys are minted under Settings, API keys and carry scopes. rail_test_… keys stamp X-Rail-Livemode: false but read and write the same data as a live key; see authentication. |
| Lists | { "data": [...], "has_more": true, "total_count": 42 }, flat, every time. limit 1 to 100 (default 25); starting_after and ending_before take an object id. program_id is required on every list except /v1/programs. |
| Expand | expand[]=partner&expand[]=sale (or expand=partner,sale) inlines related objects. The same relation name means the same object everywhere; an unknown value is a 400 naming the allowed ones. include[]=stats adds counters where they exist. |
| Money | Integer minor units in *_minor fields, always with a currency in hand. Rates and deltas are basis points. Never a float, never a string; a test walks every endpoint to keep it that way. |
| Time | ISO 8601, UTC. |
| Ids | Prefixed: prg_, part_, cus_, sale_, com_, pyt_, flw_, whe_. Sample rows are sample_…. |
| Idempotency | An Idempotency-Key header on any POST replays the first response for 24 hours; the same key with a different request is a 422, and the same key while the first request is still running is a 409. See rate limits and errors. |
| Rate limit | 25 requests per second per key, with X-RateLimit-* headers on every response. |
| Errors | { "error": { "type", "code", "message", "param" } }. See the error catalogue. |
| Versioning | Every response carries X-Rail-Version. Additive changes never bump it. |
| Tenancy | An id from another organization is a 404, never a 403, so ids can't be probed. |
Clicks and referrals are readable on purpose. A partner who can rebuild the path from visit to lead to sale can audit their own tracking. Tax forms show their status only. The form itself is the partner's tax identity, so it never travels over an API key.
A first call
List your partners, with each one's group inlined by expand:
curl "https://api.affiliaterail.com/v1/partners?program_id=prg_...&limit=5&expand[]=group" \
-H "Authorization: Bearer rail_live_..."const res = await fetch(
"https://api.affiliaterail.com/v1/partners?program_id=prg_...&limit=5&expand[]=group",
{ headers: { Authorization: `Bearer ${process.env.RAIL_API_KEY}` } },
);
const { data, has_more, total_count } = await res.json();import os, requests
res = requests.get(
"https://api.affiliaterail.com/v1/partners",
params={"program_id": "prg_...", "limit": 5, "expand[]": "group"},
headers={"Authorization": f"Bearer {os.environ['RAIL_API_KEY']}"},
)
body = res.json() # {"data": [...], "has_more": bool, "total_count": int}Creating a partner
To write, send a JSON body. Add an Idempotency-Key header so a retry can't create the partner twice. You get the created object back:
curl https://api.affiliaterail.com/v1/partners \
-H "Authorization: Bearer rail_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: signup-8412" \
-d '{"program_id": "prg_...", "email": "alice@example.com", "status": "active"}'const res = await fetch("https://api.affiliaterail.com/v1/partners", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RAIL_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "signup-8412",
},
body: JSON.stringify({ program_id: "prg_...", email: "alice@example.com", status: "active" }),
});
const partner = await res.json(); // { id: "part_...", ... }res = requests.post(
"https://api.affiliaterail.com/v1/partners",
headers={
"Authorization": f"Bearer {os.environ['RAIL_API_KEY']}",
"Idempotency-Key": "signup-8412",
},
json={"program_id": "prg_...", "email": "alice@example.com", "status": "active"},
)
partner = res.json() # {"id": "part_...", ...}Going deeper
- The interactive reference: every endpoint, parameter and schema, with a try-it console.
- Money, reports and write ergonomics: the rules every amount follows, and the answers writes give you.
- Rate limits, errors and versioning
- Webhooks: signed delivery for every event in your program.
- Server-side attribution: three lines that attribute a sale without Stripe Checkout.
- Typed clients and LLM access