{"envelope":{"error":"string, human-readable message; always present.","code":"string, a stable machine-readable code from this catalog.","requestId":"string, correlation id, also returned as the X-Request-Id response header. Send it to support and we can find the exact request.","details":"array|object, optional, code-specific context (e.g. per-field validation problems)."},"retryGuidance":"Retry ONLY codes with `retryable: true`, and honour the `Retry-After` header (seconds) when present. Retrying a non-retryable code will fail identically and consumes quota. Non-idempotent writes should carry an `Idempotency-Key` so a retry can never double-report.","tracing":"Every response carries `X-Request-Id`. Supply your own on the request and we echo it, so your logs and ours share one id.","codes":[{"code":"ACTIVATION_NO_VALID_ROWS","httpStatus":400,"title":"No confirmation request could be created","description":"POST /activation/campaigns: every submitted row either failed its own check (e.g. counterpartyRef equals the subject → CONFIRMATION_SELF) or was an in-batch duplicate of an earlier row. The campaign is not persisted; an empty campaign is never created. The `details` object carries the per-row `results` and dropped `duplicates`.","whatToDo":"Walk `details.results` for each row’s reason, fix the offending rows, and resubmit. A row cannot name the subject as its own counterparty.","retryable":false},{"code":"ACTIVITY_ACTION_INVALID","httpStatus":400,"title":"Unknown activity action filter","description":"GET /activity: `action` is not one of the platform-visible audit actions.","whatToDo":"Use one of the actions listed in the error message (they are also enumerated in the OpenAPI document).","retryable":false},{"code":"ACTIVITY_FORMAT_INVALID","httpStatus":400,"title":"Unsupported activity format","description":"GET /activity: `format` must be `json` (default) or `csv`. CSV is the SIEM/audit-export shape; paginate it with the `X-Next-Cursor` response header.","whatToDo":"Pass a supported `format`, or omit it for JSON.","retryable":false},{"code":"ACTIVITY_RANGE_INVALID","httpStatus":400,"title":"Invalid activity date range","description":"GET /activity: `from`/`to` is not a valid ISO timestamp or date, or `from` is after `to`.","whatToDo":"Send ISO-8601 values with `from` on or before `to`.","retryable":false},{"code":"ANALYTICS_RANGE_INVALID","httpStatus":400,"title":"Invalid analytics date range","description":"GET /analytics/events and /analytics/scores: `from`/`to` is missing its pair, is not a valid ISO date (YYYY-MM-DD), or `from` is after `to`.","whatToDo":"Send both bounds as `YYYY-MM-DD`, with `from` on or before `to`.","retryable":false},{"code":"ANALYTICS_WINDOW_CONFLICT","httpStatus":400,"title":"Conflicting analytics window","description":"GET /analytics/events and /analytics/scores: `days` and `from`/`to` are mutually exclusive.","whatToDo":"Send either a trailing `days` window or an explicit `from`/`to` range, not both.","retryable":false},{"code":"BAD_REQUEST","httpStatus":400,"title":"Bad request","description":"The request was malformed or failed a check that has no more specific code.","whatToDo":"Read the `error` message (it names the offending field or parameter) and fix the request.","retryable":false},{"code":"BOOK_FORMAT_INVALID","httpStatus":400,"title":"Unsupported book format","description":"GET /users: `format` must be `json` (default) or `csv`.","whatToDo":"Pass a supported `format`, or omit it for JSON.","retryable":false},{"code":"BOOK_QUERY_INVALID","httpStatus":400,"title":"Invalid book query parameter","description":"GET /users: a filter, sort, order, limit or cursor parameter is out of range or not one of the allowed values. The filter set is closed; there is no free-form query.","whatToDo":"The message names the offending parameter and its allowed values. Correct it; retrying the same query fails identically.","retryable":false},{"code":"BUSINESS_IDENTITY_NOT_DECLARED","httpStatus":400,"title":"Business identity not declared","description":"POST /businesses: a business subject must declare at least one identity. Without a name or domain, an outcome that names the business as its own counterparty cannot be detected as self-dealing.","whatToDo":"Provide at least one of `business.legalName`, `business.tradingName`, `business.homepage` or `business.did`.","retryable":false},{"code":"CAREER_FORMAT_INVALID","httpStatus":400,"title":"Unsupported career-export format","description":"GET /users/{id}/career-export and GET /verify/{token}/career-export: `format` must be `jsonresume` (JSON Resume, the default) or `jsonld` (a schema.org Person graph).","whatToDo":"Pass `format=jsonresume` or `format=jsonld`, or omit it for the default.","retryable":false},{"code":"CONFIRMATION_NOT_PENDING","httpStatus":409,"title":"Confirmation is no longer pending","description":"The confirmation request has already been confirmed, declined, cancelled, or has expired, so it cannot be actioned again. The token is single-use.","whatToDo":"Read the request state (GET /confirmations/{id}). Create a fresh request if the outcome still needs confirming.","retryable":false},{"code":"CONFIRMATION_SELF","httpStatus":400,"title":"Counterparty is the subject","description":"POST /confirmations: `counterpartyRef` names the subject itself (case/whitespace-insensitive). A subject can never be its own independent witness.","whatToDo":"Name the DISTINCT counterparty who will confirm the outcome, a different party than the subject of the record.","retryable":false},{"code":"CONFIRMATION_TOKEN_INVALID","httpStatus":401,"title":"Invalid or missing confirmation token","description":"The confirmation preview/respond endpoint requires the one-time `token` returned when the request was created, and it did not match.","whatToDo":"Use the exact `confirmationToken` from the create response. It is shown only once; if it was lost, cancel and create a new request.","retryable":false},{"code":"CONFLICT","httpStatus":409,"title":"State conflict","description":"The resource is not in a state where this operation is valid, e.g. a dispute that is already resolved.","whatToDo":"Re-read the resource and branch on its current state instead of retrying.","retryable":false},{"code":"DISPATCH_CONTEXT_INVALID","httpStatus":400,"title":"Unknown reliability context","description":"GET /users/{id}/reliability: the `context` query parameter is not one this API serves.","whatToDo":"Omit `context` (it defaults to `dispatch`) or send a supported value; `dispatch` is the only one today.","retryable":false},{"code":"EXPORT_FORMAT_INVALID","httpStatus":400,"title":"Unsupported export format","description":"GET /events/export: `format` must be `json` (default) or `csv`.","whatToDo":"Pass a supported `format`, or omit it for JSON.","retryable":false},{"code":"EXPORT_RANGE_INVALID","httpStatus":400,"title":"Invalid export date range","description":"GET /events/export: `from`/`to` is not a valid ISO timestamp or date, or `from` is after `to`.","whatToDo":"Send ISO-8601 values (e.g. `2026-07-01` or `2026-07-01T00:00:00Z`) with `from` on or before `to`.","retryable":false},{"code":"FORBIDDEN","httpStatus":403,"title":"Not permitted","description":"The key is valid but is not allowed to perform this operation on this resource.","whatToDo":"Check that the resource belongs to your platform, and that the key has the right scopes.","retryable":false},{"code":"IDEMPOTENCY_KEY_REUSED","httpStatus":422,"title":"Idempotency-Key reused with a different payload","description":"The same `Idempotency-Key` was already used for a request whose body differed. Replaying it would be ambiguous, so it is refused rather than guessed at.","whatToDo":"Use a fresh key for a genuinely new request, or resend the byte-identical body to get the original response back.","retryable":false},{"code":"IMPORT_CSV_INVALID","httpStatus":400,"title":"CSV could not be parsed","description":"POST /imports: an unbalanced quote, a blank or duplicate header, or a file with a header but no data rows.","whatToDo":"Fix the file and re-upload. The message names the specific parse problem.","retryable":false},{"code":"IMPORT_QUEUE_UNAVAILABLE","httpStatus":503,"title":"Import queue unavailable","description":"POST /imports: the async queue is unreachable and the file is too large to run inline, so it was refused rather than accepted and silently dropped.","whatToDo":"Retry later with backoff, or split the file small enough to process inline.","retryable":true},{"code":"IMPORT_TOO_MANY_ROWS","httpStatus":400,"title":"Import exceeds the row limit","description":"POST /imports: the file is larger than IMPORT_MAX_ROWS. Refused outright; an import is never silently truncated.","whatToDo":"Split the file. Idempotency keys make overlapping re-uploads safe, so the split does not have to be exact.","retryable":false},{"code":"INGEST_MAPPING_INVALID","httpStatus":400,"title":"Invalid field mapping","description":"POST /ingest, /ingest/mappings or /imports: the mapping names an unknown Credda field or an unknown transform, or a rule reads nothing.","whatToDo":"Check the field and transform names against the mapping vocabulary in the OpenAPI document; the message lists every problem found.","retryable":false},{"code":"INTERNAL_ERROR","httpStatus":500,"title":"Unexpected server error","description":"Something failed on our side. The failure is logged against the `requestId` in the response.","whatToDo":"Retry with backoff. If it persists, send us the `requestId`; it is the fastest path to a diagnosis.","retryable":true},{"code":"MONITOR_LIMIT_REACHED","httpStatus":409,"title":"Active monitor limit reached","description":"Your plan's cap on simultaneously active score monitors is reached. `details` carries `{limit, tier}`.","whatToDo":"Delete or deactivate a monitor, or upgrade for a higher cap (see GET /api/v1/plans).","retryable":false},{"code":"NOTIFICATION_CHANNEL_INVALID","httpStatus":400,"title":"Invalid notification channel","description":"POST /notification-channels: `type` must be `slack` or `email`, and `target` must match it: a Slack target must be an https `hooks.slack.com` incoming-webhook URL, an email target a valid address. The message names the specific problem.","whatToDo":"Fix the `type`/`target` pair. For Slack, use the full https://hooks.slack.com/services/… URL from your Slack app.","retryable":false},{"code":"NOTIFICATION_CHANNEL_LIMIT_REACHED","httpStatus":409,"title":"Notification channel limit reached","description":"The per-platform cap on notification channels is reached (`details` carries `{limit}`). Channels are budgeted separately per mode (test vs live).","whatToDo":"Delete a channel you no longer use, or raise NOTIFICATION_CHANNEL_MAX_PER_PLATFORM if you operate the deployment.","retryable":false},{"code":"NOT_FOUND","httpStatus":404,"title":"Resource does not exist","description":"No resource matched the id or path. For users, this also means no events have been reported yet; see USER_NOT_FOUND, which additionally covers the brief asynchronous window after a first write.","whatToDo":"Verify the id, and verify the mode: a `crd_test_` key only ever sees test data and a `crd_live_` key only live data, so the same id can 404 under one and resolve under the other. A user exists only once at least one event has landed for them.","retryable":false},{"code":"OPERATOR_NOT_DECLARED","httpStatus":400,"title":"Third-party agent operator not named","description":"POST /agents: registering an agent you do not operate requires naming the operator, otherwise it cannot be checked for self-dealing.","whatToDo":"Provide `operator.name`, `operator.homepage` or `operator.did`, or drop the flag if the calling platform IS the operator.","retryable":false},{"code":"PAYLOAD_NOT_RETAINED","httpStatus":409,"title":"Delivery payload not retained","description":"This webhook delivery predates payload retention, so there is no stored body to replay.","whatToDo":"Replay a newer delivery, or re-derive the state from the API directly.","retryable":false},{"code":"PLAN_REQUIRED","httpStatus":402,"title":"Capability not included in your plan","description":"The key authenticated and carries the right scope, but this capability is reserved for a higher tier (e.g. async bulk screening or agent subjects are Enterprise). `details` carries `{feature, currentTier, requiredPlan}`. Sandbox (test-mode) keys are never gated; try the capability with a `crd_test_` key first.","whatToDo":"Upgrade to the tier named in `requiredPlan` (see GET /api/v1/plans), or evaluate the capability in test mode where every tier is unlocked.","retryable":false},{"code":"POLICY_LIMIT_REACHED","httpStatus":409,"title":"Active threshold-policy limit reached","description":"Your plan's cap on simultaneously active threshold policies is reached. `details` carries `{limit, tier, mode}`. Policies are budgeted separately from monitors.","whatToDo":"Delete or deactivate a policy, or upgrade for a higher cap (see GET /api/v1/plans).","retryable":false},{"code":"QUOTA_EXCEEDED","httpStatus":429,"title":"Monthly quota exhausted","description":"This platform's monthly request quota is spent. Test-mode requests never count toward it.","whatToDo":"Wait until the period resets (`Retry-After` / `X-Quota-Reset`), or upgrade the plan. Subscribe to `usage.quota_warning` to be told before this happens.","retryable":true},{"code":"RATE_LIMIT_EXCEEDED","httpStatus":429,"title":"Per-minute rate limit exceeded","description":"Too many requests in the current minute window for this platform (or IP, for unauthenticated traffic). Limits are tier-scaled; see GET /api/v1/plans.","whatToDo":"Back off for the number of seconds in `Retry-After`, then retry. `RateLimit-*` headers show your live budget.","retryable":true},{"code":"REFERENCE_NOT_PENDING","httpStatus":409,"title":"Reference is no longer pending","description":"The reference request has already been confirmed, declined, cancelled, or has expired, so it cannot be actioned again. The token is single-use.","whatToDo":"Read the request state (GET /references/{id}). Create a fresh request if the claim still needs confirming.","retryable":false},{"code":"REFERENCE_SELF","httpStatus":400,"title":"Counterparty is the subject","description":"POST /references: `counterpartyRef` names the subject itself (case/whitespace-insensitive). A person can never be their own reference.","whatToDo":"Name the DISTINCT third party who will confirm the claim: a past employer, manager, client, colleague, or issuing body, never the subject.","retryable":false},{"code":"REFERENCE_TOKEN_INVALID","httpStatus":401,"title":"Invalid or missing reference token","description":"The reference preview/respond endpoint requires the one-time `token` returned when the request was created, and it did not match.","whatToDo":"Use the exact `referenceToken` from the create response. It is shown only once; if it was lost, cancel and create a new request.","retryable":false},{"code":"RESERVED_EXTERNAL_ID","httpStatus":400,"title":"externalId uses the reserved test namespace","description":"Live ingestion refuses ids beginning with `test:`: that prefix is reserved for the per-platform sandbox namespace.","whatToDo":"Use a different id for live data, or send the event with a `crd_test_` key to write it into your test universe.","retryable":false},{"code":"SCOPE_CEILING_EXCEEDED","httpStatus":403,"title":"Requested key scopes exceed the minting key","description":"POST /platform/keys: a key can only mint another key scoped AT OR BELOW its own capabilities; otherwise a narrow key could bootstrap itself to full access. `details.exceeded` lists the `<resource>:<action>` capabilities requested that the calling key does not itself hold.","whatToDo":"Request a subset of your own key’s scopes, or use a broader key to mint the broader child.","retryable":false},{"code":"SCOPE_INSUFFICIENT","httpStatus":403,"title":"API key lacks the required scope","description":"The key authenticated, but does not carry the `<resource>:<action>` scope this route requires (e.g. `events:write`). Scopes are set per key and default to your plan's.","whatToDo":"Issue a key with the needed scope from your console, or upgrade the plan if the scope is not included in your tier.","retryable":false},{"code":"SCREENING_NOT_COMPLETED","httpStatus":409,"title":"Screening is not finished","description":"Results were requested for a job that is still QUEUED/RUNNING, or that FAILED.","whatToDo":"Poll GET /screenings/{id} until `status` is COMPLETED, then fetch the results. A FAILED job must be resubmitted.","retryable":false},{"code":"SCREENING_QUEUE_UNAVAILABLE","httpStatus":503,"title":"Screening queue unavailable","description":"The async queue is unreachable and the job is too large to run inline, so it was refused rather than accepted and silently lost.","whatToDo":"Retry later with backoff, or split the roster into smaller batches that run inline.","retryable":true},{"code":"STREAM_LIMIT_REACHED","httpStatus":429,"title":"Too many simultaneous event streams","description":"GET /api/v1/events/stream holds one long-lived connection (and one dedicated Redis subscriber) per open stream, so the number a platform may hold at once is capped. `details` carries `{limit}`.","whatToDo":"Close a stream you already hold and retry, or consolidate: one stream with no `types` filter carries every event type, and webhooks fan the same envelopes out to any number of receivers.","retryable":true},{"code":"TEST_MODE_NOT_ALLOWED","httpStatus":403,"title":"Not available in test mode","description":"Share tokens, credentials, verify pages and trust exports refuse test users; test data can never become portable trust.","whatToDo":"Use a live (`crd_live_…`) key with real, verified activity for any trust surface.","retryable":false},{"code":"TEST_MODE_ONLY","httpStatus":403,"title":"Requires a test-mode key","description":"DELETE /test/data only accepts a `crd_test_` key. Live data is append-only and cannot be deleted.","whatToDo":"Send the request with a test key. There is no equivalent operation for live data, by design.","retryable":false},{"code":"UNAUTHORIZED","httpStatus":401,"title":"Missing or invalid API key","description":"No `Authorization: Bearer` header, or the key is unknown, revoked, inactive or expired.","whatToDo":"Send a live (`crd_live_…`) or test (`crd_test_…`) key from your console. Rotate it if it may have leaked.","retryable":false},{"code":"USAGE_FORMAT_INVALID","httpStatus":400,"title":"Unsupported usage format","description":"GET /usage: `format` must be `json` (default) or `csv`.","whatToDo":"Pass a supported `format`, or omit it for JSON.","retryable":false},{"code":"USAGE_RANGE_INVALID","httpStatus":400,"title":"Invalid usage date range","description":"GET /usage: `from`/`to` is missing its pair, is not a valid ISO date, or `from` is after `to`.","whatToDo":"Send both bounds as `YYYY-MM-DD`, with `from` on or before `to`.","retryable":false},{"code":"USAGE_WINDOW_CONFLICT","httpStatus":400,"title":"Conflicting usage window","description":"GET /usage: `days` and `from`/`to` are mutually exclusive.","whatToDo":"Send either a trailing `days` window or an explicit `from`/`to` range, not both.","retryable":false},{"code":"USER_NOT_FOUND","httpStatus":404,"title":"Unknown user","description":"No events have been reported for this externalId, so the user does not exist yet in your ledger. This is also what you see in the short window after a first write: scoring is recomputed ASYNCHRONOUSLY, so a read taken immediately after the event that created the subject can still miss it.","whatToDo":"A subject is created implicitly by its first event. Either report one yourself (POST /events), or (for evidence that carries weight) propose one for a counterparty to confirm (POST /confirmations); the event lands verified when they respond. If you just wrote an event, poll for a second or two before treating this as a real 404. Check the mode too: a test key never sees live subjects, and vice versa.","retryable":false},{"code":"VALIDATION_ERROR","httpStatus":400,"title":"Request body failed validation","description":"The JSON body did not match the endpoint schema. The response carries a `details` array with one entry per problem: `{path, message, code, expected?, received?}`.","whatToDo":"Walk `details` and correct each `path`. Retrying the same body will fail identically.","retryable":false}]}