Errors

RFC 7807 problem+json responses, the stable type taxonomy, and the patterns clients should rely on.

CiteFoundry returns RFC 7807 application/problem+json for every non-2xx response. The body has a fixed shape so clients can switch on a stable identifier rather than parsing free-form text:

{
"type": "https://citefoundry.com/problems/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "The provided token has expired. Mint a new one and retry.",
"instance": "/v1/projects/proj_01J.../runs"
}

Fields

FieldPurposeTreat as stable?
typeURI identifying the problem class. Switch on this, not on detail.Yes
titleShort human-readable summary.Stable text, suitable for headings.
statusHTTP status code, mirrored from the response.Yes
detailFree-form description for humans.No — wording may change.
instanceRequest path.Yes — useful for logs.

Some problem types add domain-specific fields (e.g. quotaLimit, retryAfter). Treat unknown fields as additive.

Common problem types

https://citefoundry.com/problems/unauthorized 401 — invalid/expired token
https://citefoundry.com/problems/forbidden 403 — no access to resource
https://citefoundry.com/problems/not-found 404 — resource doesn't exist
https://citefoundry.com/problems/validation 400 — request body failed validation
https://citefoundry.com/problems/plan-quota-exceeded 402 — billing limit reached
https://citefoundry.com/problems/rate-limited 429 — API rate cap hit
https://citefoundry.com/problems/integration-error 502 — upstream (Google, OpenAI) failed
https://citefoundry.com/problems/internal 500 — unexpected server error

The pattern clients should use

Switch on type:

import { ApiError } from "@citefoundry/api-client";
try {
await DefaultService.createMonitor({ projectId, requestBody });
} catch (err) {
if (!(err instanceof ApiError)) throw err;
switch (err.body?.type) {
case "https://citefoundry.com/problems/plan-quota-exceeded":
return showUpgradePrompt(err.body);
case "https://citefoundry.com/problems/rate-limited":
// err.body.retryAfter is seconds
return waitAndRetry(err.body.retryAfter);
case "https://citefoundry.com/problems/validation":
// err.body.errors is the per-field detail
return showValidationErrors(err.body.errors);
default:
throw err;
}
}

Validation errors

validation problems carry an additional errors array, one per invalid field:

{
"type": "https://citefoundry.com/problems/validation",
"title": "Validation failed",
"status": 400,
"detail": "The request body failed validation.",
"errors": [
{ "path": "cadence", "code": "enum", "message": "must be one of: daily, weekly" },
{ "path": "promptId", "code": "required", "message": "is required" }
]
}

Don’t parse detail

detail is for humans. We reserve the right to reword it for clarity or localization. Switch on type, surface title in your UI, and use detail as a fallback message — never as a feature flag.