The v2 API surfaces every error as an RFC 7807 ProblemDetails document, served asDocumentation Index
Fetch the complete documentation index at: https://docs.contraforce.com/llms.txt
Use this file to discover all available pages before exploring further.
application/problem+json. Switch on the code extension — it’s part of the contract and won’t move under your feet. The title and detail strings are human-readable and may be reworded between releases.
Response shape
| Field | Source | Description |
|---|---|---|
type | RFC 7807 | URI identifying the problem class. Points at this reference page (https://docs.contraforce.com/api-reference/errors); the code extension is the programmatic discriminator. |
title | RFC 7807 | Short, stable per-code summary. |
status | RFC 7807 | HTTP status code, mirrored in the response status line. |
detail | RFC 7807 | Per-instance message describing what went wrong. |
instance | RFC 7807 | URI of the failing request. |
code | extension | Stable identifier you should switch on. See the catalogue below. |
requestId | extension | Trace identifier for the failed request. Quote this when contacting support. |
timestamp | extension | UTC ISO-8601 timestamp the failure was generated at. |
target | extension, optional | Field or identifier the error refers to. |
errors | extension, optional | Field-keyed validation error map. Empty key means an object-level error. |
Validation responses
When binding fails or a FluentValidation rule rejects, the response includes anerrors map:
Error code catalogue
- 4xx — caller errors
- 5xx — service errors
| Code | Status | When you see it | What to do |
|---|---|---|---|
VALIDATION_ERROR | 400 | A required field is missing, malformed, or rejected by validation. The errors extension carries the per-field detail. | Inspect the errors map. Empty keys map to object-level errors; named keys map to JSON paths. |
BAD_REQUEST | 400 | A 400 not classified as a validation failure (e.g. a malformed body the framework couldn’t parse at all). | Inspect detail. Confirm the body matches the documented shape. |
UNAUTHORIZED | 401 | Missing or invalid Authorization header, revoked or rotated clientSecret, or a disabled service account. | Re-authenticate with valid credentials. If you rotated, update both halves of the credential. |
FORBIDDEN | 403 | Authenticated, but the calling principal can’t perform this action on this resource. | Check that the credential carries the scope the endpoint requires and that the service account has the right workspace role. |
INSUFFICIENT_SCOPE | 403 | The credential is missing one of the scopes required by the endpoint. | Add the scope to the credential in the portal. Scopes follow the {resource}:{action} convention. |
INSUFFICIENT_WORKSPACE_ROLE | 403 | The calling principal has no role on the target workspace. | Have an organization admin assign a workspace role to the service account. |
MFA_REQUIRED | 403 | The user-flow path requires MFA from the configured IdP. | Complete the MFA challenge in the portal. Service accounts do not encounter this code. |
USER_NOT_REGISTERED | 403 | The authenticated user is not registered in the ContraForce user store. | An organization admin must sync or invite the user before they can call the API. |
EXTENSION_NOT_ENABLED | 400 | The endpoint requires an integration (e.g. Defender XDR, Jira) that hasn’t been consented and enabled for this workspace. | Enable the integration via the workspace configuration endpoints or in the portal. |
NOT_FOUND | 404 | A workspace, incident, gamebook, or other addressed resource doesn’t exist or isn’t visible to the credential. | Verify the IDs in the path. For workspace-scoped routes, confirm the workspace is mapped to the service account. |
METHOD_NOT_ALLOWED | 405 | Wrong HTTP verb on the route. | Check the endpoint reference for the correct verb. |
CONFLICT | 409 | The resource version on the server differs from yours, the same operation was already performed, or two concurrent writes collided. | Refetch the resource and retry with the latest state. |
UNSUPPORTED_MEDIA_TYPE | 415 | Wrong Content-Type header for the body shape. | Use application/json for JSON requests, multipart/form-data for SOP uploads. |
RATE_LIMITED | 429 | Too many requests in the rolling window. | Honor Retry-After if present, otherwise back off exponentially. |
Mapping behavior
A few specific cases are worth calling out because they affect how you should design retries and surface failures to your own users.Upstream 4xx is translated to a caller-facing 4xx
Upstream 4xx is translated to a caller-facing 4xx
When a downstream SIEM rejects a request with a 4xx (404 for an unknown ID, 400 for an unparseable filter, 409 for a conflict, 429 for rate-limit, 408/504 for timeout), the v2 API surfaces that as the matching 4xx —
NOT_FOUND, VALIDATION_ERROR, CONFLICT, RATE_LIMITED, TIMEOUT — rather than collapsing onto an opaque 502. That way your retry logic can act on what’s actually wrong.Only opaque or 5xx upstream failures stay as UPSTREAM_ERROR. Upstream 401/403 also stay opaque (they mean our credential to the upstream is broken; that’s not something you can fix).Client-aborted requests do not produce a body
Client-aborted requests do not produce a body
When the caller disconnects mid-request, the response is an empty
499 and no ProblemDetails body is written. Treat 499 as “request was cancelled before we could respond” — your client probably already knows.Use requestId, not traceId
Use requestId, not traceId
Some development-mode payloads previously carried a
traceId extension from ASP.NET. The v2 contract is requestId only. Quote the requestId value when contacting support — we can correlate it to backend logs without you needing to share anything else.Correlating with our logs
EveryrequestId is also recorded in our backend logs. Including it in support requests dramatically shortens triage:
Hello, I’m seeing intermittent 502We can pull the full backend trace and pinpoint the failure without needing the request body or your credentials.UPSTREAM_ERRORonPOST /api/v2/incidents/across-workspaces. ExamplerequestId:0HNLBAGCRD4RN:00000003at2026-04-15T20:00:00Z.