CognitiveX Docs

Errors

The error JSON shape, every status code the iCog API returns, and how to handle failures in code.

The iCog API uses conventional HTTP status codes and a single, predictable JSON error shape. A 2xx status means success. A 4xx means something about your request was wrong (auth, validation, quota, or a missing resource). A 5xx means the request was valid but iCog or one of its dependencies failed.

Base URL for all endpoints is https://api.cognitivx.io. See Authentication for how to pass a key.

Error shape

Every error response is JSON with a detail field. detail is usually a string:

{
  "detail": "Memory not found"
}

For a few gated and quota errors (403 and 429), detail is a structured object instead of a string. Those shapes are documented per status code below. Always check whether detail is a string or an object before reading it.

Uncaught server errors are wrapped by a global handler, so even a 500 is valid JSON, never a plain-text stack trace or an HTML page:

{
  "detail": "Internal server error",
  "path": "/api/remember"
}

Every response, success or error, carries an X-Request-ID header (a UUID). Include it when you report a problem so it can be traced in the logs.

Validation errors (422)

When a request body or query parameter fails schema validation, FastAPI returns 422 Unprocessable Entity. Here detail is an array of field errors, not a string:

{
  "detail": [
    {
      "loc": ["body", "query"],
      "msg": "Field required",
      "type": "missing"
    }
  ]
}
FieldTypeDescription
locarrayPath to the offending field, e.g. ["body", "query"] or ["path", "memory_id"].
msgstringHuman-readable description of what is wrong.
typestringMachine-readable error type, e.g. missing, string_type, uuid_parsing.

A 422 also fires when a path parameter that must be a UUID (such as a memory_id) cannot be parsed. Validation errors are not retryable: fix the request and send it again.

Status codes

These are the status codes the API actually returns and what triggers each one.

StatusMeaningWhen it happens
200OKSuccessful request that returns a body.
201CreatedA resource was created (for example POST /api/auth/signup or minting an API key).
204No ContentA successful mutation or delete with no body.
400Bad RequestThe app explicitly rejected your input. For example, storing a foundational memory without a first-person anchor. detail is a string explaining the problem.
401UnauthorizedMissing or invalid credentials: bad API key, malformed or expired JWT, or no auth header. See below.
402Payment RequiredAn organization seat or billing gate. Personal memory routes never return this.
403ForbiddenAuthenticated but not allowed: unverified email, insufficient tier, or not a member of the target org. detail is a structured object. See below.
404Not FoundThe resource (memory, session, org) does not exist or is not owned by you, or the route is unknown.
409ConflictA uniqueness or state conflict, such as signing up with an email that already exists.
410GoneA one-time link or vault entry has expired or already been consumed.
413Payload Too LargeAn upload exceeds its size cap. POST /api/transcribe rejects audio over 25 MB.
422Unprocessable EntityRequest body or params failed validation. See Validation errors.
429Too Many RequestsAuth rate limit, a usage quota cap, or credit depletion. See below.
500Internal Server ErrorAn uncaught server error. Body is { "detail": "Internal server error", "path": "..." }.
502Bad GatewayAn upstream extraction failure (for example URL fetch and extract).
503Service UnavailableA dependency is not ready. GET /api/ready returns this while the database or LLM router is down; GET /api/health/detail returns it until the search index finishes warming.

There is no API version header. Endpoints live under the unversioned /api prefix, and the OpenAPI spec reports version 1.0.0. Do not send an Accept-Version or X-API-Version header; none is read.

401 Unauthorized

Returned when credentials are missing or invalid. detail is a string, and the response carries a WWW-Authenticate: Bearer header when a token failed to validate.

{
  "detail": "Could not validate credentials"
}

Common detail strings: Invalid API key, Could not validate credentials, and Token expired. Fix the credential rather than retrying. If you are using a short-lived JWT, mint or refresh it; if you are using an API key, confirm it starts with icog_ and has not been revoked at developers.cognitivx.io/keys.

403 Forbidden

You are authenticated but the action is not permitted. Here detail is an object whose error field tells you which gate you hit.

Email not verified (gates /api/chat, /api/senses, /api/introspect, /api/suggest and similar):

{
  "detail": {
    "error": "email_not_verified",
    "message": "Verify your email to use this feature"
  }
}

Tier too low:

{
  "detail": {
    "error": "tier_insufficient",
    "current_tier": "awakened",
    "required_tier": "conscious",
    "upgrade_url": "https://developers.cognitivx.io/billing"
  }
}

A paid-feature gate uses error: "feature_gate_denied" with feature, current_tier, recommended_tier, and upgrade_url. Admin-only and org-membership failures return a plain string detail (Admin access required, Not a member of this organization).

