Error response schema

Every error response from the CaseForge API follows the same JSON shape:

JSON
{
  "error": {
    "code":    "case_not_found",        // Machine-readable error code
    "message": "No case with ID 'case_abc123' was found in this workspace.",
    "param":   "case_id",              // Which parameter caused the error (if applicable)
    "doc_url": "https://docs.caseforge.io/errors/case_not_found"
  },
  "request_id": "req_7x2k4m9z"          // Include this when contacting support
}

SDK exception hierarchy

All SDK exceptions extend CaseForgeError. Catch the base class to handle all API errors, or catch a specific subclass to handle a particular failure mode:

Python — exception hierarchy
CaseForgeError                    # Base class — all SDK errors
├── APIError                      # HTTP 4xx/5xx from the API
│   ├── AuthenticationError       # 401 — invalid or expired credentials
│   ├── PermissionError           # 403 — insufficient scope
│   ├── NotFoundError             # 404 — resource not found
│   ├── ConflictError             # 409 — duplicate resource or state conflict
│   ├── ValidationError           # 422 — request body failed validation
│   ├── RateLimitError            # 429 — rate limit exceeded
│   └── ServerError               # 500/503 — CaseForge infrastructure error
├── NetworkError                  # Connection timeout, DNS failure, TLS error
└── TimeoutError                  # Request exceeded the configured timeout
Node.js — exception hierarchy
CaseForgeError                    // Base class — all SDK errors
├── APIError                      // HTTP 4xx/5xx from the API
│   ├── AuthenticationError       // 401
│   ├── PermissionError           // 403
│   ├── NotFoundError             // 404
│   ├── ConflictError             // 409
│   ├── ValidationError           // 422
│   ├── RateLimitError            // 429
│   └── ServerError               // 500/503
├── NetworkError                  // Connection-level errors
└── TimeoutError                  // Request timed out

Catching exceptions

Python
import caseforge
from caseforge.exceptions import (
    ValidationError, NotFoundError, RateLimitError, CaseForgeError
)

client = caseforge.Client()

try:
    case = client.cases.get("case_invalid")

except NotFoundError as e:
    # 404 — case ID doesn't exist or belongs to a different workspace
    print(f"Case not found: {e.error_code} — {e.message}")

except ValidationError as e:
    # 422 — the request body failed server-side validation
    print(f"Validation failed on '{e.param}': {e.message}")

except RateLimitError as e:
    # 429 — back off and retry after e.retry_after seconds
    print(f"Rate limited. Retry after {e.retry_after}s")

except CaseForgeError as e:
    # Catch-all for any SDK error
    print(f"SDK error [{e.request_id}]: {e}")
Node.js
import CaseForge, {
  NotFoundError, ValidationError, RateLimitError, CaseForgeError
} from '@caseforge/sdk';

const client = new CaseForge();

try {
  const aCase = await client.cases.get('case_invalid');

} catch (e) {
  if (e instanceof NotFoundError) {
    console.error(`Case not found: ${e.errorCode}`);

  } else if (e instanceof ValidationError) {
    console.error(`Validation failed on '${e.param}': ${e.message}`);

  } else if (e instanceof RateLimitError) {
    console.error(`Rate limited. Retry after ${e.retryAfter}s`);

  } else if (e instanceof CaseForgeError) {
    console.error(`SDK error [${e.requestId}]: ${e.message}`);
  }
}

HTTP status code reference

StatusSDK exceptionWhen it occursResolution
400APIErrorMalformed request — missing fields, wrong content type.Check request body structure against the API reference.
401AuthenticationErrorMissing, invalid, or expired credential.Verify your API key or refresh your OAuth token. Check for stray whitespace when setting env vars.
403PermissionErrorValid credentials, but the token lacks the required scope.Add the needed scope when registering your OAuth application, or use a key with Workspace Admin role.
404NotFoundErrorThe requested resource doesn't exist, or belongs to a different workspace.Verify the resource ID. Confirm your API key is scoped to the correct workspace.
409ConflictErrorDuplicate resource — creating a case for a subject that already has an open case of the same type.Retrieve the existing resource instead of creating a new one, or close the existing case first.
422ValidationErrorRequest body is syntactically valid JSON, but fails business rule validation.Read error.param to identify which field failed. Common causes: priority out of range, date in the past, or an unsupported case_type.
429RateLimitErrorToo many requests on the Sandbox tier, or a burst above your plan limit.Respect the Retry-After response header (e.retry_after in the SDK). Implement exponential backoff for high-volume pipelines.
500ServerErrorCaseForge infrastructure error.Retry with exponential backoff. If the error persists beyond 15 minutes, check the CaseForge status page at status.caseforge.io.
503ServerErrorService temporarily unavailable — typically a brief maintenance window.Retry after the interval in the Retry-After header.

Retry logic

The SDK includes built-in retry logic for transient failures. By default, it retries 429 and 503 responses up to two times using exponential backoff with jitter.

Configure retry behavior

Python
import caseforge

client = caseforge.Client(
    max_retries=3,             # Retry up to 3 times (default: 2)
    timeout=30.0,              # Seconds before a request times out (default: 60)
    retry_on_status=[429, 503],  # Which status codes trigger a retry
)
Node.js
import CaseForge from '@caseforge/sdk';

const client = new CaseForge({
  maxRetries:    3,              // default: 2
  timeout:       30_000,        // milliseconds, default: 60000
  retryOnStatus: [429, 503],    // default
});
Do not retry 4xx errors (except 429)

Client errors (400, 401, 403, 404, 422) indicate a problem with the request. Retrying them won't succeed and wastes rate limit budget. Only retry 429 and 5xx responses.

Idempotency keys

For write operations (case creation, SAR filing), include an idempotency key to make retries safe. If a request fails and you retry it with the same key, the API returns the result of the original request — rather than creating a duplicate resource.

Python
import uuid
import caseforge

client = caseforge.Client()

# Generate a key per request attempt — store it so you can retry with the same key
idempotency_key = str(uuid.uuid4())

case = client.cases.create(
    subject_id="cust_7a2b4c",
    case_type="transaction_monitoring",
    priority="high",
    summary="Structuring activity",
    idempotency_key=idempotency_key,   # Pass as a keyword argument
)
Node.js
import { randomUUID } from 'crypto';
import CaseForge from '@caseforge/sdk';

const client     = new CaseForge();
const idempKey   = randomUUID();    // Store this for retry

const aCase = await client.cases.create(
  {
    subjectId: 'cust_7a2b4c',
    caseType:  'transaction_monitoring',
    priority:  'high',
    summary:   'Structuring activity',
  },
  { idempotencyKey: idempKey },       // Pass as second argument
);