All errors share a consistent JSON envelope:
"code" : " INVALID_API_KEY " ,
"message" : " API key is invalid or no longer recognized. " ,
"request_id" : " req_01J... "
request_id is also echoed in the response headers — please quote it in support tickets.
Code HTTP Meaning MISSING_AUTH401 No Authorization header sent. INVALID_API_KEY401 Key not found. REVOKED_API_KEY401 Key was revoked. WRONG_AUTH_TYPE401 Sent an API key to a dashboard endpoint, or a Clerk JWT to a product endpoint. INVALID_CLERK_TOKEN401 JWT failed signature, expiry, or issuer checks. ACCOUNT_NOT_FOUND404 Clerk user has no Nordva Launch account. Call POST /v1/auth/provision.
Code HTTP Meaning VALIDATION_ERROR400 Body or query fails schema. param names the offending field. INVALID_PLAN400 Plan name not recognised (must be indie or builder). RATE_LIMITED429 Plan rate limit exceeded. Retry-After header included. PLAN_LIMIT_REACHED403 Monthly quota exhausted. Upgrade or wait for the cycle to reset. PLAN_UPGRADE_REQUIRED403 Feature gated to a higher plan. NOT_FOUND404 Resource missing or owned by another project. CANNOT_EDIT_PUBLISHED409 Tried to edit a published changelog entry. HOSTNAME_ALREADY_REGISTERED409 Custom hostname already registered to another project. IDEMPOTENCY_CONFLICT409 Same Idempotency-Key used with a different request body.
Code HTTP Meaning BILLING_NOT_CONFIGURED503 Server-side Paddle environment is not configured. NO_BILLING_ACCOUNT404 Account has no Paddle customer yet — must complete a checkout first. BILLING_PORTAL_UNAVAILABLE502 Paddle portal-session API call failed. Retry shortly.
Code HTTP Meaning CUSTOM_DOMAIN_NOT_CONFIGURED503 Server-side Cloudflare for SaaS env not configured. CUSTOM_DOMAIN_LIMIT_REACHED403 Reached the per-plan custom hostname limit.
Code HTTP Meaning KEY_INSUFFICIENT_PERMISSIONS403 A publishable key (nv_pub_*) was used on a secret-only endpoint. Publishable keys are browser-safe and may only write to the public endpoints listed in Authentication — list, read, delete, and admin operations require a secret key (nv_live_* / nv_test_*). API_KEY_LIMIT_REACHED422 Project already has the per-plan maximum of active keys of the requested type. Secret and publishable keys are counted independently. Revoke an unused key of the same type, or upgrade the plan.
Code Status Meaning INVALID_SLUG422 Slug is not 1–40 lowercase letters, numbers and hyphens. SLUG_TAKEN409 The slug is in use, reserved, or was released by another project in the last 30 days. SLUG_CHANGE_LIMIT_REACHED429 The slug was already changed twice in the last 30 days. remediation gives the date it can change again. Old links keep working in the meantime.
Code Status Meaning STRIPE_NOT_CONNECTED409 No Stripe account is connected to this project. Connect one in the dashboard. STRIPE_ALREADY_CONNECTED409 Disconnect the current key before connecting another. STRIPE_KEY_NOT_RESTRICTED422 The key is not a restricted key (rk_live_ / rk_test_). If a secret key was pasted, roll it in Stripe. STRIPE_KEY_INVALID422 Stripe did not recognise the key. STRIPE_KEY_MISSING_PERMISSION422 A required read permission is missing. remediation carries Stripe’s message naming it. STRIPE_CONNECTION_LIMIT_REACHED403 Indie includes one Stripe connection per account; Builder one per project. STRIPE_CONNECT_NOT_CONFIGURED503 Server-side encryption key for Stripe connections is not configured. CONTACT_NOT_FOUND404 No contact with that id in this project. EMAIL_TEMPLATE_INVALID422 Unknown placeholder, missing required placeholder, or empty/too long subject or body. param is subject or body.
Code HTTP Meaning INTERNAL_ERROR500 Unexpected server error. Include the request_id in your report. TURNSTILE_FAILED400 Cloudflare Turnstile token verification failed.
If you hit a code not listed here, please open an issue — we keep the canonical list at packages/api/src/lib/errors.ts .