429 Too Many Requests

429 covers three distinct situations, distinguishable by the body. Read Rate limits for the full picture.

Auth rate limiter. A fixed limit of 5 requests per 60 seconds per client IP, applied only to the auth endpoints (/api/auth/signup, /signin, /forgot-password, /reset-password, /resend-verification). No Retry-After or X-RateLimit-* headers are sent.

{
  "detail": "Too many requests. Please try again later."
}

Tier quota exceeded. You hit a monthly message or recall cap for your tier. detail is an object:

{
  "detail": {
    "error": "quota_exceeded",
    "message": "Monthly recall quota reached",
    "quota": { "used": 1000, "limit": 1000, "remaining": 0 },
    "current_tier": "awakened",
    "recommended_tier": "conscious",
    "upgrade_url": "https://developers.cognitivx.io/billing"
  }
}

Budget or credit depletion. Memory routes (/api/recall, /api/remember, /api/talk) surface a quota error as a structured detail object when your credits run out. Top up or upgrade.

The auth limiter and the usage quotas do not return Retry-After headers. The only endpoint that sets Retry-After is the per-method limiter on /api/url, whose body is {"error":"rate_limited","retry_after":N,"method":"..."}. Do not write code that depends on Retry-After being present on a generic 429.

Retry guidance

StatusRetry?How
400, 403, 404, 409, 410, 413, 422NoThese are caused by the request. Fix it, then resend.
401NoRefresh or replace the credential, then resend.
429 (auth limiter)Yes, after a delayWait and back off. No Retry-After is provided, so use your own backoff (start around 60s for the auth limiter).
429 (quota or credits)NoRetrying will not help until the quota resets or you upgrade or add credits.
500YesTransient. Retry with exponential backoff and jitter.
502, 503YesA dependency is down or warming. Retry with backoff; check GET /api/ready.

There is no Idempotency-Key header. Because most write operations are not idempotent, only retry writes when you received a 5xx (the server may not have processed the request) or a network error, and keep retries bounded.

POST /api/recall is resilient: on an internal failure it returns an empty result {"memories": [], "count": 0} with a 200 rather than a 500. Treat an empty result as "nothing found right now," not necessarily as a hard error.

Catching errors in TypeScript

The @cognitivx/sdk throws an ApiError for any non-2xx response. It exposes three properties:

PropertyTypeDescription
statusnumberThe HTTP status code.
messagestringThe detail string when detail is a string, otherwise a generic Request failed: <status>.
bodyunknownThe full parsed JSON response body. Read this for structured detail objects (403, 429) and 422 arrays.

For 403 and 429 errors whose detail is an object, and for 422 whose detail is an array, message falls back to Request failed: <status>. The useful information lives in error.body. Always inspect error.body for those cases.

import { configureApiClient, memory, ApiError } from '@cognitivx/sdk';

configureApiClient({ apiKey: process.env.ICOG_API_KEY });

try {
  const result = await memory.recall({ query: 'what did we decide about billing', limit: 5 });
  console.log(result.memories);
} catch (err) {
  if (err instanceof ApiError) {
    console.error(`iCog returned ${err.status}: ${err.message}`);
    console.error('body:', err.body);
  } else {
    throw err; // network or unexpected error
  }
}
import { configureApiClient, memory, ApiError } from '@cognitivx/sdk';

configureApiClient({ apiKey: process.env.ICOG_API_KEY });

try {
  await memory.remember({ content: 'Parsa prefers limit/offset pagination', memory_type: 'semantic' });
} catch (err) {
  if (!(err instanceof ApiError)) throw err;

  switch (err.status) {
    case 401:
      // Credential is bad or expired. Refresh or replace the key, then retry.
      console.error('Auth failed:', err.message);
      break;

    case 403: {
      // detail is a structured object; read it from body.
      const detail = (err.body as { detail?: { error?: string; upgrade_url?: string } }).detail;
      console.error('Forbidden:', detail?.error, detail?.upgrade_url);
      break;
    }

    case 422: {
      // detail is an array of field errors.
      const fields = (err.body as { detail?: { loc: string[]; msg: string }[] }).detail ?? [];
      for (const f of fields) console.error(`${f.loc.join('.')}: ${f.msg}`);
      break;
    }

    case 429:
      // Quota or rate limit. Back off; do not hammer the endpoint.
      console.error('Rate limited or over quota:', err.body);
      break;

    default:
      if (err.status >= 500) {
        // Transient. Safe to retry with backoff.
        console.error('Server error, retrying later:', err.status);
      } else {
        console.error('Request error:', err.status, err.message);
      }
  }
}