Skip to main content

Errors

All errors follow this shape:

{
"error": "Human-readable description of what went wrong"
}

Status codes

StatusMeaning
400Bad request — invalid body, missing required field, or failed validation
401Unauthorized — missing or invalid token
403Forbidden — authenticated, but not allowed to perform this action
404Not found
409Conflict — e.g. duplicate slug or email
429Too many requests — rate limit exceeded
500Internal server error

Tier-upgrade 403

When a user tries to fetch a premium deal they can't see, you get a richer envelope so your UI can show an upgrade prompt:

{
"error": "This deal requires a premium subscription",
"upgrade_required": true
}

Handling errors

Treat anything in the 4xx range as a fixable client problem (bad input, expired token, missing record). Treat 5xx as transient — retry with exponential backoff up to ~3 attempts.

async function callApi(path, options) {
const res = await fetch(`https://perks.founderpass.com${path}`, options);
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new Error(body.error || `HTTP ${res.status}`);
}
return res.json();
}

:::warning Don't retry 4xx A 4xx response means the request was understood and rejected. Retrying the same request will return the same error. Fix the input first. :::