stubkit docs

Error codes

Every non-2xx response has the shape { success: false, error: { code, message } }. The code is stable — safe to branch on in client code. The message is human-readable and may change.

Authentication (401, 403)

CodeHTTPMeaning
missing_header401Authorization header absent.
malformed_key401Key prefix not recognized (expected pk_live_ / sk_live_ / pk_test_ / sk_test_).
unknown_key401No matching key in the database.
revoked401The key was revoked by an admin.
scope_mismatch403This key doesn't have the scope required for the endpoint.
app_mismatch403Key is locked to a different app than the one in the path.
salt_unset500Server misconfiguration — API_KEY_SALT secret not set.

Request validation (400, 401)

CodeHTTPMeaning
invalid_payload400Zod validation failed. The error.message field explains what field.
invalid_query400A query parameter is malformed.
missing_user_id400An endpoint that requires user_id in the query did not receive one.
empty_update400PATCH request had no fields to change.
invalid_code401TOTP or backup code did not match.

Resource (404, 400)

CodeHTTPMeaning
app_not_found404The app does not exist or is owned by a different account.
user_not_found404No user with this id under your account.
experiment_not_found404Experiment id is unknown or paused.
offering_missing404A variant references an offering that was deleted.
no_variants404Experiment has no variants defined.
webhook_not_found404Webhook endpoint id unknown.
offering_not_found404Offering slug unknown for this app.
setup_not_started4002FA verify called before setup.
already_enabled4002FA setup called while it was already enabled.
not_enabled4002FA disable called when not enabled.

Server errors (5xx)

CodeHTTPMeaning
server_sync_failed502The upstream receipt validator (Apple/Google/Stripe) rejected the receipt or timed out.
internal_error500Generic server error. Please retry with exponential backoff.

Retrying

5xx errors and network timeouts should be retried with exponential backoff (1s, 5s, 30s, 2m, 8m, 24m — matching stubkit's own webhook retry schedule). 4xx errors are permanent; fix the request instead of retrying.

Rate limits (429)

429 responses include a Retry-After header in seconds. The X-RateLimit-Reset header on every response tells you the unix timestamp when your budget refills.