Signup Attribution - cred.diy API documentation
Signup Attribution
Track which app, landing page, or marketing channel each end user came from — and target promotions per app. Every end user carries three optional attribution fields.
The three fields
signup_url— full URL the user signed up from (e.g.https://app1.example.com/signup?ref=twitter).signup_host— normalized lowercase hostname. Auto-derived by cred fromsignup_urlif omitted; you can override by sending it explicitly.signup_credit_type_id— the credit type the user signed up for (useful for cross-app / multi-product attribution).
Sending the app origin
You have three ways to tell cred which app a call came from, checked in this order:
signup_url(orsignup_host) in the request body — best for server-to-server calls where you know the exact signup page.- An
x-app-origin: https://yourapp.comrequest header — set this once in your HTTP client. The official SDK sends it automatically when you passappOriginto the client. - The browser's
Origin/Refererheader — used automatically for calls made from a browser.
Optional (but strongly recommended) on POST /users, POST /users/bulk, /issue and /deduct — these calls always succeed without it, they just leave attribution blank.
First-touch semantics
Once set on an end user, these fields are never overwritten by later calls. Safe to send on every request — cred latches the first non-null value. Cred has no way to know which of your apps a user came from, so your app must send signup_url and signup_credit_type_id. Blank attribution is backfilled, not frozen: because cred latches the first non-null value, a user created without attribution is filled in by their next call that carries an app origin. Only users who never call again stay blank — backfill those from the dashboard.
Endpoints that accept attribution
Send the three fields as top-level body parameters on:
POST /usersandPOST /users/bulkPOST /issueandPOST /deduct(stamps the user on first-touch if they don't exist yet)POST /hosted-checkout(also stored on the resulting purchase)- Presale checkout
// Create a user with signup attribution
// signup_host is auto-derived from signup_url — no need to send it
const response = await fetch(
'https://eutdgemlrpvnxfkkpvlu.supabase.co/functions/v1/credits-api/users',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'sk_live_your_api_key'
},
body: JSON.stringify({
external_id: 'user_123',
email: 'user@example.com',
signup_url: 'https://app1.example.com/signup?ref=twitter',
signup_credit_type_id: 'ct_abc123'
})
}
);Filter users by attribution
GET /users accepts two additional filter query params: signup_host and signup_credit_type_id.
// Filter users by attribution
// signup_host — normalized hostname (e.g. app1.example.com)
// signup_credit_type_id — the credit type the user signed up for
const res = await fetch(
'https://eutdgemlrpvnxfkkpvlu.supabase.co/functions/v1/credits-api/users?signup_host=app1.example.com&limit=100',
{ headers: { 'x-api-key': 'sk_live_your_api_key' } }
);GET /signup-analytics
User counts, purchase counts, and revenue grouped by signup_host. Pass days=0 for all-time (default: 30).
// GET /signup-analytics?days=30
// User counts, purchase counts, and revenue grouped by signup_host.
// Pass days=0 for all-time (default: 30).
const res = await fetch(
'https://eutdgemlrpvnxfkkpvlu.supabase.co/functions/v1/credits-api/signup-analytics?days=30',
{ headers: { 'x-api-key': 'sk_live_your_api_key' } }
);
// {
// "breakdown": [
// {
// "signup_host": "app1.example.com",
// "signup_credit_type_id": "ct_abc",
// "signup_credit_type_name": "GEMS",
// "user_count": 1250,
// "purchase_count": 87,
// "revenue": 4321.50
// }
// ]
// }Your first credit is one request away.
Create a project, copy an API key, and issue a credit from your server.
