Referrals API
All endpoints require an authenticated session cookie. Use Authentication to log in first.
GET /api/referrals/me#
Returns the current user's referral code, share link, partner status, stats, and the last 20 conversions. Lazy-creates the code on first call.
Response:
{
"data": {
"code": "ABCD1234",
"link": "https://taxmtd.uk/r/ABCD1234",
"signup_url": "https://taxmtd.uk/signup?ref=ABCD1234",
"tier": "standard",
"partner_slug": null,
"partner_url": null,
"og_image_url": "https://taxmtd.uk/api/referrals/og/ABCD1234.png",
"friend_discount": "20% off for 3 months",
"commission_pct": 20,
"stats": {
"click_count": 142,
"signup_count": 8,
"paid_count": 3,
"lifetime_pence": 1620,
"balance_pence": 1620
},
"conversions": [
{
"id": "...",
"friend_name": "Jane S.",
"gross": 27.0,
"commission": 5.4,
"status": "cleared",
"tier": "standard",
"date": "2026-04-29T08:21:00.000Z",
"cleared_at": "2026-04-29T08:21:01.000Z"
}
]
}
}POST /api/referrals/regenerate#
Set a custom vanity code. Body is optional — when omitted, generates a random 8-char code.
{ "code": "jane-smith" }Validation:
- 4–12 chars, lowercase a–z and 0–9 (hyphens not yet supported in vanity codes)
- Rate-limited 3 changes per user per day
- 409 on collision with another user's code
GET /api/referrals/og/{code}.png#
Personalised Open Graph share card (1200×630 PNG). Public, cached 24h. Use as og:image on share targets, or download from the dashboard.
GET /api/partner/{slug}#
Public partner-page payload. Returns first name, bio, avatar URL, and the referral code that powers the signup CTA. Sets the 60-day attribution cookie + records a click as a side effect.
POST /api/admin/referrals/promote-partner *(admin only)*#
{
"user_id": "...",
"partner_slug": "jane-smith",
"partner_bio": "Markdown bio here",
"partner_avatar": "uuid-of-uploaded-image"
}Set demote: true to revert to standard tier (preserves the slug for re-promotion).
Webhook events#
Referral commission is credited inside the existing Stripe webhook at /api/stripe/webhook — no separate endpoint to wire up. Relevant cases:
invoice.paid(billing_reasonsubscription_create) — first paid invoice for a referred user. Credits the referrer's Stripe customer balance, inserts areferral_conversionsrow withstatus: cleared.charge.refunded— looks up the matching invoice, flips the conversion tovoided, posts a reversing balance transaction.
Idempotency is enforced via the unique stripe_invoice_id column on referral_conversions. Stripe webhook retries are no-ops.
Short links#
https://taxmtd.uk/r/{code} is a 302 redirect to /signup?ref={code} that sets the cookie + records the click. Use it on print, SMS, QR codes, or anywhere else the long URL is awkward.