API overview
One consistent REST contract over every object in your program, with a generated, interactive reference.
The AffiliateRail API is a REST API at https://api.affiliaterail.com: predictable resource URLs, JSON in and out, and one consistent contract over every object in your program, from partners and links to commissions, payouts and webhook endpoints. Whatever the dashboard shows you, the API can read, and the daily work of running a program (adding partners, recording sales, approving commissions) can be written.
The interactive reference is generated from the OpenAPI document the API itself publishes, so it cannot drift from the running product. The same contract is published for machines at /v1/openapi.json and, as plain text for models, 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 work against the same organization and stamp X-Rail-Livemode: false. |
| 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 beside a currency. Rates are basis points. Never a float, never a string. |
| 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. 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 cannot be probed. |
Clicks and referrals are readable on purpose: a partner who can reconstruct visit, lead and conversion can audit their own tracking. Tax forms expose status only; the form itself is the partner's tax identity and never travels over an API key.
A first call
List your partners, expanding their group:
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
Writes take JSON bodies, honour Idempotency-Key, and return the created object:
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.
- 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