Errors
Every userGist API error follows the same envelope. Wire your client to it once and you're done.
Envelope
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request",
"details": []
}
}| Field | Type | Notes |
|---|---|---|
success | false | Distinguishes errors from successful data envelopes. |
error.code | string | Stable, upper snake case; safe to switch on. |
error.message | string | Human-readable, not localised. |
error.details | unknown? | Code-specific structured details. |
Status codes
| HTTP | Code | When |
|---|---|---|
400 | VALIDATION_ERROR / BAD_REQUEST | Invalid body, query, or operation. |
401 | UNAUTHORIZED | Missing, invalid, expired, or revoked credential. |
402 | SUBSCRIPTION_REQUIRED | Workspace or app cannot currently use the product. |
403 | FORBIDDEN | Credential is valid but lacks role, scope, consent, or subject ownership. |
404 | NOT_FOUND | Resource does not exist or is not visible to the caller. |
409 | CONFLICT or operation-specific code | The request conflicts with current state. |
429 | RATE_LIMITED | Honour Retry-After. |
500 | INTERNAL_ERROR | Unexpected server failure. |
502 | UPSTREAM_ERROR or provider-specific code | An upstream service failed. |
Idempotency
Idempotency is explicit on operations where a duplicate would create user-visible
state. Feature-request submissions, request comments, prompt responses, and
transactional push accept an idempotencyKey in their JSON bodies. Reuse the
same value when retrying the same logical operation.
POST /v1/apps/:appId/push/transactional
Content-Type: application/json
{ "campaignId": "…", "externalId": "user_42", "idempotencyKey": "order-1042-shipped" }Retries
Retry 429, 500, 502, and network timeouts only when the operation is
read-only or uses its documented idempotency field.
Use exponential backoff with jitter, capped at 30s. The SDKs do this automatically.