Docs
Get in touchPartner Portal

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. Alongside error you always get a stable, machine-readable reason (always tier_upgrade_required for this 403) plus the upgrade_required flag:

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

Handling errors

Treat most of the 4xx range as a fixable client problem (bad input, expired token, missing record); retrying without changing the request returns the same error. The exception is 429, which is retriable: back off and retry after the delay in the Retry-After response header (the API sets it on rate-limited responses). 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();
}