{
  "openapi": "3.1.0",
  "info": {
    "title": "komplai Account API",
    "version": "1.3.0",
    "description": "The komplai account API. Most integrations use **Redact**: pseudonymise text, find personal data, put the originals back, and read the text of PDF and Word files (beta). Guides, one page per error code and consoles to try each call are at https://developers.komplai.dk.\n\n## Keys\n\nSend `Authorization: Bearer <key>` with every call to an operation below. GET /v1/openapi.json is public and ignores any Authorization header.\n\n- **Organisation integration key** (`organisationKey`, starts with `kp_org_`): komplai issues it to your organisation for one of its own systems, such as a robot or an integration platform. It belongs to no person and needs no account or sign-in. It works with Redact and GET /v1/access only; every other operation answers 403 `integration_not_permitted`.\n- **Personal key** (`personalKey`, starts with `kp_live_`): a member of the organisation creates it in the hub's API area (https://hub.komplai.dk/api-access). It reaches the apps that person is assigned, within the scopes chosen for the key.\n\nEach operation's security lists the keys it accepts, and `x-komplai-scopes` the scopes it needs. Keep keys in a secret store, never in code or e-mail.\n\n## Start here\n\nBase URL: `https://api.komplai.dk`. Call GET /v1/access first: it shows the key's scopes, where each Redact layer processes text, the NER places and the models a call may choose, the limits and whether text jobs are accepted.\n\n## Stability\n\nEvery operation has `x-komplai-stability`: `stable` keeps its contract, `beta` may still change, with notice, and `alpha` is early and may change without notice. A request field marked `preview` is an early option inside a stable operation and may change without notice.\n\n## Limits\n\nText limits and every offset count UTF-16 code units, as in JavaScript and .NET: 16,000 for POST /v1/redact/detect, 750,000 for POST /v1/redact and 2,000,000 for text jobs. The per-minute request limit (limits in GET /v1/access) applies per person for a personal key, shared with that person's other keys and use of the hub, and per integration for an organisation integration key, shared by its keys and kept across rotation. The organisation shares 120 requests per minute. On 429, wait the seconds in `Retry-After`.\n\n## Data\n\nWithout an approved data processing agreement, use test data only. Rules run on komplai's servers in Germany (EU). NER runs at the place a call names in `nerDeployment` or, without one, at the default place GET /v1/access marks: `eu-de` is komplai's servers in Germany, and `local` is komplai's local infrastructure in Denmark, once GET /v1/access lists it as available. The language model runs on komplai's local infrastructure in Denmark unless a call asks for `processing: \"eu-cloud\"`, which needs EU processing approved for your organisation and counts towards its monthly AI spending cap. A call can name a NER model in `nerModel` and a language model validated for Redact in `model`; without them, the default models run. A place or model that cannot be used is refused by name and never replaced by another. No method guarantees anonymity: check the result before you use it.\n\n## Errors\n\nErrors are JSON: `{\"error\": \"<code>\", \"request_id\": \"<uuid>\", \"doc_url\": \"<page>\"}`, sometimes with more fields that the code's example shows. `doc_url` is the page that explains the code and what to do; `x-komplai-error-codes` lists every code. Branch on the status first and the code second, and treat an unknown code by its status. Apart from a streamed answer that has already begun, a failed call returns no partial result.\n\nThe API answers every call it handles with `Request-Id`, which an error body repeats as `request_id`. Send your own `X-Request-Id` (a UUID) to have it used; anything else gets a new one. Quote it when you ask about a call. A response without `Request-Id` was answered before the API handled the call, for example by the gateway while the API restarts: treat it by its status.\n\nQuestions or a new key: hej@komplai.dk.",
    "contact": {
      "name": "komplai",
      "email": "hej@komplai.dk",
      "url": "https://komplai.dk"
    }
  },
  "externalDocs": {
    "description": "Guides, error codes and consoles to try each call",
    "url": "https://developers.komplai.dk"
  },
  "servers": [
    {
      "url": "https://api.komplai.dk",
      "description": "Production"
    }
  ],
  "security": [
    {
      "organisationKey": []
    },
    {
      "personalKey": []
    }
  ],
  "tags": [
    {
      "name": "Redact",
      "description": "Pseudonymise text, find personal data and put the originals back. Send short texts synchronously and long documents as encrypted text jobs; PDF and Word files are in beta. Organisation integration keys and personal keys both work here.",
      "x-komplai-stability": "stable"
    },
    {
      "name": "Account",
      "description": "What the key can reach, where each Redact layer processes text, the NER places and models a call may choose, and the limits.",
      "x-komplai-stability": "stable"
    },
    {
      "name": "Chat",
      "description": "Chat completions and model discovery on the organisation's approved processing. Needs a personal key with chat:complete; an organisation integration key gets 403 integration_not_permitted.",
      "x-komplai-stability": "stable"
    },
    {
      "name": "Decide",
      "description": "Typed questions about a text, answered with probabilities. Beta. Needs a personal key with decide:process; an organisation integration key gets 403 integration_not_permitted.",
      "x-komplai-stability": "beta"
    },
    {
      "name": "Coding",
      "description": "Account, models and streamed completions for coding clients. Beta. Needs a personal key with coding:read or coding:complete; an organisation integration key gets 403 integration_not_permitted.",
      "x-komplai-stability": "beta"
    }
  ],
  "components": {
    "securitySchemes": {
      "organisationKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Organisation integration key. komplai issues it to your organisation for one of its own systems. It starts with kp_org_, belongs to no person and needs no account or sign-in. It works with Redact and GET /v1/access only, within the scopes it was issued with (access:read, redact:detect, redact:process). Send the whole key as the bearer token. A text job belongs to the key that created it: after the key is rotated or revoked, its jobs can no longer be read."
      },
      "personalKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Personal key. A member of the organisation creates it in the hub's API area (https://hub.komplai.dk/api-access). It starts with kp_live_ and reaches the apps that person is assigned, within the scopes chosen when it was created. Membership, assignment and approval are checked again on every call."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "additionalProperties": true,
        "required": [
          "error",
          "request_id",
          "doc_url"
        ],
        "description": "Every error body the API writes. Some codes add fields, such as hint, state or nerDeployment; each code's example shows them.",
        "properties": {
          "error": {
            "type": "string",
            "description": "The error code, such as rate_limited. x-komplai-error-codes lists every code with its meaning and what to do."
          },
          "request_id": {
            "type": "string",
            "format": "uuid",
            "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
          },
          "doc_url": {
            "type": "string",
            "format": "uri",
            "description": "The page that explains this code and what to do about it."
          }
        }
      }
    },
    "headers": {
      "RequestId": {
        "description": "This request's id: your X-Request-Id when you sent one valid UUID, otherwise a new UUID. An error body repeats it as request_id.",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "example": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60"
      }
    }
  },
  "paths": {
    "/v1/redact": {
      "post": {
        "operationId": "redactText",
        "summary": "Pseudonymise a text",
        "description": "Replaces personal data with labels such as [PERSON-1] and returns the pseudonymised text in redacted. Choose the layers in detectors: regex (rules for CPR numbers, phone numbers, e-mail addresses and more), ner (a model for names, organisations and addresses) and llm (a language model for what rules and NER miss, such as health data in free text). Without detectors, regex and ner run. Rules run on komplai's servers in Germany (EU), and NER at the place in nerDeployment or, without one, at the default place GET /v1/access marks. The language model runs on komplai's local infrastructure in Denmark by default; processing eu-cloud uses komplai's EU cloud, needs llm in detectors and EU processing approved for your organisation, and counts towards the organisation's monthly AI spending cap. Your organisation's own redaction rules always apply.\n\nUse this call for short texts such as a letter or a note: at most 750,000 UTF-16 code units, and work that finishes within 300 seconds. Before anything runs, the call estimates the time from komplai's measured speeds with an empty queue; a run whose estimate exceeds the hold is refused with 413 use_job, its estimatedSeconds and holdSeconds, and nothing is processed or charged. The estimate cannot cover an EU language model, which has no measured speed yet, time spent waiting in the shared queue, or a text that takes longer than a typical one: such a run can still reach the hold and end with 504 processing_timeout and no result, and calls already sent to the EU cloud count towards the monthly AI spending cap. Send long texts and EU runs as a text job (POST /v1/redact/jobs), or send this call with the header Prefer: respond-async to get 202 and a job instead. Without that header the call never turns into a job. PDF and Word files go to POST /v1/redact/file. Ask for include reports only when you need them: they contain the originals. A failed call returns no partial result. engine agentic (preview) requires the organisation administrator's opt-in (403 when absent) and processing local; it reads up to 2,000,000 UTF-16 code units as a job, while a synchronous call takes what it can finish in time. External lookups require explicit per-run sources and the administrator's current source permission. No method guarantees anonymity; check the result.\n\nOrganisation integration keys cannot use engine agentic: 403 integration_not_permitted.",
        "tags": [
          "Redact"
        ],
        "x-komplai-stability": "stable",
        "security": [
          {
            "organisationKey": []
          },
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "redact:process"
        ],
        "x-komplai-principals": [
          "person",
          "integration"
        ],
        "parameters": [
          {
            "in": "header",
            "name": "prefer",
            "required": false,
            "description": "Optional. respond-async answers 202 with a text job, exactly as POST /v1/redact/jobs, instead of holding the connection; the answer carries Preference-Applied: respond-async. Without it the call never becomes a job. Ignored while text jobs are not accepted.",
            "schema": {
              "type": "string",
              "enum": [
                "respond-async"
              ]
            }
          },
          {
            "in": "header",
            "name": "idempotency-key",
            "required": false,
            "description": "Optional. A new UUID for each job, in upper or lower case. If the connection drops, send the same request again with the same key: within the job's lifetime the same key and body return the same job (reused true) instead of a second one, and the same key with another body answers 409 idempotency_conflict. Never reuse a key for another text. The key is bound to your organisation, the owner (person or integration) and the API key. The default {{$guid}} is Postman's variable for a new UUID on each request; in other tools, generate a UUID for each job or leave the header out.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "default": "{{$guid}}"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 750000,
                    "description": "The exact text to pseudonymise: non-blank, valid Unicode, without NUL. At most 750,000 UTF-16 code units, as in JavaScript and .NET; every offset in the response counts the same way."
                  },
                  "detectors": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 3,
                    "uniqueItems": true,
                    "default": [
                      "regex",
                      "ner"
                    ],
                    "items": {
                      "type": "string",
                      "enum": [
                        "regex",
                        "ner",
                        "llm"
                      ]
                    },
                    "description": "The layers to run, in any non-empty combination. regex: rules for CPR numbers, phone numbers, e-mail addresses and more. ner: a model for names, organisations and addresses. llm: a language model for what rules and NER miss, such as health data in free text; it can take up to a minute. It reads the text in parts of at most 12,000 characters. When its answer for a part cannot be used, that answer is discarded and the part is asked again in two overlapping halves, down to halves of 1,500 characters: at most 14 extra calls for a part. If an answer still cannot be used, the call fails with 502 invalid_detection or incomplete_detection and returns no result. Default regex and ner. The older field mode is still accepted but is not listed here: leave it out, because if you send both, they must agree (400 unsupported_engine_mode). engine agentic needs regex and llm, with optional ner."
                  },
                  "processing": {
                    "type": "string",
                    "enum": [
                      "local",
                      "eu-cloud"
                    ],
                    "default": "local",
                    "description": "Where the language model (llm) runs. local (default): komplai's local infrastructure in Denmark, with no AI charge. eu-cloud: komplai's EU cloud, which answers faster. It needs llm in detectors and EU processing approved for your organisation, and its usage counts towards the organisation's monthly AI spending cap (429 ai_credit_exhausted when the cap is reached). Every call to the model, including a part asked again in halves, is checked against the cap and counted on its own. Name the model in model, or leave it out for the default model there. Rules always run on komplai's servers in Germany (EU), and NER at the place in nerDeployment or, without one, at the default place GET /v1/access marks. GET /v1/access names where each layer processes text for your organisation, including eu-cloud, in services[].hosting.processing.layers. The API never switches between local and eu-cloud by itself."
                  },
                  "nerDeployment": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "eu-de",
                      "local",
                      null
                    ],
                    "default": null,
                    "description": "Where the ner layer runs: eu-de (komplai's servers in Germany, EU) or local (komplai's local infrastructure in Denmark). Leave it out, or send null, for the default place: the one GET /v1/access marks with default true in the ner layer's places (services[].hosting.processing.layers), eu-de unless komplai has changed it. Each place there has available and a reason for the operations with detectors, and detect with the same for POST /v1/redact/detect and the operations without detectors, so check that the place you name is available for the operation you call. A place that cannot be used now answers 503 ner_location_unavailable and names the place in nerDeployment; NER never moves to another place. Needs ner in detectors (400 ner_deployment_without_ner); any other value answers 400 invalid_ner_deployment. The response names the place in configuration.nerDeployment (nerDeployment on POST /v1/redact/detect)."
                  },
                  "nerModel": {
                    "type": "string",
                    "nullable": true,
                    "default": null,
                    "description": "The NER model, by id from the ner layer's models in GET /v1/access. Leave it out, or send null, for the default model. An unknown id answers 400 ner_model_unknown, and a retired one or one that does not run at the place you name 400 ner_model_unavailable; a preview model needs your organisation's Redact preview, which an organisation integration key does not have (403 ner_model_preview_required). POST /v1/redact/detect, and the text operations without detectors, run only the default model (400 ner_model_unavailable for another). A model that is not set up at the default place on this server answers 503 ner_model_unconfigured; NER never runs another model. Needs ner in detectors (400 ner_model_without_ner). The response names the model in configuration.nerModel (nerModel on POST /v1/redact/detect)."
                  },
                  "model": {
                    "type": "string",
                    "nullable": true,
                    "default": null,
                    "description": "The language model for llm, by id from GET /v1/access: the models of the llm layer for the processing you choose. Leave it out, or send null, for the default model there. Only models validated for Redact at that processing are accepted (400 llm_model_not_validated): qwen3.8-27b-q8 for local and mistral-small-3.2-24b-instruct-2506 for eu-cloud. GET /v1/access also lists models with reason not_validated, which cannot be chosen. A local model that local infrastructure does not offer right now answers 503 local_model_unavailable, and an EU model your organisation's EU route does not have answers 409 model_not_enabled. Without model, the default must be validated too: on local the server's default model, on eu-cloud the EU model the person behind a personal key saved, else the model on your organisation's EU route (409 llm_model_not_validated). The API never picks another model. Needs llm in detectors and engine pipeline (400 llm_model_not_applicable). configuration.llmModel names the model that ran."
                  },
                  "include": {
                    "type": "array",
                    "maxItems": 4,
                    "uniqueItems": true,
                    "default": [],
                    "items": {
                      "type": "string",
                      "enum": [
                        "mapping",
                        "findings",
                        "changes",
                        "skipped"
                      ]
                    },
                    "description": "Extra reports. Each contains originals: protect the response as personal information. mapping: label to original, for POST /v1/redact/restore. changes: exact positions in your text and in redacted. findings: everything the layers found. skipped: CPR-shaped numbers the regex layer left in redacted because their digits do not decode to a plausible birth date, with the reason. A skipped number can still identify a person, for example an erstatningspersonnummer with day + 60. skipped is filled only when regex runs."
                  },
                  "policy": {
                    "type": "string",
                    "enum": [
                      "all-detected"
                    ],
                    "default": "all-detected",
                    "description": "Always all-detected: everything found is replaced, under your organisation's redaction and allow rules."
                  },
                  "engine": {
                    "type": "string",
                    "enum": [
                      "pipeline",
                      "agentic"
                    ],
                    "default": "pipeline",
                    "description": "Preview. Leave out. pipeline (default) runs the chosen detectors. agentic is an experimental method for organisations whose administrator has turned on the Redact Agentic preview (403 agentic_preview_not_enabled otherwise); an organisation integration key gets 403 integration_not_permitted. It needs processing local and detectors regex and llm, with optional ner. It reads the text in windows, so it takes the operation's own text limit; a long document belongs in a text job.",
                    "x-komplai-stability": "preview"
                  },
                  "sources": {
                    "type": "array",
                    "maxItems": 5,
                    "uniqueItems": true,
                    "default": [],
                    "items": {
                      "type": "string",
                      "enum": [
                        "cvr-official",
                        "bbr",
                        "web",
                        "dar",
                        "wikidata-local"
                      ]
                    },
                    "description": "Preview. Leave out. Lookup sources for engine agentic, chosen for each call. Each needs the administrator's approval and configured access; an unapproved or unavailable source refuses the call (403 agentic_lookup_not_allowed or 503 agentic_lookup_unavailable) and is never silently dropped. Default none.",
                    "x-komplai-stability": "preview"
                  }
                }
              },
              "examples": {
                "rulesAndNer": {
                  "summary": "Rules and NER, with mapping and changes",
                  "description": "regex finds the CPR number, phone number and e-mail address; ner finds the name and the address. mapping is what POST /v1/redact/restore needs; changes gives exact positions in your text and in redacted.",
                  "value": {
                    "text": "Borgeren Mette Kjærgaard Holm, cpr-nr. 120385-1234, bor på Fiktivvej 12, 8000 Aarhus C. Hun kan kontaktes på 21 34 56 78 eller mette.holm@example.invalid.",
                    "detectors": [
                      "regex",
                      "ner"
                    ],
                    "include": [
                      "mapping",
                      "changes"
                    ]
                  }
                },
                "nerPlace": {
                  "summary": "NER only, at a chosen place with a chosen model",
                  "description": "detectors [\"ner\"] runs NER without the rules. nerDeployment eu-de runs it on komplai's servers in Germany, and nerModel names the model; both are optional and come from GET /v1/access. Name local only when GET /v1/access lists it as available: a place that cannot be used answers 503 ner_location_unavailable, and NER never moves elsewhere. configuration names the place and the model.",
                  "value": {
                    "text": "Borgeren Mette Kjærgaard Holm bor på Fiktivvej 12, 8000 Aarhus C.",
                    "detectors": [
                      "ner"
                    ],
                    "nerDeployment": "eu-de",
                    "nerModel": "danish-pii-ner-v1.3.0"
                  }
                },
                "localLanguageModel": {
                  "summary": "With a chosen language model on local infrastructure",
                  "description": "llm also finds what rules and NER miss, here health data. It runs on komplai's local infrastructure in Denmark, has no AI charge and can take up to a minute. model names a model validated for Redact there; leave it out for the default model. configuration.llmModel names the model that ran.",
                  "value": {
                    "text": "Sagsbehandleren talte med Mette Kjærgaard Holm om hendes diabetes.",
                    "detectors": [
                      "regex",
                      "ner",
                      "llm"
                    ],
                    "processing": "local",
                    "model": "qwen3.8-27b-q8",
                    "include": [
                      "findings"
                    ]
                  }
                },
                "euCloud": {
                  "summary": "With a chosen language model in komplai's EU cloud",
                  "description": "The same layers with processing eu-cloud: the language model runs in komplai's EU cloud, which answers faster. It needs EU processing approved for your organisation, and its usage counts towards the organisation's monthly AI spending cap. model names a model validated for Redact on your organisation's EU route. Leave it out for the default there: for a personal key the EU model its person saved, else the model on your organisation's EU route. That default must be validated for Redact too (409 llm_model_not_validated). configuration.llmModel names the model that ran.",
                  "value": {
                    "text": "Sagsbehandleren talte med Mette Kjærgaard Holm om hendes diabetes.",
                    "detectors": [
                      "regex",
                      "ner",
                      "llm"
                    ],
                    "processing": "eu-cloud",
                    "model": "mistral-small-3.2-24b-instruct-2506",
                    "include": [
                      "mapping"
                    ]
                  }
                },
                "skippedCpr": {
                  "summary": "CPR-shaped numbers that were left in the text",
                  "description": "A number that looks like a CPR number but is not a possible birth date stays in the text. skipped lists each one with the reason. It can still identify a person, so treat it as personal data.",
                  "value": {
                    "text": "Gammelt nummer 150677-8642, erstatningspersonnummer 720385-1234 og cpr-nr. 120385-1234.",
                    "detectors": [
                      "regex"
                    ],
                    "include": [
                      "skipped"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The pseudonymised text and what ran. The include reports you asked for contain originals.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "redacted",
                    "mode",
                    "engine",
                    "processing",
                    "policy",
                    "policyVersion",
                    "offsetEncoding",
                    "charsIn",
                    "charsOut",
                    "truncated",
                    "degraded"
                  ],
                  "properties": {
                    "engine": {
                      "type": "string",
                      "enum": [
                        "pipeline",
                        "agentic"
                      ]
                    },
                    "redacted": {
                      "type": "string",
                      "description": "The pseudonymised text, with labels such as [PERSON-1]. The same original gets the same label throughout the text."
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "regex",
                        "ner",
                        "semantic"
                      ]
                    },
                    "processing": {
                      "type": "string",
                      "enum": [
                        "local",
                        "eu-cloud"
                      ],
                      "description": "Where the language model ran, or would have run."
                    },
                    "policy": {
                      "type": "string",
                      "enum": [
                        "all-detected"
                      ]
                    },
                    "policyVersion": {
                      "type": "string",
                      "enum": [
                        "redact-text-v1"
                      ]
                    },
                    "offsetEncoding": {
                      "type": "string",
                      "enum": [
                        "utf-16"
                      ],
                      "description": "Every offset counts UTF-16 code units."
                    },
                    "charsIn": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 2000000,
                      "description": "Length of your text in UTF-16 code units."
                    },
                    "charsOut": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Length of redacted in UTF-16 code units."
                    },
                    "truncated": {
                      "type": "boolean",
                      "enum": [
                        false
                      ],
                      "description": "Always false: a truncated detection fails the call instead."
                    },
                    "degraded": {
                      "type": "boolean",
                      "enum": [
                        false
                      ],
                      "description": "Always false: a degraded detection fails the call instead."
                    },
                    "configuration": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "engine",
                        "mode",
                        "detectors",
                        "sources",
                        "processing"
                      ],
                      "description": "The choices that took effect. llmModel names the language model when it is known, and llmReasoningEffort the reasoning effort it was sent: by the llm layer, and on eu-cloud by mode semantic without detectors too. nerModel and nerDeployment name the NER model and place when you chose them, and nerDeployment also when NER ran at another place than eu-de.",
                      "properties": {
                        "engine": {
                          "type": "string",
                          "enum": [
                            "pipeline",
                            "agentic"
                          ]
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "regex",
                            "ner",
                            "semantic"
                          ]
                        },
                        "detectors": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "llm"
                            ]
                          }
                        },
                        "sources": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "cvr-official",
                              "bbr",
                              "web",
                              "dar",
                              "wikidata-local"
                            ]
                          }
                        },
                        "processing": {
                          "type": "string",
                          "enum": [
                            "local",
                            "eu-cloud"
                          ]
                        },
                        "nerModel": {
                          "type": "string",
                          "description": "The NER model you named in nerModel."
                        },
                        "nerDeployment": {
                          "type": "string",
                          "enum": [
                            "eu-de",
                            "local"
                          ],
                          "description": "Where NER ran: present when you named the place, or when NER ran somewhere other than eu-de."
                        },
                        "llmModel": {
                          "type": "string",
                          "description": "The language model that answered, when it is known."
                        },
                        "llmReasoningEffort": {
                          "type": "string",
                          "enum": [
                            "none",
                            "low",
                            "medium",
                            "high",
                            "xhigh",
                            "max"
                          ],
                          "description": "The reasoning effort the language model was sent: by the llm layer of the pipeline engine, and on eu-cloud by mode semantic without detectors too. komplai sets it per model when it validates the model for Redact; none means no reasoning."
                        }
                      }
                    },
                    "execution": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "completed",
                        "detectorsRun"
                      ],
                      "description": "What actually ran: detectorsRun lists the layers, and the counts appear when they were measured. A missing count does not mean zero. llmOccurrenceRepairs counts llm findings whose quote occurs once in the text but whose occurrence number pointed past it; each was placed on that one match. llmResolvedFindings counts llm findings whose quote could not be placed exactly and that were placed on every span equal to the quote once spacing and line breaks, invisible characters, typographic quotes and dashes, and for quotes of two or more words or with a digit, letter case are ignored; each is an exact span of the text. llmWindows counts the language model's answers the result is made of. llmSplits counts answers that could not be used and were discarded; each made two more calls, one for each half of its part of the text.",
                      "properties": {
                        "completed": {
                          "type": "boolean",
                          "enum": [
                            true
                          ]
                        },
                        "detectorsRun": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "llm"
                            ]
                          }
                        },
                        "detectionPasses": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "nerCalls": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmCalls": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmOccurrenceRepairs": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmResolvedFindings": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmWindows": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmSplits": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "toolCalls": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "rounds": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "documentCoverageComplete": {
                          "type": "boolean"
                        },
                        "releaseReviewComplete": {
                          "type": "boolean"
                        }
                      }
                    },
                    "mapping": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "Label to canonical original mention. Use changes for exact source reconstruction."
                    },
                    "entries": {
                      "type": "array",
                      "description": "Returned with mapping: one entry per label, with its type, original, confidence and source layer.",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "placeholder",
                          "type",
                          "value",
                          "confidence",
                          "source"
                        ],
                        "properties": {
                          "placeholder": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          },
                          "value": {
                            "type": "string"
                          },
                          "confidence": {
                            "type": "string",
                            "enum": [
                              "high",
                              "medium",
                              "low"
                            ]
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "llm",
                              "custom"
                            ],
                            "description": "The layer that found it: regex (the rules), ner, llm (the language model) or custom (your organisation's own redaction rules). Always present."
                          }
                        }
                      }
                    },
                    "findings": {
                      "type": "array",
                      "description": "Everything the layers found, with offsets in your text and the layer that found each in source.",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "start",
                          "end",
                          "type",
                          "value",
                          "confidence",
                          "source"
                        ],
                        "properties": {
                          "start": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          },
                          "value": {
                            "type": "string"
                          },
                          "confidence": {
                            "type": "string",
                            "enum": [
                              "high",
                              "medium",
                              "low"
                            ]
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "llm",
                              "custom"
                            ],
                            "description": "The layer that found it: regex (the rules), ner, llm (the language model) or custom (your organisation's own redaction rules). Always present."
                          },
                          "customLabel": {
                            "type": "string",
                            "maxLength": 64
                          },
                          "replacementToken": {
                            "type": "string",
                            "maxLength": 128
                          },
                          "entityId": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "changes": {
                      "type": "array",
                      "description": "Each replacement with its offsets in your text (start, end) and in redacted (outputStart, outputEnd). Apply them in reverse order of outputStart to rebuild your exact text.",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "start",
                          "end",
                          "outputStart",
                          "outputEnd",
                          "original",
                          "replacement",
                          "type"
                        ],
                        "properties": {
                          "start": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "outputStart": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "outputEnd": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "original": {
                            "type": "string"
                          },
                          "replacement": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          }
                        }
                      }
                    },
                    "skipped": {
                      "type": "array",
                      "description": "CPR-shaped numbers the regex layer declined and no final change covers, with a reason code and an English explanation. They remain in redacted and can still identify a person, for example an erstatningspersonnummer with day + 60 (reported as cpr_invalid_date). Empty when the regex layer did not run (NER- or LLM-only detectors, or engine agentic).",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "start",
                          "end",
                          "value",
                          "type",
                          "reason",
                          "detail"
                        ],
                        "properties": {
                          "start": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "value": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          },
                          "reason": {
                            "type": "string",
                            "enum": [
                              "cpr_unknown_century",
                              "cpr_birth_year_out_of_range",
                              "cpr_invalid_date"
                            ]
                          },
                          "detail": {
                            "type": "string",
                            "maxLength": 200
                          }
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "rulesAndNer": {
                    "summary": "Rules and NER, with mapping and changes",
                    "value": {
                      "redacted": "Borgeren [PERSON-1], cpr-nr. [CPR-1], bor på [ADRESSE-1]. Hun kan kontaktes på [TLF-1] eller [EMAIL-1].",
                      "engine": "pipeline",
                      "mode": "ner",
                      "processing": "local",
                      "policy": "all-detected",
                      "policyVersion": "redact-text-v1",
                      "offsetEncoding": "utf-16",
                      "charsIn": 154,
                      "charsOut": 103,
                      "truncated": false,
                      "degraded": false,
                      "configuration": {
                        "engine": "pipeline",
                        "mode": "ner",
                        "detectors": [
                          "regex",
                          "ner"
                        ],
                        "sources": [],
                        "processing": "local"
                      },
                      "execution": {
                        "completed": true,
                        "detectorsRun": [
                          "regex",
                          "ner"
                        ],
                        "detectionPasses": 1,
                        "nerCalls": 1,
                        "llmCalls": 0
                      },
                      "mapping": {
                        "[PERSON-1]": "Mette Kjærgaard Holm",
                        "[CPR-1]": "120385-1234",
                        "[ADRESSE-1]": "Fiktivvej 12, 8000 Aarhus C",
                        "[TLF-1]": "21 34 56 78",
                        "[EMAIL-1]": "mette.holm@example.invalid"
                      },
                      "entries": [
                        {
                          "placeholder": "[PERSON-1]",
                          "type": "person",
                          "value": "Mette Kjærgaard Holm",
                          "confidence": "medium",
                          "source": "ner"
                        },
                        {
                          "placeholder": "[CPR-1]",
                          "type": "cpr",
                          "value": "120385-1234",
                          "confidence": "high",
                          "source": "regex"
                        },
                        {
                          "placeholder": "[ADRESSE-1]",
                          "type": "address",
                          "value": "Fiktivvej 12, 8000 Aarhus C",
                          "confidence": "high",
                          "source": "ner"
                        },
                        {
                          "placeholder": "[TLF-1]",
                          "type": "phone",
                          "value": "21 34 56 78",
                          "confidence": "medium",
                          "source": "regex"
                        },
                        {
                          "placeholder": "[EMAIL-1]",
                          "type": "email",
                          "value": "mette.holm@example.invalid",
                          "confidence": "high",
                          "source": "regex"
                        }
                      ],
                      "changes": [
                        {
                          "start": 9,
                          "end": 29,
                          "outputStart": 9,
                          "outputEnd": 19,
                          "original": "Mette Kjærgaard Holm",
                          "replacement": "[PERSON-1]",
                          "type": "person"
                        },
                        {
                          "start": 39,
                          "end": 50,
                          "outputStart": 29,
                          "outputEnd": 36,
                          "original": "120385-1234",
                          "replacement": "[CPR-1]",
                          "type": "cpr"
                        },
                        {
                          "start": 59,
                          "end": 86,
                          "outputStart": 45,
                          "outputEnd": 56,
                          "original": "Fiktivvej 12, 8000 Aarhus C",
                          "replacement": "[ADRESSE-1]",
                          "type": "address"
                        },
                        {
                          "start": 109,
                          "end": 120,
                          "outputStart": 79,
                          "outputEnd": 86,
                          "original": "21 34 56 78",
                          "replacement": "[TLF-1]",
                          "type": "phone"
                        },
                        {
                          "start": 127,
                          "end": 153,
                          "outputStart": 93,
                          "outputEnd": 102,
                          "original": "mette.holm@example.invalid",
                          "replacement": "[EMAIL-1]",
                          "type": "email"
                        }
                      ]
                    }
                  },
                  "nerPlace": {
                    "summary": "NER only, at a chosen place with a chosen model",
                    "value": {
                      "redacted": "Borgeren [PERSON-1] bor på [ADRESSE-1].",
                      "engine": "pipeline",
                      "mode": "ner",
                      "processing": "local",
                      "policy": "all-detected",
                      "policyVersion": "redact-text-v1",
                      "offsetEncoding": "utf-16",
                      "charsIn": 65,
                      "charsOut": 39,
                      "truncated": false,
                      "degraded": false,
                      "configuration": {
                        "engine": "pipeline",
                        "mode": "ner",
                        "detectors": [
                          "ner"
                        ],
                        "sources": [],
                        "processing": "local",
                        "nerModel": "danish-pii-ner-v1.3.0",
                        "nerDeployment": "eu-de"
                      },
                      "execution": {
                        "completed": true,
                        "detectorsRun": [
                          "ner"
                        ],
                        "detectionPasses": 1,
                        "nerCalls": 1,
                        "llmCalls": 0
                      }
                    }
                  },
                  "localLanguageModel": {
                    "summary": "With a chosen language model on local infrastructure",
                    "value": {
                      "redacted": "Sagsbehandleren talte med [PERSON-1] om hendes [HELBRED-1].",
                      "engine": "pipeline",
                      "mode": "semantic",
                      "processing": "local",
                      "policy": "all-detected",
                      "policyVersion": "redact-text-v1",
                      "offsetEncoding": "utf-16",
                      "charsIn": 66,
                      "charsOut": 59,
                      "truncated": false,
                      "degraded": false,
                      "configuration": {
                        "engine": "pipeline",
                        "mode": "semantic",
                        "detectors": [
                          "regex",
                          "ner",
                          "llm"
                        ],
                        "sources": [],
                        "processing": "local",
                        "llmModel": "qwen3.8-27b-q8"
                      },
                      "execution": {
                        "completed": true,
                        "detectorsRun": [
                          "regex",
                          "ner",
                          "llm"
                        ],
                        "detectionPasses": 1,
                        "nerCalls": 1,
                        "llmCalls": 1
                      },
                      "findings": [
                        {
                          "start": 26,
                          "end": 46,
                          "type": "person",
                          "value": "Mette Kjærgaard Holm",
                          "confidence": "medium",
                          "source": "ner",
                          "entityId": "person:Mette Kjærgaard Holm"
                        },
                        {
                          "start": 57,
                          "end": 65,
                          "type": "health",
                          "value": "diabetes",
                          "confidence": "medium",
                          "source": "llm"
                        }
                      ]
                    }
                  },
                  "euCloud": {
                    "summary": "With a chosen language model in komplai's EU cloud",
                    "value": {
                      "redacted": "Sagsbehandleren talte med [PERSON-1] om hendes [HELBRED-1].",
                      "engine": "pipeline",
                      "mode": "semantic",
                      "processing": "eu-cloud",
                      "policy": "all-detected",
                      "policyVersion": "redact-text-v1",
                      "offsetEncoding": "utf-16",
                      "charsIn": 66,
                      "charsOut": 59,
                      "truncated": false,
                      "degraded": false,
                      "configuration": {
                        "engine": "pipeline",
                        "mode": "semantic",
                        "detectors": [
                          "regex",
                          "ner",
                          "llm"
                        ],
                        "sources": [],
                        "processing": "eu-cloud",
                        "llmModel": "mistral-small-3.2-24b-instruct-2506"
                      },
                      "execution": {
                        "completed": true,
                        "detectorsRun": [
                          "regex",
                          "ner",
                          "llm"
                        ],
                        "detectionPasses": 1,
                        "nerCalls": 1,
                        "llmCalls": 1
                      },
                      "mapping": {
                        "[PERSON-1]": "Mette Kjærgaard Holm",
                        "[HELBRED-1]": "diabetes"
                      },
                      "entries": [
                        {
                          "placeholder": "[PERSON-1]",
                          "type": "person",
                          "value": "Mette Kjærgaard Holm",
                          "confidence": "medium",
                          "source": "ner"
                        },
                        {
                          "placeholder": "[HELBRED-1]",
                          "type": "health",
                          "value": "diabetes",
                          "confidence": "medium",
                          "source": "llm"
                        }
                      ]
                    }
                  },
                  "skippedCpr": {
                    "summary": "CPR-shaped numbers that were left in the text",
                    "value": {
                      "redacted": "Gammelt nummer 150677-8642, erstatningspersonnummer 720385-1234 og cpr-nr. [CPR-1].",
                      "engine": "pipeline",
                      "mode": "regex",
                      "processing": "local",
                      "policy": "all-detected",
                      "policyVersion": "redact-text-v1",
                      "offsetEncoding": "utf-16",
                      "charsIn": 87,
                      "charsOut": 83,
                      "truncated": false,
                      "degraded": false,
                      "configuration": {
                        "engine": "pipeline",
                        "mode": "regex",
                        "detectors": [
                          "regex"
                        ],
                        "sources": [],
                        "processing": "local"
                      },
                      "execution": {
                        "completed": true,
                        "detectorsRun": [
                          "regex"
                        ],
                        "detectionPasses": 1,
                        "nerCalls": 0,
                        "llmCalls": 0
                      },
                      "skipped": [
                        {
                          "start": 15,
                          "end": 26,
                          "value": "150677-8642",
                          "type": "cpr",
                          "reason": "cpr_birth_year_out_of_range",
                          "detail": "Birth year 1877 is before 1880"
                        },
                        {
                          "start": 52,
                          "end": 63,
                          "value": "720385-1234",
                          "type": "cpr",
                          "reason": "cpr_invalid_date",
                          "detail": "Day 72 does not exist in month 03 of 1985"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Only with Prefer: respond-async: the text is a job. Location points to its status; poll it, then read the result.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Location": {
                "description": "The job's status: /v1/redact/jobs/{id}.",
                "schema": {
                  "type": "string"
                },
                "example": "/v1/redact/jobs/3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c"
              },
              "Preference-Applied": {
                "description": "respond-async",
                "schema": {
                  "type": "string",
                  "enum": [
                    "respond-async"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "state",
                    "createdAt",
                    "expiresAt",
                    "progress"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "state": {
                      "type": "string",
                      "enum": [
                        "queued",
                        "running",
                        "succeeded",
                        "failed",
                        "cancelled",
                        "invalidated"
                      ]
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Application retention deadline. A queued job expires this long after it was submitted; a running job is kept for as long as its worker is alive; when the job ends, the retention period counts from the end. Results remain encrypted until explicit deletion or expiry; reads do not consume them."
                    },
                    "progress": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "completed",
                        "total"
                      ],
                      "properties": {
                        "completed": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    },
                    "error": {
                      "type": "string"
                    },
                    "reused": {
                      "type": "boolean"
                    }
                  }
                },
                "examples": {
                  "submitted": {
                    "summary": "A new job",
                    "value": {
                      "id": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c",
                      "state": "queued",
                      "createdAt": "2026-10-20T08:00:00.000Z",
                      "expiresAt": "2026-10-20T09:00:00.000Z",
                      "progress": {
                        "completed": 0,
                        "total": 0
                      },
                      "reused": false
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `invalid_body`: The body is not a JSON object with the documented fields: invalid JSON, Content-Type text/plain, an unknown field, a field of the wrong type, or a required field that is missing or blank.\n- `invalid_text_encoding`: text contains NUL or a lone UTF-16 surrogate.\n- `unsupported_redact_engine`: engine is not one of the documented values.\n- `engine_retired`: engine review, the separate LLM engine, is retired. Send engine pipeline, or leave engine out, with detectors [\"regex\", \"llm\"]; hint says the same. Nothing was reserved or processed.\n- `unsupported_redact_mode`: mode is not regex, ner or semantic.\n- `invalid_detectors`: detectors is empty, has an unknown or repeated value, or more than three values.\n- `unsupported_engine_mode`: mode disagrees with detectors. Leave mode out when you send detectors.\n- `invalid_processing`: processing is not local or eu-cloud.\n- `unsupported_processing`: processing eu-cloud needs llm in detectors (and engine pipeline). Add llm to detectors.\n- `unsupported_redact_policy`: policy is not all-detected.\n- `invalid_include`: include has an unknown or repeated value.\n- `invalid_ner_deployment`: nerDeployment is not eu-de or local.\n- `ner_deployment_without_ner`: nerDeployment was sent, but ner is not in detectors (or mode is regex).\n- `ner_model_without_ner`: nerModel was sent, but ner is not in detectors (or mode is regex).\n- `ner_model_unknown`: No NER model has the id in nerModel. GET /v1/access lists the models you may choose.\n- `ner_model_unavailable`: The NER model in nerModel is retired or does not run at the chosen place. A model other than the default also needs detectors.\n- `llm_model_not_applicable`: model was sent, but llm is not in detectors, or engine is not pipeline.\n- `llm_model_not_validated`: The language model in model is not validated for Redact at the processing you chose. GET /v1/access lists the models you may name.\n- `unsupported_engine_detectors`: The detectors do not fit engine agentic: it needs regex and llm, with optional ner.\n- `invalid_sources`: sources has an unknown or repeated value.\n- `unsupported_engine_sources`: sources was sent without engine agentic.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_body": {
                    "summary": "invalid_body",
                    "value": {
                      "error": "invalid_body",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_body"
                    }
                  },
                  "invalid_text_encoding": {
                    "summary": "invalid_text_encoding",
                    "value": {
                      "error": "invalid_text_encoding",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_text_encoding"
                    }
                  },
                  "unsupported_redact_engine": {
                    "summary": "unsupported_redact_engine",
                    "value": {
                      "error": "unsupported_redact_engine",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_engine"
                    }
                  },
                  "engine_retired": {
                    "summary": "engine_retired",
                    "value": {
                      "error": "engine_retired",
                      "hint": "engine review is retired. Send engine pipeline, or leave engine out, with detectors [\"regex\", \"llm\"].",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/engine_retired"
                    }
                  },
                  "unsupported_redact_mode": {
                    "summary": "unsupported_redact_mode",
                    "value": {
                      "error": "unsupported_redact_mode",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_mode"
                    }
                  },
                  "invalid_detectors": {
                    "summary": "invalid_detectors",
                    "value": {
                      "error": "invalid_detectors",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_detectors"
                    }
                  },
                  "unsupported_engine_mode": {
                    "summary": "unsupported_engine_mode",
                    "value": {
                      "error": "unsupported_engine_mode",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_mode"
                    }
                  },
                  "invalid_processing": {
                    "summary": "invalid_processing",
                    "value": {
                      "error": "invalid_processing",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_processing"
                    }
                  },
                  "unsupported_processing": {
                    "summary": "unsupported_processing",
                    "value": {
                      "error": "unsupported_processing",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_processing"
                    }
                  },
                  "unsupported_redact_policy": {
                    "summary": "unsupported_redact_policy",
                    "value": {
                      "error": "unsupported_redact_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_policy"
                    }
                  },
                  "invalid_include": {
                    "summary": "invalid_include",
                    "value": {
                      "error": "invalid_include",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_include"
                    }
                  },
                  "invalid_ner_deployment": {
                    "summary": "invalid_ner_deployment",
                    "value": {
                      "error": "invalid_ner_deployment",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_ner_deployment"
                    }
                  },
                  "ner_deployment_without_ner": {
                    "summary": "ner_deployment_without_ner",
                    "value": {
                      "error": "ner_deployment_without_ner",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_deployment_without_ner"
                    }
                  },
                  "ner_model_without_ner": {
                    "summary": "ner_model_without_ner",
                    "value": {
                      "error": "ner_model_without_ner",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_without_ner"
                    }
                  },
                  "ner_model_unknown": {
                    "summary": "ner_model_unknown",
                    "value": {
                      "error": "ner_model_unknown",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unknown"
                    }
                  },
                  "ner_model_unavailable": {
                    "summary": "ner_model_unavailable",
                    "value": {
                      "error": "ner_model_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unavailable"
                    }
                  },
                  "llm_model_not_applicable": {
                    "summary": "llm_model_not_applicable",
                    "value": {
                      "error": "llm_model_not_applicable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_applicable"
                    }
                  },
                  "llm_model_not_validated": {
                    "summary": "llm_model_not_validated",
                    "value": {
                      "error": "llm_model_not_validated",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_validated"
                    }
                  },
                  "unsupported_engine_detectors": {
                    "summary": "unsupported_engine_detectors",
                    "value": {
                      "error": "unsupported_engine_detectors",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_detectors"
                    }
                  },
                  "invalid_sources": {
                    "summary": "invalid_sources",
                    "value": {
                      "error": "invalid_sources",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_sources"
                    }
                  },
                  "unsupported_engine_sources": {
                    "summary": "unsupported_engine_sources",
                    "value": {
                      "error": "unsupported_engine_sources",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_sources"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not issued with it, the person behind a personal key is no longer assigned Redact, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `processing_choice_not_allowed`: Your organisation does not allow this processing (local or eu-cloud) through the API. NER on local infrastructure is local processing, also when llm runs on eu-cloud; that refusal names the place in nerDeployment.\n- `processing_not_enabled`: processing eu-cloud was requested, but EU processing is not enabled for Redact in your organisation, or its agreement keeps processing local.\n- `processing_not_approved`: The organisation's processing approval for Redact was withdrawn.\n- `app_not_assigned`: The person behind a personal key lost Redact or their membership while the request was admitted. A person who is no longer assigned Redact normally gets 401 invalid_api_key or 403 insufficient_scope instead.\n- `processing_access_changed`: The organisation's processing setup changed while the request was handled. Send it again.\n- `ai_credit_not_granted`: No monthly AI spending cap is set up for this month, so eu-cloud cannot run.\n- `ner_model_preview_required`: The NER model in nerModel is a preview. It needs your organisation's Redact preview, which an organisation integration key does not have.\n- `integration_not_permitted`: An organisation integration key sent engine agentic.\n- `agentic_preview_not_enabled`: The organisation's administrator has not turned on the Redact Agentic preview.\n- `agentic_lookup_not_allowed`: A source in sources is not approved by the organisation's administrator.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "processing_choice_not_allowed": {
                    "summary": "processing_choice_not_allowed",
                    "value": {
                      "error": "processing_choice_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_choice_not_allowed"
                    }
                  },
                  "processing_not_enabled": {
                    "summary": "processing_not_enabled",
                    "value": {
                      "error": "processing_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_enabled"
                    }
                  },
                  "processing_not_approved": {
                    "summary": "processing_not_approved",
                    "value": {
                      "error": "processing_not_approved",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_approved"
                    }
                  },
                  "app_not_assigned": {
                    "summary": "app_not_assigned",
                    "value": {
                      "error": "app_not_assigned",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/app_not_assigned"
                    }
                  },
                  "processing_access_changed": {
                    "summary": "processing_access_changed",
                    "value": {
                      "error": "processing_access_changed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_access_changed"
                    }
                  },
                  "ai_credit_not_granted": {
                    "summary": "ai_credit_not_granted",
                    "value": {
                      "error": "ai_credit_not_granted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ai_credit_not_granted"
                    }
                  },
                  "ner_model_preview_required": {
                    "summary": "ner_model_preview_required",
                    "value": {
                      "error": "ner_model_preview_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_preview_required"
                    }
                  },
                  "integration_not_permitted": {
                    "summary": "integration_not_permitted",
                    "value": {
                      "error": "integration_not_permitted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/integration_not_permitted"
                    }
                  },
                  "agentic_preview_not_enabled": {
                    "summary": "agentic_preview_not_enabled",
                    "value": {
                      "error": "agentic_preview_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_preview_not_enabled"
                    }
                  },
                  "agentic_lookup_not_allowed": {
                    "summary": "agentic_lookup_not_allowed",
                    "value": {
                      "error": "agentic_lookup_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_lookup_not_allowed"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict with the current state.\n\n- `processing_policy_changed`: Your organisation's redaction rules changed while the request ran. Send it again.\n- `model_not_enabled`: The EU cloud model set up for your organisation is not available, or the EU model you named in model is not on your organisation's EU route.\n- `llm_model_not_validated`: No model was named, and the default model where llm runs is not validated for Redact: the local language model this server uses by default or, with eu-cloud, the EU model the person behind a personal key saved, else the model on your organisation's EU route. Name a model from GET /v1/access.\n- `reasoning_effort_not_supported`: The reasoning effort komplai set for this EU language model in Redact is not one the model accepts on your organisation's EU route. Nothing was sent to the model, and the API does not send another effort.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "processing_policy_changed": {
                    "summary": "processing_policy_changed",
                    "value": {
                      "error": "processing_policy_changed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_policy_changed"
                    }
                  },
                  "model_not_enabled": {
                    "summary": "model_not_enabled",
                    "value": {
                      "error": "model_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_not_enabled"
                    }
                  },
                  "llm_model_not_validated": {
                    "summary": "llm_model_not_validated",
                    "value": {
                      "error": "llm_model_not_validated",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_validated"
                    }
                  },
                  "reasoning_effort_not_supported": {
                    "summary": "reasoning_effort_not_supported",
                    "value": {
                      "error": "reasoning_effort_not_supported",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/reasoning_effort_not_supported"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Too large.\n\n- `text_too_large`: text is over 750,000 UTF-16 code units. Send it as a text job.\n- `use_job`: The run cannot finish within the 300 seconds a synchronous call may take. estimatedSeconds is komplai's estimate from its measured speeds, with an empty queue; holdSeconds is the limit. Nothing was processed or charged. Send the same body as a text job (POST /v1/redact/jobs), or this call with Prefer: respond-async.\n- `processing_complexity_exceeded`: The text needs more work than its length allows: more candidate matches or matched characters than the per-character bounds in /v1/access, coreference matching work beyond its per-character bound (for example one name nested inside itself thousands of times), or more time for the organisation rules than their limit, which grows with the text and the number of rules. The bounds grow with the text, so splitting it rarely helps. Nothing partial is returned.\n- `result_too_large`: The result would be over 32 MiB. Ask for fewer include reports or split the text.\n- `context_limit`: A part of the text does not fit the language model's context, even after it was asked again in halves down to 1,500 characters.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "text_too_large": {
                    "summary": "text_too_large",
                    "value": {
                      "error": "text_too_large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/text_too_large"
                    }
                  },
                  "use_job": {
                    "summary": "use_job",
                    "value": {
                      "error": "use_job",
                      "estimatedSeconds": 540,
                      "holdSeconds": 300,
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/use_job"
                    }
                  },
                  "processing_complexity_exceeded": {
                    "summary": "processing_complexity_exceeded",
                    "value": {
                      "error": "processing_complexity_exceeded",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_complexity_exceeded"
                    }
                  },
                  "result_too_large": {
                    "summary": "result_too_large",
                    "value": {
                      "error": "result_too_large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/result_too_large"
                    }
                  },
                  "context_limit": {
                    "summary": "context_limit",
                    "value": {
                      "error": "context_limit",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/context_limit"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.\n\n- `invalid_body`: The body has no Content-Type, or one the API does not read, such as XML or form data. text/plain is read and answers 400 invalid_body.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_body": {
                    "summary": "invalid_body",
                    "value": {
                      "error": "invalid_body",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_body"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.\n- `ai_credit_exhausted`: The organisation's monthly AI spending cap for EU cloud is reached, or its own lower limit, or on a pilot the pilot's total allowance. Local processing keeps working.\n- `person_ai_credit_exhausted`: Your personal AI limit is reached (personal keys only).\n- `processing_capacity_busy`: All organisation-rule workers in this API process are busy. There is no queue: try again after Retry-After.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  },
                  "ai_credit_exhausted": {
                    "summary": "ai_credit_exhausted",
                    "value": {
                      "error": "ai_credit_exhausted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ai_credit_exhausted"
                    }
                  },
                  "person_ai_credit_exhausted": {
                    "summary": "person_ai_credit_exhausted",
                    "value": {
                      "error": "person_ai_credit_exhausted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/person_ai_credit_exhausted"
                    }
                  },
                  "processing_capacity_busy": {
                    "summary": "processing_capacity_busy",
                    "value": {
                      "error": "processing_capacity_busy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_capacity_busy"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "A processing service failed. No partial result is returned.\n\n- `incomplete_detection`: A detection layer did not finish the whole text. No partial result is returned.\n- `invalid_detection`: A detection layer returned output that failed validation. For llm: an answer that still could not be used after its part of the text was asked again in halves down to 1,500 characters.\n- `detection_service_unavailable`: The detection service could not be reached or failed.\n- `unsafe_placeholder`: A fixed replacement token from your organisation's rules occurs in the text or collides with a label.\n- `service_unavailable`: The EU cloud model failed or could not be reached.\n- `invalid_response`: The EU cloud model's answer failed validation.\n- `agent_no_progress`: Redact Agentic stopped making progress: several rounds in a row read nothing new, found nothing and moved to no new step. No partial result is returned.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "incomplete_detection": {
                    "summary": "incomplete_detection",
                    "value": {
                      "error": "incomplete_detection",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/incomplete_detection"
                    }
                  },
                  "invalid_detection": {
                    "summary": "invalid_detection",
                    "value": {
                      "error": "invalid_detection",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_detection"
                    }
                  },
                  "detection_service_unavailable": {
                    "summary": "detection_service_unavailable",
                    "value": {
                      "error": "detection_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/detection_service_unavailable"
                    }
                  },
                  "unsafe_placeholder": {
                    "summary": "unsafe_placeholder",
                    "value": {
                      "error": "unsafe_placeholder",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsafe_placeholder"
                    }
                  },
                  "service_unavailable": {
                    "summary": "service_unavailable",
                    "value": {
                      "error": "service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/service_unavailable"
                    }
                  },
                  "invalid_response": {
                    "summary": "invalid_response",
                    "value": {
                      "error": "invalid_response",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_response"
                    }
                  },
                  "agent_no_progress": {
                    "summary": "agent_no_progress",
                    "value": {
                      "error": "agent_no_progress",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agent_no_progress"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `ner_location_unavailable`: The NER place cannot be used now: komplai has not switched it on for the API, or it did not answer. nerDeployment names the place. NER never moves to another place; GET /v1/access shows which places are available.\n- `ner_model_unconfigured`: The NER model in nerModel is not set up at the default place on this server.\n- `local_model_unavailable`: The local language model in model is validated for Redact, but local infrastructure does not offer it right now. The API never picks another model.\n- `redact_runtime_unavailable`: A selected layer (NER or the language model) is not set up or not available right now.\n- `semantic_runtime_not_configured`: No local language model is set up for llm.\n- `detector_unavailable`: detectors includes ner, but NER is not set up.\n- `detection_service_unavailable`: Unexpected failure during detection.\n- `access_service_unavailable`: Keys and access could not be checked.\n- `invalid_organisation_policy`: Your organisation's redaction rules cannot be applied (more than 500 rules, or an invalid rule).\n- `chat_cloud_unavailable`: The organisation's EU cloud setup cannot be used right now.\n- `gateway_misconfigured`: The connection to the detection service or the language model is misconfigured.\n- `incoherent_redaction_policy`: Your organisation's redaction and allow rules conflict for this text.\n- `agentic_disabled`: engine agentic is switched off on this server.\n- `agentic_lookup_unavailable`: A source in sources is not available right now.\n- `model_busy`: The language model on local infrastructure cannot take this call right now: its shared queue is full, or no node can run the model (for example while it restarts). Wait Retry-After seconds; a text job waits by itself.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "ner_location_unavailable": {
                    "summary": "ner_location_unavailable",
                    "value": {
                      "error": "ner_location_unavailable",
                      "nerDeployment": "local",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_location_unavailable"
                    }
                  },
                  "ner_model_unconfigured": {
                    "summary": "ner_model_unconfigured",
                    "value": {
                      "error": "ner_model_unconfigured",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unconfigured"
                    }
                  },
                  "local_model_unavailable": {
                    "summary": "local_model_unavailable",
                    "value": {
                      "error": "local_model_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/local_model_unavailable"
                    }
                  },
                  "redact_runtime_unavailable": {
                    "summary": "redact_runtime_unavailable",
                    "value": {
                      "error": "redact_runtime_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_runtime_unavailable"
                    }
                  },
                  "semantic_runtime_not_configured": {
                    "summary": "semantic_runtime_not_configured",
                    "value": {
                      "error": "semantic_runtime_not_configured",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/semantic_runtime_not_configured"
                    }
                  },
                  "detector_unavailable": {
                    "summary": "detector_unavailable",
                    "value": {
                      "error": "detector_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/detector_unavailable"
                    }
                  },
                  "detection_service_unavailable": {
                    "summary": "detection_service_unavailable",
                    "value": {
                      "error": "detection_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/detection_service_unavailable"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  },
                  "invalid_organisation_policy": {
                    "summary": "invalid_organisation_policy",
                    "value": {
                      "error": "invalid_organisation_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_organisation_policy"
                    }
                  },
                  "chat_cloud_unavailable": {
                    "summary": "chat_cloud_unavailable",
                    "value": {
                      "error": "chat_cloud_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/chat_cloud_unavailable"
                    }
                  },
                  "gateway_misconfigured": {
                    "summary": "gateway_misconfigured",
                    "value": {
                      "error": "gateway_misconfigured",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/gateway_misconfigured"
                    }
                  },
                  "incoherent_redaction_policy": {
                    "summary": "incoherent_redaction_policy",
                    "value": {
                      "error": "incoherent_redaction_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/incoherent_redaction_policy"
                    }
                  },
                  "agentic_disabled": {
                    "summary": "agentic_disabled",
                    "value": {
                      "error": "agentic_disabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_disabled"
                    }
                  },
                  "agentic_lookup_unavailable": {
                    "summary": "agentic_lookup_unavailable",
                    "value": {
                      "error": "agentic_lookup_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_lookup_unavailable"
                    }
                  },
                  "model_busy": {
                    "summary": "model_busy",
                    "value": {
                      "error": "model_busy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_busy"
                    }
                  }
                }
              }
            }
          },
          "504": {
            "description": "The call timed out.\n\n- `processing_timeout`: The call took more than 300 seconds, or the client disconnected. Send long texts as text jobs.\n- `model_stalled`: The language model stopped answering: its connection was silent, or it ran past the time its answer needs at the model's measured speed. No partial result is returned.\n- `model_queue_expired`: The language model's shared queue on local infrastructure did not reach this call before its deadline. Try again later, or send it as a text job, which waits for its turn.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "processing_timeout": {
                    "summary": "processing_timeout",
                    "value": {
                      "error": "processing_timeout",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_timeout"
                    }
                  },
                  "model_stalled": {
                    "summary": "model_stalled",
                    "value": {
                      "error": "model_stalled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_stalled"
                    }
                  },
                  "model_queue_expired": {
                    "summary": "model_queue_expired",
                    "value": {
                      "error": "model_queue_expired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_queue_expired"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/redact/detect": {
      "post": {
        "operationId": "detectPersonalData",
        "summary": "Find personal data in a text",
        "description": "Returns what regex and NER find, with UTF-16 offsets into your text, without changing it. At most 16,000 UTF-16 code units and 100,000 bytes per call; split longer texts or use POST /v1/redact. Rules run on komplai's servers in Germany (EU), and NER at the place you choose with nerDeployment or, without one, at the default place GET /v1/access marks (it lists the places). No language model runs here. Your organisation's own redaction rules apply, as on POST /v1/redact: a rule's match is returned with type and source custom and the rule's label in customLabel, and a finding that an allow rule covers is left out.",
        "tags": [
          "Redact"
        ],
        "x-komplai-stability": "stable",
        "security": [
          {
            "organisationKey": []
          },
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "redact:detect"
        ],
        "x-komplai-principals": [
          "person",
          "integration"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 16000,
                    "description": "The text to search: at least one character, at most 16,000 UTF-16 code units."
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "regex",
                      "ner"
                    ],
                    "default": "ner",
                    "description": "ner (default): rules and NER. regex: rules only. Your organisation's own redaction rules apply in both."
                  },
                  "semantic": {
                    "type": "boolean",
                    "enum": [
                      false
                    ],
                    "default": false,
                    "description": "Leave out or send false. This operation runs no language model: true answers 400 unsupported_pilot_mode."
                  },
                  "nerDeployment": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "eu-de",
                      "local",
                      null
                    ],
                    "default": null,
                    "description": "Where the ner layer runs: eu-de (komplai's servers in Germany, EU) or local (komplai's local infrastructure in Denmark). Leave it out, or send null, for the default place: the one GET /v1/access marks with default true in the ner layer's places (services[].hosting.processing.layers), eu-de unless komplai has changed it. Each place there has available and a reason for the operations with detectors, and detect with the same for POST /v1/redact/detect and the operations without detectors, so check that the place you name is available for the operation you call. A place that cannot be used now answers 503 ner_location_unavailable and names the place in nerDeployment; NER never moves to another place. Needs ner in detectors (400 ner_deployment_without_ner); any other value answers 400 invalid_ner_deployment. The response names the place in configuration.nerDeployment (nerDeployment on POST /v1/redact/detect)."
                  },
                  "nerModel": {
                    "type": "string",
                    "nullable": true,
                    "default": null,
                    "description": "The NER model, by id from the ner layer's models in GET /v1/access. Leave it out, or send null, for the default model. An unknown id answers 400 ner_model_unknown, and a retired one or one that does not run at the place you name 400 ner_model_unavailable; a preview model needs your organisation's Redact preview, which an organisation integration key does not have (403 ner_model_preview_required). POST /v1/redact/detect, and the text operations without detectors, run only the default model (400 ner_model_unavailable for another). A model that is not set up at the default place on this server answers 503 ner_model_unconfigured; NER never runs another model. Needs ner in detectors (400 ner_model_without_ner). The response names the model in configuration.nerModel (nerModel on POST /v1/redact/detect)."
                  }
                }
              },
              "examples": {
                "nameAndPhone": {
                  "summary": "A name and a phone number",
                  "value": {
                    "text": "Ring til Mette Kjærgaard Holm på 21 34 56 78.",
                    "mode": "ner"
                  }
                },
                "nerPlace": {
                  "summary": "The same, with the NER place named",
                  "description": "nerDeployment eu-de runs NER on komplai's servers in Germany. Leave it out for the default place; GET /v1/access lists the places and whether each is available.",
                  "value": {
                    "text": "Ring til Mette Kjærgaard Holm på 21 34 56 78.",
                    "mode": "ner",
                    "nerDeployment": "eu-de"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What regex and NER found. The text itself is not changed or returned. When you named the NER place or model, nerDeployment and nerModel name what ran.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "findings",
                    "truncated",
                    "degraded"
                  ],
                  "properties": {
                    "findings": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true,
                        "required": [
                          "start",
                          "end",
                          "type",
                          "value",
                          "confidence",
                          "source"
                        ],
                        "properties": {
                          "start": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          },
                          "value": {
                            "type": "string"
                          },
                          "confidence": {
                            "type": "string",
                            "enum": [
                              "high",
                              "medium",
                              "low"
                            ]
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "custom"
                            ],
                            "description": "The layer that found it: regex (the rules), ner, or custom (your organisation's own redaction rules). Always present."
                          }
                        }
                      }
                    },
                    "truncated": {
                      "type": "boolean",
                      "enum": [
                        false
                      ]
                    },
                    "degraded": {
                      "type": "boolean",
                      "enum": [
                        false
                      ]
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "When you chose a place: the place NER ran."
                    },
                    "nerModel": {
                      "type": "string",
                      "description": "When you chose a model: the NER model that ran."
                    }
                  }
                },
                "examples": {
                  "nameAndPhone": {
                    "summary": "A name and a phone number",
                    "value": {
                      "findings": [
                        {
                          "start": 9,
                          "end": 29,
                          "type": "person",
                          "value": "Mette Kjærgaard Holm",
                          "confidence": "high",
                          "source": "ner"
                        },
                        {
                          "start": 33,
                          "end": 44,
                          "type": "phone",
                          "value": "21 34 56 78",
                          "confidence": "medium",
                          "source": "regex"
                        }
                      ],
                      "truncated": false,
                      "degraded": false
                    }
                  },
                  "nerPlace": {
                    "summary": "The same, with the NER place named",
                    "value": {
                      "findings": [
                        {
                          "start": 9,
                          "end": 29,
                          "type": "person",
                          "value": "Mette Kjærgaard Holm",
                          "confidence": "high",
                          "source": "ner"
                        },
                        {
                          "start": 33,
                          "end": 44,
                          "type": "phone",
                          "value": "21 34 56 78",
                          "confidence": "medium",
                          "source": "regex"
                        }
                      ],
                      "truncated": false,
                      "degraded": false,
                      "nerDeployment": "eu-de"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `invalid_body`: The body is not a JSON object with the documented fields: invalid JSON, Content-Type text/plain, an unknown field, a field of the wrong type, or a required field that is missing or blank.\n- `unsupported_pilot_mode`: mode is not regex or ner, or semantic is true. No language model runs on this operation.\n- `invalid_ner_deployment`: nerDeployment is not eu-de or local.\n- `ner_deployment_without_ner`: nerDeployment was sent, but ner is not in detectors (or mode is regex).\n- `ner_model_without_ner`: nerModel was sent, but ner is not in detectors (or mode is regex).\n- `ner_model_unknown`: No NER model has the id in nerModel. GET /v1/access lists the models you may choose.\n- `ner_model_unavailable`: The NER model in nerModel is retired or does not run at the chosen place. A model other than the default also needs detectors.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_body": {
                    "summary": "invalid_body",
                    "value": {
                      "error": "invalid_body",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_body"
                    }
                  },
                  "unsupported_pilot_mode": {
                    "summary": "unsupported_pilot_mode",
                    "value": {
                      "error": "unsupported_pilot_mode",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_pilot_mode"
                    }
                  },
                  "invalid_ner_deployment": {
                    "summary": "invalid_ner_deployment",
                    "value": {
                      "error": "invalid_ner_deployment",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_ner_deployment"
                    }
                  },
                  "ner_deployment_without_ner": {
                    "summary": "ner_deployment_without_ner",
                    "value": {
                      "error": "ner_deployment_without_ner",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_deployment_without_ner"
                    }
                  },
                  "ner_model_without_ner": {
                    "summary": "ner_model_without_ner",
                    "value": {
                      "error": "ner_model_without_ner",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_without_ner"
                    }
                  },
                  "ner_model_unknown": {
                    "summary": "ner_model_unknown",
                    "value": {
                      "error": "ner_model_unknown",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unknown"
                    }
                  },
                  "ner_model_unavailable": {
                    "summary": "ner_model_unavailable",
                    "value": {
                      "error": "ner_model_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unavailable"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not issued with it, the person behind a personal key is no longer assigned Redact, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `processing_choice_not_allowed`: Your organisation does not allow this processing (local or eu-cloud) through the API. NER on local infrastructure is local processing, also when llm runs on eu-cloud; that refusal names the place in nerDeployment.\n- `processing_not_enabled`: processing eu-cloud was requested, but EU processing is not enabled for Redact in your organisation, or its agreement keeps processing local.\n- `processing_not_approved`: The organisation's processing approval for Redact was withdrawn.\n- `app_not_assigned`: The person behind a personal key lost Redact or their membership while the request was admitted. A person who is no longer assigned Redact normally gets 401 invalid_api_key or 403 insufficient_scope instead.\n- `ner_model_preview_required`: The NER model in nerModel is a preview. It needs your organisation's Redact preview, which an organisation integration key does not have.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "processing_choice_not_allowed": {
                    "summary": "processing_choice_not_allowed",
                    "value": {
                      "error": "processing_choice_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_choice_not_allowed"
                    }
                  },
                  "processing_not_enabled": {
                    "summary": "processing_not_enabled",
                    "value": {
                      "error": "processing_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_enabled"
                    }
                  },
                  "processing_not_approved": {
                    "summary": "processing_not_approved",
                    "value": {
                      "error": "processing_not_approved",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_approved"
                    }
                  },
                  "app_not_assigned": {
                    "summary": "app_not_assigned",
                    "value": {
                      "error": "app_not_assigned",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/app_not_assigned"
                    }
                  },
                  "ner_model_preview_required": {
                    "summary": "ner_model_preview_required",
                    "value": {
                      "error": "ner_model_preview_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_preview_required"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Too large.\n\n- `exceeds_ner_window`: text is over 16,000 UTF-16 code units, or the body is over 100,000 bytes. Use POST /v1/redact for longer texts.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "exceeds_ner_window": {
                    "summary": "exceeds_ner_window",
                    "value": {
                      "error": "exceeds_ner_window",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/exceeds_ner_window"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.\n\n- `invalid_body`: The body has no Content-Type, or one the API does not read, such as XML or form data. text/plain is read and answers 400 invalid_body.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_body": {
                    "summary": "invalid_body",
                    "value": {
                      "error": "invalid_body",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_body"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "A processing service failed. No partial result is returned.\n\n- `detection_service_unavailable`: The detection service could not be reached, failed or timed out.\n- `invalid_detection`: A detection layer returned output that failed validation. For llm: an answer that still could not be used after its part of the text was asked again in halves down to 1,500 characters.\n- `incomplete_detection`: A detection layer did not finish the whole text. No partial result is returned.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "detection_service_unavailable": {
                    "summary": "detection_service_unavailable",
                    "value": {
                      "error": "detection_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/detection_service_unavailable"
                    }
                  },
                  "invalid_detection": {
                    "summary": "invalid_detection",
                    "value": {
                      "error": "invalid_detection",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_detection"
                    }
                  },
                  "incomplete_detection": {
                    "summary": "incomplete_detection",
                    "value": {
                      "error": "incomplete_detection",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/incomplete_detection"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `ner_location_unavailable`: The NER place cannot be used now: komplai has not switched it on for the API, or it did not answer. nerDeployment names the place. NER never moves to another place; GET /v1/access shows which places are available.\n- `gateway_misconfigured`: The connection to the detection service is not set up.\n- `redact_runtime_unavailable`: The server's default NER place is not readable.\n- `access_service_unavailable`: Keys and access could not be checked.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "ner_location_unavailable": {
                    "summary": "ner_location_unavailable",
                    "value": {
                      "error": "ner_location_unavailable",
                      "nerDeployment": "local",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_location_unavailable"
                    }
                  },
                  "gateway_misconfigured": {
                    "summary": "gateway_misconfigured",
                    "value": {
                      "error": "gateway_misconfigured",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/gateway_misconfigured"
                    }
                  },
                  "redact_runtime_unavailable": {
                    "summary": "redact_runtime_unavailable",
                    "value": {
                      "error": "redact_runtime_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_runtime_unavailable"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/redact/restore": {
      "post": {
        "operationId": "restoreRedactText",
        "summary": "Restore the originals in a text",
        "description": "Puts the originals back into text with labels such as [PERSON-1], for example an answer from your own model or workflow step. Send either the mapping from POST /v1/redact (asked for with include [\"mapping\"]) or the id of your own finished text job; with a job, komplai reads its retained result and you do not hold the mapping. Labels match regardless of case and spaces inside the brackets. Labels the mapping does not contain stay unchanged and are listed in unknownPlaceholders. Fixed replacement tokens from your organisation's redaction rules can stand for several originals, so they are never restored; those in the text are listed in fixedTokens. No model runs and the call has no AI charge; it counts against your request rate. The response contains originals: protect it as personal information.",
        "tags": [
          "Redact"
        ],
        "x-komplai-stability": "stable",
        "security": [
          {
            "organisationKey": []
          },
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "redact:process"
        ],
        "x-komplai-principals": [
          "person",
          "integration"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "description": "Send text and exactly one of mapping and job.",
                "oneOf": [
                  {
                    "title": "With mapping",
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "text",
                      "mapping"
                    ],
                    "properties": {
                      "text": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 750000,
                        "description": "Text with labels such as [PERSON-1], for example an answer from your own model. Non-blank valid Unicode without NUL; the limit counts UTF-16 code units."
                      },
                      "mapping": {
                        "type": "object",
                        "additionalProperties": {
                          "type": "string",
                          "minLength": 1
                        },
                        "description": "Label to original, as POST /v1/redact returns it with include [\"mapping\"]; send it unchanged. Keys in the exact [LABEL-N] form are restored. Any other key is a fixed replacement token from your organisation's redaction rules, such as [SAGSNR]: it can stand for several originals, so it is never restored and is listed in fixedTokens when the text contains it. Keys hold at most 128 UTF-16 code units without surrounding spaces, with at most 500 fixed tokens. Keys and values together may hold 2,000,000 UTF-16 code units. Send either mapping or job. At most 100,000 entries."
                      }
                    }
                  },
                  {
                    "title": "With job",
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "text",
                      "job"
                    ],
                    "properties": {
                      "text": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 750000,
                        "description": "Text with labels such as [PERSON-1], for example an answer from your own model. Non-blank valid Unicode without NUL; the limit counts UTF-16 code units."
                      },
                      "job": {
                        "type": "string",
                        "format": "uuid",
                        "description": "Id of your own finished job from POST /v1/redact/jobs, submitted with include [\"mapping\"] or [\"changes\"]. komplai reads the retained result, so you do not hold the mapping. Send either job or mapping."
                      }
                    }
                  }
                ]
              },
              "examples": {
                "withMapping": {
                  "summary": "With the mapping from POST /v1/redact",
                  "description": "mapping as POST /v1/redact returned it with include [\"mapping\"], sent unchanged.",
                  "value": {
                    "text": "Kære [PERSON-1]. Vi har modtaget din ansøgning og svarer på [EMAIL-1].",
                    "mapping": {
                      "[PERSON-1]": "Mette Kjærgaard Holm",
                      "[CPR-1]": "120385-1234",
                      "[ADRESSE-1]": "Fiktivvej 12, 8000 Aarhus C",
                      "[TLF-1]": "21 34 56 78",
                      "[EMAIL-1]": "mette.holm@example.invalid"
                    }
                  }
                },
                "withJob": {
                  "summary": "With your own finished text job",
                  "description": "The job was submitted with include [\"mapping\"] or [\"changes\"]; komplai reads its retained result, so you do not hold the mapping.",
                  "value": {
                    "text": "Kære [PERSON-1]. Vi har modtaget din ansøgning og svarer på [EMAIL-1].",
                    "job": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The text with the originals put back. It contains personal data; protect it like the source document.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "text",
                    "restored",
                    "unknownPlaceholders"
                  ],
                  "properties": {
                    "text": {
                      "type": "string",
                      "description": "The text with originals put back. It contains personal information; protect it like the source document."
                    },
                    "restored": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Number of label occurrences replaced."
                    },
                    "unknownPlaceholders": {
                      "type": "array",
                      "uniqueItems": true,
                      "items": {
                        "type": "string"
                      },
                      "description": "Labels found in the text that the mapping does not contain, in [LABEL-N] form and sorted. They are left unchanged. A label that does not start with a letter, such as [2026_SAG-2], is listed only when the mapping uses that label, so ranges such as [1-3] are not."
                    },
                    "ambiguousPlaceholders": {
                      "type": "array",
                      "uniqueItems": true,
                      "items": {
                        "type": "string"
                      },
                      "description": "Only for a job kept with changes and without mapping: restored labels that stood for more than one spelling in the source. The first spelling was used."
                    },
                    "fixedTokens": {
                      "type": "array",
                      "uniqueItems": true,
                      "items": {
                        "type": "string"
                      },
                      "description": "Fixed replacement tokens from the mapping or job, such as [SAGSNR], that occur in the text you sent. A fixed token can stand for several originals, so it is never restored and stays unchanged. Sorted; present only when not empty."
                    }
                  }
                },
                "examples": {
                  "restored": {
                    "summary": "Both labels restored",
                    "value": {
                      "text": "Kære Mette Kjærgaard Holm. Vi har modtaget din ansøgning og svarer på mette.holm@example.invalid.",
                      "restored": 2,
                      "unknownPlaceholders": []
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `invalid_body`: The body is not a JSON object with the documented fields: invalid JSON, Content-Type text/plain, an unknown field, a field of the wrong type, or a required field that is missing or blank.\n- `invalid_text_encoding`: text contains NUL or a lone UTF-16 surrogate.\n- `invalid_restore_source`: Send exactly one of mapping and job.\n- `invalid_job`: job is not a UUID.\n- `invalid_mapping`: mapping is not an object, or a value is empty, not a string, or contains NUL or a lone surrogate.\n- `invalid_placeholder`: A mapping key is empty, longer than 128 UTF-16 code units, has surrounding spaces, contains NUL or a lone surrogate, or is `__proto__`, `constructor` or `prototype`.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_body": {
                    "summary": "invalid_body",
                    "value": {
                      "error": "invalid_body",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_body"
                    }
                  },
                  "invalid_text_encoding": {
                    "summary": "invalid_text_encoding",
                    "value": {
                      "error": "invalid_text_encoding",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_text_encoding"
                    }
                  },
                  "invalid_restore_source": {
                    "summary": "invalid_restore_source",
                    "value": {
                      "error": "invalid_restore_source",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_restore_source"
                    }
                  },
                  "invalid_job": {
                    "summary": "invalid_job",
                    "value": {
                      "error": "invalid_job",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_job"
                    }
                  },
                  "invalid_mapping": {
                    "summary": "invalid_mapping",
                    "value": {
                      "error": "invalid_mapping",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_mapping"
                    }
                  },
                  "invalid_placeholder": {
                    "summary": "invalid_placeholder",
                    "value": {
                      "error": "invalid_placeholder",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_placeholder"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not issued with it, the person behind a personal key is no longer assigned Redact, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `processing_access_withdrawn`: Access to the job was withdrawn after it was created: the agreement, the processing setup, the organisation's redaction rules or the storage policy for text jobs changed. The job is invalidated and its text and result are deleted. This is final: stop reading the job and do not reuse its Idempotency-Key; submit the text again as a new job when access allows it.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "processing_access_withdrawn": {
                    "summary": "processing_access_withdrawn",
                    "value": {
                      "error": "processing_access_withdrawn",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_access_withdrawn"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.\n\n- `not_found`: No job with this id for this key. A job is visible only to the key that created it, and it disappears when it expires.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "error": "not_found",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/not_found"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict with the current state.\n\n- `job_not_finished`: The job is still queued or running; state says which. Try again after Retry-After seconds.\n- `mapping_not_available`: The job kept neither mapping nor changes; hint says how to submit it again.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "With job_not_finished: seconds to wait before trying again.",
                "schema": {
                  "type": "integer",
                  "enum": [
                    2
                  ]
                },
                "example": 2
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string"
                    },
                    "state": {
                      "type": "string"
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "job_not_finished": {
                    "summary": "job_not_finished",
                    "value": {
                      "error": "job_not_finished",
                      "state": "running",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/job_not_finished"
                    }
                  },
                  "mapping_not_available": {
                    "summary": "mapping_not_available",
                    "value": {
                      "error": "mapping_not_available",
                      "hint": "The job kept neither mapping nor changes. Submit it again with include [\"mapping\"] or [\"changes\"] to restore from it.",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/mapping_not_available"
                    }
                  }
                }
              }
            }
          },
          "410": {
            "description": "The job ended without a result. error is the job's code (see GET /v1/redact/jobs/{id}) and state its state.\n\n- `redact_job_cancelled`: The job was deleted or cancelled.\n- `redact_job_failed`: The job failed; GET /v1/redact/jobs/{id} lists the codes a failed job can carry.\n- `redact_job_unavailable`: The job ended without a result and without a stored reason.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string"
                    },
                    "state": {
                      "type": "string"
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "redact_job_cancelled": {
                    "summary": "redact_job_cancelled",
                    "value": {
                      "error": "redact_job_cancelled",
                      "state": "cancelled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_cancelled"
                    }
                  },
                  "redact_job_failed": {
                    "summary": "redact_job_failed",
                    "value": {
                      "error": "redact_job_failed",
                      "state": "failed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_failed"
                    }
                  },
                  "redact_job_unavailable": {
                    "summary": "redact_job_unavailable",
                    "value": {
                      "error": "redact_job_unavailable",
                      "state": "invalidated",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_unavailable"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Too large.\n\n- `text_too_large`: text is over 750,000 UTF-16 code units.\n- `restore_too_large`: The body, the mapping (more than 100,000 entries, more than 2,000,000 UTF-16 code units or more than 500 fixed tokens) or the restored text (over 8,000,000 UTF-16 code units) is too large.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "text_too_large": {
                    "summary": "text_too_large",
                    "value": {
                      "error": "text_too_large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/text_too_large"
                    }
                  },
                  "restore_too_large": {
                    "summary": "restore_too_large",
                    "value": {
                      "error": "restore_too_large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/restore_too_large"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.\n\n- `invalid_body`: The body has no Content-Type, or one the API does not read, such as XML or form data. text/plain is read and answers 400 invalid_body.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_body": {
                    "summary": "invalid_body",
                    "value": {
                      "error": "invalid_body",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_body"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `redact_job_integrity_failed`: The job's encrypted data could not be read.\n- `redact_job_service_unavailable`: The job store failed.\n- `redact_restore_unavailable`: Unexpected failure while restoring.\n- `invalid_organisation_policy`: Your organisation's redaction rules cannot be applied (more than 500 rules, or an invalid rule).\n- `access_service_unavailable`: Keys and access could not be checked.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "redact_job_integrity_failed": {
                    "summary": "redact_job_integrity_failed",
                    "value": {
                      "error": "redact_job_integrity_failed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_integrity_failed"
                    }
                  },
                  "redact_job_service_unavailable": {
                    "summary": "redact_job_service_unavailable",
                    "value": {
                      "error": "redact_job_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_service_unavailable"
                    }
                  },
                  "redact_restore_unavailable": {
                    "summary": "redact_restore_unavailable",
                    "value": {
                      "error": "redact_restore_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_restore_unavailable"
                    }
                  },
                  "invalid_organisation_policy": {
                    "summary": "invalid_organisation_policy",
                    "value": {
                      "error": "invalid_organisation_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_organisation_policy"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/redact/jobs": {
      "post": {
        "operationId": "submitRedactJob",
        "summary": "Submit a text job",
        "description": "Stores the text encrypted and pseudonymises it in the background with the same options as POST /v1/redact, up to 2,000,000 UTF-16 code units (about 800 standard pages of 2,400 characters). Use it for long documents, also below the 300-second limit of a synchronous call. The answer is 202 with the job's id, and Location points to its status. Poll GET /v1/redact/jobs/{id}, at most every few seconds because reads count against the request limit, then read GET /v1/redact/jobs/{id}/result. Ask for include mapping or changes if you want to restore from the job with POST /v1/redact/restore. Personal data needs an approved storage policy for text jobs; the result stays encrypted until you delete the job or it expires, and the retention period counts from when the job ends. A job has no time limit of its own while it makes progress: it waits for the shared queue, every model call has a budget from the model's measured speed, and a job that makes no progress for 65 minutes fails with redact_job_stalled. A job belongs to the key that created it. A NER place or local language model the job names is checked against the settings when it is submitted, and whether it answers when the job runs; one that does not answer then fails the job with its code. A job that is interrupted (a restart or a lost worker) resumes from its encrypted checkpoint and sends at most the step that was in flight again: a call to the language model on local infrastructure gets its answer back from the queue when the queue finished it, and runs once more when the interruption cut it off, a call to the EU cloud is sent again once and counted towards the monthly AI spending cap like any call, and a register lookup is sent again; each is logged. After three interruptions the job ends with redact_job_attempts_exhausted. engine agentic (preview) requires the organisation administrator's opt-in (403 when absent) and processing local, and reads up to the same 2,000,000 UTF-16 code units in windows; external sources require explicit per-run selection and current administrator permission.\n\nOrganisation integration keys cannot use engine agentic: 403 integration_not_permitted.",
        "tags": [
          "Redact"
        ],
        "x-komplai-stability": "stable",
        "security": [
          {
            "organisationKey": []
          },
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "redact:process"
        ],
        "x-komplai-principals": [
          "person",
          "integration"
        ],
        "parameters": [
          {
            "in": "header",
            "name": "idempotency-key",
            "required": false,
            "description": "Optional. A new UUID for each job, in upper or lower case. If the connection drops, send the same request again with the same key: within the job's lifetime the same key and body return the same job (reused true) instead of a second one, and the same key with another body answers 409 idempotency_conflict. Never reuse a key for another text. The key is bound to your organisation, the owner (person or integration) and the API key. The default {{$guid}} is Postman's variable for a new UUID on each request; in other tools, generate a UUID for each job or leave the header out.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "default": "{{$guid}}"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000000,
                    "description": "The exact text to pseudonymise: non-blank, valid Unicode, without NUL. At most 2,000,000 UTF-16 code units, as in JavaScript and .NET; every offset in the response counts the same way."
                  },
                  "detectors": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 3,
                    "uniqueItems": true,
                    "default": [
                      "regex",
                      "ner"
                    ],
                    "items": {
                      "type": "string",
                      "enum": [
                        "regex",
                        "ner",
                        "llm"
                      ]
                    },
                    "description": "The layers to run, in any non-empty combination. regex: rules for CPR numbers, phone numbers, e-mail addresses and more. ner: a model for names, organisations and addresses. llm: a language model for what rules and NER miss, such as health data in free text; it can take up to a minute. It reads the text in parts of at most 12,000 characters. When its answer for a part cannot be used, that answer is discarded and the part is asked again in two overlapping halves, down to halves of 1,500 characters: at most 14 extra calls for a part. If an answer still cannot be used, the call fails with 502 invalid_detection or incomplete_detection and returns no result. Default regex and ner. The older field mode is still accepted but is not listed here: leave it out, because if you send both, they must agree (400 unsupported_engine_mode). engine agentic needs regex and llm, with optional ner."
                  },
                  "processing": {
                    "type": "string",
                    "enum": [
                      "local",
                      "eu-cloud"
                    ],
                    "default": "local",
                    "description": "Where the language model (llm) runs. local (default): komplai's local infrastructure in Denmark, with no AI charge. eu-cloud: komplai's EU cloud, which answers faster. It needs llm in detectors and EU processing approved for your organisation, and its usage counts towards the organisation's monthly AI spending cap (429 ai_credit_exhausted when the cap is reached). Every call to the model, including a part asked again in halves, is checked against the cap and counted on its own. Name the model in model, or leave it out for the default model there. Rules always run on komplai's servers in Germany (EU), and NER at the place in nerDeployment or, without one, at the default place GET /v1/access marks. GET /v1/access names where each layer processes text for your organisation, including eu-cloud, in services[].hosting.processing.layers. The API never switches between local and eu-cloud by itself."
                  },
                  "nerDeployment": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "eu-de",
                      "local",
                      null
                    ],
                    "default": null,
                    "description": "Where the ner layer runs: eu-de (komplai's servers in Germany, EU) or local (komplai's local infrastructure in Denmark). Leave it out, or send null, for the default place: the one GET /v1/access marks with default true in the ner layer's places (services[].hosting.processing.layers), eu-de unless komplai has changed it. Each place there has available and a reason for the operations with detectors, and detect with the same for POST /v1/redact/detect and the operations without detectors, so check that the place you name is available for the operation you call. A place that cannot be used now answers 503 ner_location_unavailable and names the place in nerDeployment; NER never moves to another place. Needs ner in detectors (400 ner_deployment_without_ner); any other value answers 400 invalid_ner_deployment. The response names the place in configuration.nerDeployment (nerDeployment on POST /v1/redact/detect)."
                  },
                  "nerModel": {
                    "type": "string",
                    "nullable": true,
                    "default": null,
                    "description": "The NER model, by id from the ner layer's models in GET /v1/access. Leave it out, or send null, for the default model. An unknown id answers 400 ner_model_unknown, and a retired one or one that does not run at the place you name 400 ner_model_unavailable; a preview model needs your organisation's Redact preview, which an organisation integration key does not have (403 ner_model_preview_required). POST /v1/redact/detect, and the text operations without detectors, run only the default model (400 ner_model_unavailable for another). A model that is not set up at the default place on this server answers 503 ner_model_unconfigured; NER never runs another model. Needs ner in detectors (400 ner_model_without_ner). The response names the model in configuration.nerModel (nerModel on POST /v1/redact/detect)."
                  },
                  "model": {
                    "type": "string",
                    "nullable": true,
                    "default": null,
                    "description": "The language model for llm, by id from GET /v1/access: the models of the llm layer for the processing you choose. Leave it out, or send null, for the default model there. Only models validated for Redact at that processing are accepted (400 llm_model_not_validated): qwen3.8-27b-q8 for local and mistral-small-3.2-24b-instruct-2506 for eu-cloud. GET /v1/access also lists models with reason not_validated, which cannot be chosen. A local model that local infrastructure does not offer right now answers 503 local_model_unavailable, and an EU model your organisation's EU route does not have answers 409 model_not_enabled. Without model, the default must be validated too: on local the server's default model, on eu-cloud the EU model the person behind a personal key saved, else the model on your organisation's EU route (409 llm_model_not_validated). The API never picks another model. Needs llm in detectors and engine pipeline (400 llm_model_not_applicable). configuration.llmModel names the model that ran."
                  },
                  "include": {
                    "type": "array",
                    "maxItems": 4,
                    "uniqueItems": true,
                    "default": [],
                    "items": {
                      "type": "string",
                      "enum": [
                        "mapping",
                        "findings",
                        "changes",
                        "skipped"
                      ]
                    },
                    "description": "Extra reports. Each contains originals: protect the response as personal information. mapping: label to original, for POST /v1/redact/restore. changes: exact positions in your text and in redacted. findings: everything the layers found. skipped: CPR-shaped numbers the regex layer left in redacted because their digits do not decode to a plausible birth date, with the reason. A skipped number can still identify a person, for example an erstatningspersonnummer with day + 60. skipped is filled only when regex runs."
                  },
                  "policy": {
                    "type": "string",
                    "enum": [
                      "all-detected"
                    ],
                    "default": "all-detected",
                    "description": "Always all-detected: everything found is replaced, under your organisation's redaction and allow rules."
                  },
                  "engine": {
                    "type": "string",
                    "enum": [
                      "pipeline",
                      "agentic"
                    ],
                    "default": "pipeline",
                    "description": "Preview. Leave out. pipeline (default) runs the chosen detectors. agentic is an experimental method for organisations whose administrator has turned on the Redact Agentic preview (403 agentic_preview_not_enabled otherwise); an organisation integration key gets 403 integration_not_permitted. It needs processing local and detectors regex and llm, with optional ner. It reads the text in windows, so it takes the operation's own text limit; a long document belongs in a text job.",
                    "x-komplai-stability": "preview"
                  },
                  "sources": {
                    "type": "array",
                    "maxItems": 5,
                    "uniqueItems": true,
                    "default": [],
                    "items": {
                      "type": "string",
                      "enum": [
                        "cvr-official",
                        "bbr",
                        "web",
                        "dar",
                        "wikidata-local"
                      ]
                    },
                    "description": "Preview. Leave out. Lookup sources for engine agentic, chosen for each call. Each needs the administrator's approval and configured access; an unapproved or unavailable source refuses the call (403 agentic_lookup_not_allowed or 503 agentic_lookup_unavailable) and is never silently dropped. Default none.",
                    "x-komplai-stability": "preview"
                  }
                }
              },
              "examples": {
                "rulesAndNer": {
                  "summary": "Rules and NER, with mapping and changes",
                  "description": "The same body as POST /v1/redact. Ask for mapping or changes if you want to restore from the job later.",
                  "value": {
                    "text": "Borgeren Mette Kjærgaard Holm, cpr-nr. 120385-1234, bor på Fiktivvej 12, 8000 Aarhus C. Hun kan kontaktes på 21 34 56 78 eller mette.holm@example.invalid.",
                    "detectors": [
                      "regex",
                      "ner"
                    ],
                    "include": [
                      "mapping",
                      "changes"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The job is accepted. Location points to its status; poll it, then read the result.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Location": {
                "description": "The job's status: /v1/redact/jobs/{id}.",
                "schema": {
                  "type": "string"
                },
                "example": "/v1/redact/jobs/3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "state",
                    "createdAt",
                    "expiresAt",
                    "progress"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "state": {
                      "type": "string",
                      "enum": [
                        "queued",
                        "running",
                        "succeeded",
                        "failed",
                        "cancelled",
                        "invalidated"
                      ]
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Application retention deadline. A queued job expires this long after it was submitted; a running job is kept for as long as its worker is alive; when the job ends, the retention period counts from the end. Results remain encrypted until explicit deletion or expiry; reads do not consume them."
                    },
                    "progress": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "completed",
                        "total"
                      ],
                      "properties": {
                        "completed": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    },
                    "error": {
                      "type": "string"
                    },
                    "reused": {
                      "type": "boolean"
                    }
                  }
                },
                "examples": {
                  "submitted": {
                    "summary": "A new job",
                    "value": {
                      "id": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c",
                      "state": "queued",
                      "createdAt": "2026-10-20T08:00:00.000Z",
                      "expiresAt": "2026-10-20T09:00:00.000Z",
                      "progress": {
                        "completed": 0,
                        "total": 0
                      },
                      "reused": false
                    }
                  },
                  "replayed": {
                    "summary": "The same Idempotency-Key and body again",
                    "value": {
                      "id": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c",
                      "state": "succeeded",
                      "createdAt": "2026-10-20T08:00:00.000Z",
                      "expiresAt": "2026-10-20T09:00:00.000Z",
                      "progress": {
                        "completed": 1,
                        "total": 1
                      },
                      "reused": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `invalid_body`: The body is not a JSON object with the documented fields: invalid JSON, Content-Type text/plain, an unknown field, a field of the wrong type, or a required field that is missing or blank.\n- `invalid_text_encoding`: text contains NUL or a lone UTF-16 surrogate.\n- `unsupported_redact_engine`: engine is not one of the documented values.\n- `engine_retired`: engine review, the separate LLM engine, is retired. Send engine pipeline, or leave engine out, with detectors [\"regex\", \"llm\"]; hint says the same. Nothing was reserved or processed.\n- `unsupported_redact_mode`: mode is not regex, ner or semantic.\n- `invalid_detectors`: detectors is empty, has an unknown or repeated value, or more than three values.\n- `unsupported_engine_mode`: mode disagrees with detectors. Leave mode out when you send detectors.\n- `invalid_processing`: processing is not local or eu-cloud.\n- `unsupported_processing`: processing eu-cloud needs llm in detectors (and engine pipeline). Add llm to detectors.\n- `unsupported_redact_policy`: policy is not all-detected.\n- `invalid_include`: include has an unknown or repeated value.\n- `invalid_ner_deployment`: nerDeployment is not eu-de or local.\n- `ner_deployment_without_ner`: nerDeployment was sent, but ner is not in detectors (or mode is regex).\n- `ner_model_without_ner`: nerModel was sent, but ner is not in detectors (or mode is regex).\n- `ner_model_unknown`: No NER model has the id in nerModel. GET /v1/access lists the models you may choose.\n- `ner_model_unavailable`: The NER model in nerModel is retired or does not run at the chosen place. A model other than the default also needs detectors.\n- `llm_model_not_applicable`: model was sent, but llm is not in detectors, or engine is not pipeline.\n- `llm_model_not_validated`: The language model in model is not validated for Redact at the processing you chose. GET /v1/access lists the models you may name.\n- `unsupported_engine_detectors`: The detectors do not fit engine agentic: it needs regex and llm, with optional ner.\n- `invalid_sources`: sources has an unknown or repeated value.\n- `unsupported_engine_sources`: sources was sent without engine agentic.\n- `invalid_idempotency_key`: Idempotency-Key is not a UUID, or it was sent more than once.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_body": {
                    "summary": "invalid_body",
                    "value": {
                      "error": "invalid_body",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_body"
                    }
                  },
                  "invalid_text_encoding": {
                    "summary": "invalid_text_encoding",
                    "value": {
                      "error": "invalid_text_encoding",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_text_encoding"
                    }
                  },
                  "unsupported_redact_engine": {
                    "summary": "unsupported_redact_engine",
                    "value": {
                      "error": "unsupported_redact_engine",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_engine"
                    }
                  },
                  "engine_retired": {
                    "summary": "engine_retired",
                    "value": {
                      "error": "engine_retired",
                      "hint": "engine review is retired. Send engine pipeline, or leave engine out, with detectors [\"regex\", \"llm\"].",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/engine_retired"
                    }
                  },
                  "unsupported_redact_mode": {
                    "summary": "unsupported_redact_mode",
                    "value": {
                      "error": "unsupported_redact_mode",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_mode"
                    }
                  },
                  "invalid_detectors": {
                    "summary": "invalid_detectors",
                    "value": {
                      "error": "invalid_detectors",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_detectors"
                    }
                  },
                  "unsupported_engine_mode": {
                    "summary": "unsupported_engine_mode",
                    "value": {
                      "error": "unsupported_engine_mode",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_mode"
                    }
                  },
                  "invalid_processing": {
                    "summary": "invalid_processing",
                    "value": {
                      "error": "invalid_processing",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_processing"
                    }
                  },
                  "unsupported_processing": {
                    "summary": "unsupported_processing",
                    "value": {
                      "error": "unsupported_processing",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_processing"
                    }
                  },
                  "unsupported_redact_policy": {
                    "summary": "unsupported_redact_policy",
                    "value": {
                      "error": "unsupported_redact_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_policy"
                    }
                  },
                  "invalid_include": {
                    "summary": "invalid_include",
                    "value": {
                      "error": "invalid_include",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_include"
                    }
                  },
                  "invalid_ner_deployment": {
                    "summary": "invalid_ner_deployment",
                    "value": {
                      "error": "invalid_ner_deployment",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_ner_deployment"
                    }
                  },
                  "ner_deployment_without_ner": {
                    "summary": "ner_deployment_without_ner",
                    "value": {
                      "error": "ner_deployment_without_ner",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_deployment_without_ner"
                    }
                  },
                  "ner_model_without_ner": {
                    "summary": "ner_model_without_ner",
                    "value": {
                      "error": "ner_model_without_ner",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_without_ner"
                    }
                  },
                  "ner_model_unknown": {
                    "summary": "ner_model_unknown",
                    "value": {
                      "error": "ner_model_unknown",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unknown"
                    }
                  },
                  "ner_model_unavailable": {
                    "summary": "ner_model_unavailable",
                    "value": {
                      "error": "ner_model_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unavailable"
                    }
                  },
                  "llm_model_not_applicable": {
                    "summary": "llm_model_not_applicable",
                    "value": {
                      "error": "llm_model_not_applicable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_applicable"
                    }
                  },
                  "llm_model_not_validated": {
                    "summary": "llm_model_not_validated",
                    "value": {
                      "error": "llm_model_not_validated",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_validated"
                    }
                  },
                  "unsupported_engine_detectors": {
                    "summary": "unsupported_engine_detectors",
                    "value": {
                      "error": "unsupported_engine_detectors",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_detectors"
                    }
                  },
                  "invalid_sources": {
                    "summary": "invalid_sources",
                    "value": {
                      "error": "invalid_sources",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_sources"
                    }
                  },
                  "unsupported_engine_sources": {
                    "summary": "unsupported_engine_sources",
                    "value": {
                      "error": "unsupported_engine_sources",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_sources"
                    }
                  },
                  "invalid_idempotency_key": {
                    "summary": "invalid_idempotency_key",
                    "value": {
                      "error": "invalid_idempotency_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_idempotency_key"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not issued with it, the person behind a personal key is no longer assigned Redact, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `approved_redact_storage_policy_required`: Your organisation processes personal data but has no approved storage policy for text jobs.\n- `processing_access_withdrawn`: Access to the job was withdrawn after it was created: the agreement, the processing setup, the organisation's redaction rules or the storage policy for text jobs changed. The job is invalidated and its text and result are deleted. This is final: stop reading the job and do not reuse its Idempotency-Key; submit the text again as a new job when access allows it.\n- `processing_choice_not_allowed`: Your organisation does not allow this processing (local or eu-cloud) through the API. NER on local infrastructure is local processing, also when llm runs on eu-cloud; that refusal names the place in nerDeployment.\n- `processing_not_enabled`: processing eu-cloud was requested, but EU processing is not enabled for Redact in your organisation, or its agreement keeps processing local.\n- `processing_not_approved`: The organisation's processing approval for Redact was withdrawn.\n- `app_not_assigned`: The person behind a personal key lost Redact or their membership while the request was admitted. A person who is no longer assigned Redact normally gets 401 invalid_api_key or 403 insufficient_scope instead.\n- `ner_model_preview_required`: The NER model in nerModel is a preview. It needs your organisation's Redact preview, which an organisation integration key does not have.\n- `integration_not_permitted`: An organisation integration key sent engine agentic.\n- `agentic_preview_not_enabled`: The organisation's administrator has not turned on the Redact Agentic preview.\n- `agentic_lookup_not_allowed`: A source in sources is not approved by the organisation's administrator.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "approved_redact_storage_policy_required": {
                    "summary": "approved_redact_storage_policy_required",
                    "value": {
                      "error": "approved_redact_storage_policy_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/approved_redact_storage_policy_required"
                    }
                  },
                  "processing_access_withdrawn": {
                    "summary": "processing_access_withdrawn",
                    "value": {
                      "error": "processing_access_withdrawn",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_access_withdrawn"
                    }
                  },
                  "processing_choice_not_allowed": {
                    "summary": "processing_choice_not_allowed",
                    "value": {
                      "error": "processing_choice_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_choice_not_allowed"
                    }
                  },
                  "processing_not_enabled": {
                    "summary": "processing_not_enabled",
                    "value": {
                      "error": "processing_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_enabled"
                    }
                  },
                  "processing_not_approved": {
                    "summary": "processing_not_approved",
                    "value": {
                      "error": "processing_not_approved",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_approved"
                    }
                  },
                  "app_not_assigned": {
                    "summary": "app_not_assigned",
                    "value": {
                      "error": "app_not_assigned",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/app_not_assigned"
                    }
                  },
                  "ner_model_preview_required": {
                    "summary": "ner_model_preview_required",
                    "value": {
                      "error": "ner_model_preview_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_preview_required"
                    }
                  },
                  "integration_not_permitted": {
                    "summary": "integration_not_permitted",
                    "value": {
                      "error": "integration_not_permitted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/integration_not_permitted"
                    }
                  },
                  "agentic_preview_not_enabled": {
                    "summary": "agentic_preview_not_enabled",
                    "value": {
                      "error": "agentic_preview_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_preview_not_enabled"
                    }
                  },
                  "agentic_lookup_not_allowed": {
                    "summary": "agentic_lookup_not_allowed",
                    "value": {
                      "error": "agentic_lookup_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_lookup_not_allowed"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict with the current state.\n\n- `idempotency_conflict`: This Idempotency-Key was used with a different body. Use a new UUID for a new job.\n- `processing_access_changed`: The processing setup changed between admission and submission. Send it again.\n- `model_not_enabled`: The EU cloud model set up for your organisation is not available, or the EU model you named in model is not on your organisation's EU route.\n- `llm_model_not_validated`: No model was named, and the default model where llm runs is not validated for Redact: the local language model this server uses by default or, with eu-cloud, the EU model the person behind a personal key saved, else the model on your organisation's EU route. Name a model from GET /v1/access.\n- `reasoning_effort_not_supported`: The reasoning effort komplai set for this EU language model in Redact is not one the model accepts on your organisation's EU route. Nothing was sent to the model, and the API does not send another effort.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "idempotency_conflict": {
                    "summary": "idempotency_conflict",
                    "value": {
                      "error": "idempotency_conflict",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/idempotency_conflict"
                    }
                  },
                  "processing_access_changed": {
                    "summary": "processing_access_changed",
                    "value": {
                      "error": "processing_access_changed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_access_changed"
                    }
                  },
                  "model_not_enabled": {
                    "summary": "model_not_enabled",
                    "value": {
                      "error": "model_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_not_enabled"
                    }
                  },
                  "llm_model_not_validated": {
                    "summary": "llm_model_not_validated",
                    "value": {
                      "error": "llm_model_not_validated",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_validated"
                    }
                  },
                  "reasoning_effort_not_supported": {
                    "summary": "reasoning_effort_not_supported",
                    "value": {
                      "error": "reasoning_effort_not_supported",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/reasoning_effort_not_supported"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Too large.\n\n- `text_too_large`: text is over 2,000,000 UTF-16 code units.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "text_too_large": {
                    "summary": "text_too_large",
                    "value": {
                      "error": "text_too_large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/text_too_large"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.\n\n- `invalid_body`: The body has no Content-Type, or one the API does not read, such as XML or form data. text/plain is read and answers 400 invalid_body.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_body": {
                    "summary": "invalid_body",
                    "value": {
                      "error": "invalid_body",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_body"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `redact_jobs_not_enabled`: Text jobs are not accepted right now; GET /v1/access shows text_contract.jobs_accepting_submissions.\n- `redact_job_service_unavailable`: The job store failed.\n- `redact_job_integrity_failed`: The job's encrypted data could not be read.\n- `invalid_organisation_policy`: Your organisation's redaction rules cannot be applied (more than 500 rules, or an invalid rule).\n- `chat_cloud_unavailable`: The organisation's EU cloud setup cannot be used right now.\n- `access_service_unavailable`: Keys and access could not be checked.\n- `ner_location_unavailable`: The NER place the job names is not set up here. Whether a set-up place answers is checked when the job runs; one that does not then fails the job with this code.\n- `ner_model_unconfigured`: The NER model in nerModel is not set up at the default place on this server.\n- `redact_runtime_unavailable`: The server's default NER place is not readable.\n- `agentic_disabled`: engine agentic is switched off on this server.\n- `agentic_lookup_unavailable`: A source in sources is not available right now.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "redact_jobs_not_enabled": {
                    "summary": "redact_jobs_not_enabled",
                    "value": {
                      "error": "redact_jobs_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_jobs_not_enabled"
                    }
                  },
                  "redact_job_service_unavailable": {
                    "summary": "redact_job_service_unavailable",
                    "value": {
                      "error": "redact_job_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_service_unavailable"
                    }
                  },
                  "redact_job_integrity_failed": {
                    "summary": "redact_job_integrity_failed",
                    "value": {
                      "error": "redact_job_integrity_failed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_integrity_failed"
                    }
                  },
                  "invalid_organisation_policy": {
                    "summary": "invalid_organisation_policy",
                    "value": {
                      "error": "invalid_organisation_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_organisation_policy"
                    }
                  },
                  "chat_cloud_unavailable": {
                    "summary": "chat_cloud_unavailable",
                    "value": {
                      "error": "chat_cloud_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/chat_cloud_unavailable"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  },
                  "ner_location_unavailable": {
                    "summary": "ner_location_unavailable",
                    "value": {
                      "error": "ner_location_unavailable",
                      "nerDeployment": "local",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_location_unavailable"
                    }
                  },
                  "ner_model_unconfigured": {
                    "summary": "ner_model_unconfigured",
                    "value": {
                      "error": "ner_model_unconfigured",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unconfigured"
                    }
                  },
                  "redact_runtime_unavailable": {
                    "summary": "redact_runtime_unavailable",
                    "value": {
                      "error": "redact_runtime_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_runtime_unavailable"
                    }
                  },
                  "agentic_disabled": {
                    "summary": "agentic_disabled",
                    "value": {
                      "error": "agentic_disabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_disabled"
                    }
                  },
                  "agentic_lookup_unavailable": {
                    "summary": "agentic_lookup_unavailable",
                    "value": {
                      "error": "agentic_lookup_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_lookup_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/redact/jobs/{id}": {
      "get": {
        "operationId": "getRedactJob",
        "summary": "Read the status of a text job",
        "description": "state is queued, running, succeeded, failed, cancelled or invalidated. Read the result once it is succeeded. A failed, cancelled or invalidated job is final and carries error with the reason. Wait at least 2 seconds between reads: each read counts against the request limit.",
        "tags": [
          "Redact"
        ],
        "x-komplai-stability": "stable",
        "security": [
          {
            "organisationKey": []
          },
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "redact:process"
        ],
        "x-komplai-principals": [
          "person",
          "integration"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The job's id from POST /v1/redact/jobs.",
            "example": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c"
          }
        ],
        "responses": {
          "200": {
            "description": "The job's status. A failed, cancelled or invalidated job carries error, for example redact_job_cancelled, processing_access_withdrawn, processing_outcome_uncertain, redact_job_attempts_exhausted, redact_job_stalled, redact_job_progress_failed, engine_retired, model_stalled, model_queue_expired, context_limit, agent_no_progress, rate_limited, ai_credit_exhausted, person_ai_credit_exhausted, redact_job_integrity_failed, incomplete_detection, invalid_detection, detection_service_unavailable, result_too_large, processing_complexity_exceeded, redact_runtime_unavailable, ner_location_unavailable, ner_model_unknown, ner_model_unavailable, ner_model_preview_required, ner_model_unconfigured, llm_model_not_validated, local_model_unavailable, reasoning_effort_not_supported, processing_choice_not_allowed, redact_job_failed. When access to the job was withdrawn, the read answers 403 processing_access_withdrawn instead; that is final too.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "state",
                    "createdAt",
                    "expiresAt",
                    "progress"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "state": {
                      "type": "string",
                      "enum": [
                        "queued",
                        "running",
                        "succeeded",
                        "failed",
                        "cancelled",
                        "invalidated"
                      ]
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Application retention deadline. A queued job expires this long after it was submitted; a running job is kept for as long as its worker is alive; when the job ends, the retention period counts from the end. Results remain encrypted until explicit deletion or expiry; reads do not consume them."
                    },
                    "progress": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "completed",
                        "total"
                      ],
                      "properties": {
                        "completed": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    },
                    "error": {
                      "type": "string"
                    },
                    "reused": {
                      "type": "boolean"
                    }
                  }
                },
                "examples": {
                  "queued": {
                    "summary": "Queued",
                    "value": {
                      "id": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c",
                      "state": "queued",
                      "createdAt": "2026-10-20T08:00:00.000Z",
                      "expiresAt": "2026-10-20T09:00:00.000Z",
                      "progress": {
                        "completed": 0,
                        "total": 0
                      }
                    }
                  },
                  "running": {
                    "summary": "Running",
                    "value": {
                      "id": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c",
                      "state": "running",
                      "createdAt": "2026-10-20T08:00:00.000Z",
                      "expiresAt": "2026-10-20T09:00:00.000Z",
                      "progress": {
                        "completed": 0,
                        "total": 1
                      }
                    }
                  },
                  "succeeded": {
                    "summary": "Succeeded: read the result",
                    "value": {
                      "id": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c",
                      "state": "succeeded",
                      "createdAt": "2026-10-20T08:00:00.000Z",
                      "expiresAt": "2026-10-20T09:00:00.000Z",
                      "progress": {
                        "completed": 1,
                        "total": 1
                      }
                    }
                  },
                  "failed": {
                    "summary": "Failed",
                    "value": {
                      "id": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c",
                      "state": "failed",
                      "createdAt": "2026-10-20T08:00:00.000Z",
                      "expiresAt": "2026-10-20T09:00:00.000Z",
                      "progress": {
                        "completed": 0,
                        "total": 1
                      },
                      "error": "detection_service_unavailable"
                    }
                  },
                  "invalidated": {
                    "summary": "Invalidated",
                    "value": {
                      "id": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c",
                      "state": "invalidated",
                      "createdAt": "2026-10-20T08:00:00.000Z",
                      "expiresAt": "2026-10-20T09:00:00.000Z",
                      "progress": {
                        "completed": 0,
                        "total": 1
                      },
                      "error": "redact_job_attempts_exhausted"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `Bad Request`: The id in the path is not a UUID.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "bad_request": {
                    "summary": "Bad Request",
                    "value": {
                      "error": "Bad Request",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/bad-request"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not issued with it, the person behind a personal key is no longer assigned Redact, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `processing_access_withdrawn`: Access to the job was withdrawn after it was created: the agreement, the processing setup, the organisation's redaction rules or the storage policy for text jobs changed. The job is invalidated and its text and result are deleted. This is final: stop reading the job and do not reuse its Idempotency-Key; submit the text again as a new job when access allows it.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "processing_access_withdrawn": {
                    "summary": "processing_access_withdrawn",
                    "value": {
                      "error": "processing_access_withdrawn",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_access_withdrawn"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.\n\n- `not_found`: No job with this id for this key. A job is visible only to the key that created it, and it disappears when it expires.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "error": "not_found",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `redact_job_service_unavailable`: The job store failed.\n- `invalid_organisation_policy`: Your organisation's redaction rules cannot be applied (more than 500 rules, or an invalid rule).\n- `access_service_unavailable`: Keys and access could not be checked.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "redact_job_service_unavailable": {
                    "summary": "redact_job_service_unavailable",
                    "value": {
                      "error": "redact_job_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_service_unavailable"
                    }
                  },
                  "invalid_organisation_policy": {
                    "summary": "invalid_organisation_policy",
                    "value": {
                      "error": "invalid_organisation_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_organisation_policy"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteRedactJob",
        "summary": "Delete a text job",
        "description": "Cancels the job if it is still queued or running and deletes its encrypted text and result at once. Cancellation is final. Only the encrypted replay fingerprint for Idempotency-Key remains until the original expiry. Deleting a job that is already cancelled answers the same 200. Send no body. Deleting does not count against the request limit.",
        "tags": [
          "Redact"
        ],
        "x-komplai-stability": "stable",
        "security": [
          {
            "organisationKey": []
          },
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "redact:process"
        ],
        "x-komplai-principals": [
          "person",
          "integration"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The job's id from POST /v1/redact/jobs.",
            "example": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c"
          }
        ],
        "responses": {
          "200": {
            "description": "The job is cancelled and its text and result are deleted.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "state",
                    "createdAt",
                    "expiresAt",
                    "progress"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "state": {
                      "type": "string",
                      "enum": [
                        "queued",
                        "running",
                        "succeeded",
                        "failed",
                        "cancelled",
                        "invalidated"
                      ]
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Application retention deadline. A queued job expires this long after it was submitted; a running job is kept for as long as its worker is alive; when the job ends, the retention period counts from the end. Results remain encrypted until explicit deletion or expiry; reads do not consume them."
                    },
                    "progress": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "completed",
                        "total"
                      ],
                      "properties": {
                        "completed": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    },
                    "error": {
                      "type": "string"
                    },
                    "reused": {
                      "type": "boolean"
                    }
                  }
                },
                "examples": {
                  "cancelled": {
                    "summary": "Cancelled and deleted",
                    "value": {
                      "id": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c",
                      "state": "cancelled",
                      "createdAt": "2026-10-20T08:00:00.000Z",
                      "expiresAt": "2026-10-20T09:00:00.000Z",
                      "progress": {
                        "completed": 0,
                        "total": 1
                      },
                      "error": "redact_job_cancelled"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `Bad Request`: The id in the path is not a UUID.\n- `invalid_input`: The request has a JSON Content-Type but an empty or invalid body. Send DELETE without a body.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "bad_request": {
                    "summary": "Bad Request",
                    "value": {
                      "error": "Bad Request",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/bad-request"
                    }
                  },
                  "invalid_input": {
                    "summary": "invalid_input",
                    "value": {
                      "error": "invalid_input",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_input"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not issued with it, the person behind a personal key is no longer assigned Redact, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `processing_access_withdrawn`: Access to the job was withdrawn after it was created: the agreement, the processing setup, the organisation's redaction rules or the storage policy for text jobs changed. The job is invalidated and its text and result are deleted. This is final: stop reading the job and do not reuse its Idempotency-Key; submit the text again as a new job when access allows it.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "processing_access_withdrawn": {
                    "summary": "processing_access_withdrawn",
                    "value": {
                      "error": "processing_access_withdrawn",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_access_withdrawn"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.\n\n- `not_found`: No job with this id for this key. A job is visible only to the key that created it, and it disappears when it expires.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "error": "not_found",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: The per-IP network limit. Deleting does not count against the request limit.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `redact_job_service_unavailable`: The job store failed.\n- `invalid_organisation_policy`: Your organisation's redaction rules cannot be applied (more than 500 rules, or an invalid rule).\n- `access_service_unavailable`: Keys and access could not be checked.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "redact_job_service_unavailable": {
                    "summary": "redact_job_service_unavailable",
                    "value": {
                      "error": "redact_job_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_service_unavailable"
                    }
                  },
                  "invalid_organisation_policy": {
                    "summary": "invalid_organisation_policy",
                    "value": {
                      "error": "invalid_organisation_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_organisation_policy"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/redact/jobs/{id}/result": {
      "get": {
        "operationId": "getRedactJobResult",
        "summary": "Read the result of a text job",
        "description": "200 with the same result as POST /v1/redact once the job has succeeded. 202 with the job's status while it is queued or running; wait Retry-After seconds. 410 when it failed, was cancelled or was invalidated. Reading does not delete the encrypted result: DELETE removes text and result at once, and expiry removes the job and its replay fingerprint.",
        "tags": [
          "Redact"
        ],
        "x-komplai-stability": "stable",
        "security": [
          {
            "organisationKey": []
          },
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "redact:process"
        ],
        "x-komplai-principals": [
          "person",
          "integration"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The job's id from POST /v1/redact/jobs.",
            "example": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c"
          }
        ],
        "responses": {
          "200": {
            "description": "The job's result, the same as POST /v1/redact returns for its body. Reading it does not delete it.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "redacted",
                    "mode",
                    "engine",
                    "processing",
                    "policy",
                    "policyVersion",
                    "offsetEncoding",
                    "charsIn",
                    "charsOut",
                    "truncated",
                    "degraded"
                  ],
                  "properties": {
                    "engine": {
                      "type": "string",
                      "enum": [
                        "pipeline",
                        "agentic"
                      ]
                    },
                    "redacted": {
                      "type": "string",
                      "description": "The pseudonymised text, with labels such as [PERSON-1]. The same original gets the same label throughout the text."
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "regex",
                        "ner",
                        "semantic"
                      ]
                    },
                    "processing": {
                      "type": "string",
                      "enum": [
                        "local",
                        "eu-cloud"
                      ],
                      "description": "Where the language model ran, or would have run."
                    },
                    "policy": {
                      "type": "string",
                      "enum": [
                        "all-detected"
                      ]
                    },
                    "policyVersion": {
                      "type": "string",
                      "enum": [
                        "redact-text-v1"
                      ]
                    },
                    "offsetEncoding": {
                      "type": "string",
                      "enum": [
                        "utf-16"
                      ],
                      "description": "Every offset counts UTF-16 code units."
                    },
                    "charsIn": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 2000000,
                      "description": "Length of your text in UTF-16 code units."
                    },
                    "charsOut": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Length of redacted in UTF-16 code units."
                    },
                    "truncated": {
                      "type": "boolean",
                      "enum": [
                        false
                      ],
                      "description": "Always false: a truncated detection fails the call instead."
                    },
                    "degraded": {
                      "type": "boolean",
                      "enum": [
                        false
                      ],
                      "description": "Always false: a degraded detection fails the call instead."
                    },
                    "configuration": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "engine",
                        "mode",
                        "detectors",
                        "sources",
                        "processing"
                      ],
                      "description": "The choices that took effect. llmModel names the language model when it is known, and llmReasoningEffort the reasoning effort it was sent: by the llm layer, and on eu-cloud by mode semantic without detectors too. nerModel and nerDeployment name the NER model and place when you chose them, and nerDeployment also when NER ran at another place than eu-de.",
                      "properties": {
                        "engine": {
                          "type": "string",
                          "enum": [
                            "pipeline",
                            "agentic"
                          ]
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "regex",
                            "ner",
                            "semantic"
                          ]
                        },
                        "detectors": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "llm"
                            ]
                          }
                        },
                        "sources": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "cvr-official",
                              "bbr",
                              "web",
                              "dar",
                              "wikidata-local"
                            ]
                          }
                        },
                        "processing": {
                          "type": "string",
                          "enum": [
                            "local",
                            "eu-cloud"
                          ]
                        },
                        "nerModel": {
                          "type": "string",
                          "description": "The NER model you named in nerModel."
                        },
                        "nerDeployment": {
                          "type": "string",
                          "enum": [
                            "eu-de",
                            "local"
                          ],
                          "description": "Where NER ran: present when you named the place, or when NER ran somewhere other than eu-de."
                        },
                        "llmModel": {
                          "type": "string",
                          "description": "The language model that answered, when it is known."
                        },
                        "llmReasoningEffort": {
                          "type": "string",
                          "enum": [
                            "none",
                            "low",
                            "medium",
                            "high",
                            "xhigh",
                            "max"
                          ],
                          "description": "The reasoning effort the language model was sent: by the llm layer of the pipeline engine, and on eu-cloud by mode semantic without detectors too. komplai sets it per model when it validates the model for Redact; none means no reasoning."
                        }
                      }
                    },
                    "execution": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "completed",
                        "detectorsRun"
                      ],
                      "description": "What actually ran: detectorsRun lists the layers, and the counts appear when they were measured. A missing count does not mean zero. llmOccurrenceRepairs counts llm findings whose quote occurs once in the text but whose occurrence number pointed past it; each was placed on that one match. llmResolvedFindings counts llm findings whose quote could not be placed exactly and that were placed on every span equal to the quote once spacing and line breaks, invisible characters, typographic quotes and dashes, and for quotes of two or more words or with a digit, letter case are ignored; each is an exact span of the text. llmWindows counts the language model's answers the result is made of. llmSplits counts answers that could not be used and were discarded; each made two more calls, one for each half of its part of the text.",
                      "properties": {
                        "completed": {
                          "type": "boolean",
                          "enum": [
                            true
                          ]
                        },
                        "detectorsRun": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "llm"
                            ]
                          }
                        },
                        "detectionPasses": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "nerCalls": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmCalls": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmOccurrenceRepairs": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmResolvedFindings": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmWindows": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmSplits": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "toolCalls": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "rounds": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "documentCoverageComplete": {
                          "type": "boolean"
                        },
                        "releaseReviewComplete": {
                          "type": "boolean"
                        }
                      }
                    },
                    "mapping": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "Label to canonical original mention. Use changes for exact source reconstruction."
                    },
                    "entries": {
                      "type": "array",
                      "description": "Returned with mapping: one entry per label, with its type, original, confidence and source layer.",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "placeholder",
                          "type",
                          "value",
                          "confidence",
                          "source"
                        ],
                        "properties": {
                          "placeholder": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          },
                          "value": {
                            "type": "string"
                          },
                          "confidence": {
                            "type": "string",
                            "enum": [
                              "high",
                              "medium",
                              "low"
                            ]
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "llm",
                              "custom"
                            ],
                            "description": "The layer that found it: regex (the rules), ner, llm (the language model) or custom (your organisation's own redaction rules). Always present."
                          }
                        }
                      }
                    },
                    "findings": {
                      "type": "array",
                      "description": "Everything the layers found, with offsets in your text and the layer that found each in source.",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "start",
                          "end",
                          "type",
                          "value",
                          "confidence",
                          "source"
                        ],
                        "properties": {
                          "start": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          },
                          "value": {
                            "type": "string"
                          },
                          "confidence": {
                            "type": "string",
                            "enum": [
                              "high",
                              "medium",
                              "low"
                            ]
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "llm",
                              "custom"
                            ],
                            "description": "The layer that found it: regex (the rules), ner, llm (the language model) or custom (your organisation's own redaction rules). Always present."
                          },
                          "customLabel": {
                            "type": "string",
                            "maxLength": 64
                          },
                          "replacementToken": {
                            "type": "string",
                            "maxLength": 128
                          },
                          "entityId": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "changes": {
                      "type": "array",
                      "description": "Each replacement with its offsets in your text (start, end) and in redacted (outputStart, outputEnd). Apply them in reverse order of outputStart to rebuild your exact text.",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "start",
                          "end",
                          "outputStart",
                          "outputEnd",
                          "original",
                          "replacement",
                          "type"
                        ],
                        "properties": {
                          "start": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "outputStart": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "outputEnd": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "original": {
                            "type": "string"
                          },
                          "replacement": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          }
                        }
                      }
                    },
                    "skipped": {
                      "type": "array",
                      "description": "CPR-shaped numbers the regex layer declined and no final change covers, with a reason code and an English explanation. They remain in redacted and can still identify a person, for example an erstatningspersonnummer with day + 60 (reported as cpr_invalid_date). Empty when the regex layer did not run (NER- or LLM-only detectors, or engine agentic).",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "start",
                          "end",
                          "value",
                          "type",
                          "reason",
                          "detail"
                        ],
                        "properties": {
                          "start": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "value": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          },
                          "reason": {
                            "type": "string",
                            "enum": [
                              "cpr_unknown_century",
                              "cpr_birth_year_out_of_range",
                              "cpr_invalid_date"
                            ]
                          },
                          "detail": {
                            "type": "string",
                            "maxLength": 200
                          }
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "rulesAndNer": {
                    "summary": "Rules and NER, with mapping and changes",
                    "value": {
                      "redacted": "Borgeren [PERSON-1], cpr-nr. [CPR-1], bor på [ADRESSE-1]. Hun kan kontaktes på [TLF-1] eller [EMAIL-1].",
                      "engine": "pipeline",
                      "mode": "ner",
                      "processing": "local",
                      "policy": "all-detected",
                      "policyVersion": "redact-text-v1",
                      "offsetEncoding": "utf-16",
                      "charsIn": 154,
                      "charsOut": 103,
                      "truncated": false,
                      "degraded": false,
                      "configuration": {
                        "engine": "pipeline",
                        "mode": "ner",
                        "detectors": [
                          "regex",
                          "ner"
                        ],
                        "sources": [],
                        "processing": "local"
                      },
                      "execution": {
                        "completed": true,
                        "detectorsRun": [
                          "regex",
                          "ner"
                        ],
                        "detectionPasses": 1,
                        "nerCalls": 1,
                        "llmCalls": 0
                      },
                      "mapping": {
                        "[PERSON-1]": "Mette Kjærgaard Holm",
                        "[CPR-1]": "120385-1234",
                        "[ADRESSE-1]": "Fiktivvej 12, 8000 Aarhus C",
                        "[TLF-1]": "21 34 56 78",
                        "[EMAIL-1]": "mette.holm@example.invalid"
                      },
                      "entries": [
                        {
                          "placeholder": "[PERSON-1]",
                          "type": "person",
                          "value": "Mette Kjærgaard Holm",
                          "confidence": "medium",
                          "source": "ner"
                        },
                        {
                          "placeholder": "[CPR-1]",
                          "type": "cpr",
                          "value": "120385-1234",
                          "confidence": "high",
                          "source": "regex"
                        },
                        {
                          "placeholder": "[ADRESSE-1]",
                          "type": "address",
                          "value": "Fiktivvej 12, 8000 Aarhus C",
                          "confidence": "high",
                          "source": "ner"
                        },
                        {
                          "placeholder": "[TLF-1]",
                          "type": "phone",
                          "value": "21 34 56 78",
                          "confidence": "medium",
                          "source": "regex"
                        },
                        {
                          "placeholder": "[EMAIL-1]",
                          "type": "email",
                          "value": "mette.holm@example.invalid",
                          "confidence": "high",
                          "source": "regex"
                        }
                      ],
                      "changes": [
                        {
                          "start": 9,
                          "end": 29,
                          "outputStart": 9,
                          "outputEnd": 19,
                          "original": "Mette Kjærgaard Holm",
                          "replacement": "[PERSON-1]",
                          "type": "person"
                        },
                        {
                          "start": 39,
                          "end": 50,
                          "outputStart": 29,
                          "outputEnd": 36,
                          "original": "120385-1234",
                          "replacement": "[CPR-1]",
                          "type": "cpr"
                        },
                        {
                          "start": 59,
                          "end": 86,
                          "outputStart": 45,
                          "outputEnd": 56,
                          "original": "Fiktivvej 12, 8000 Aarhus C",
                          "replacement": "[ADRESSE-1]",
                          "type": "address"
                        },
                        {
                          "start": 109,
                          "end": 120,
                          "outputStart": 79,
                          "outputEnd": 86,
                          "original": "21 34 56 78",
                          "replacement": "[TLF-1]",
                          "type": "phone"
                        },
                        {
                          "start": 127,
                          "end": 153,
                          "outputStart": 93,
                          "outputEnd": 102,
                          "original": "mette.holm@example.invalid",
                          "replacement": "[EMAIL-1]",
                          "type": "email"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "The job is still queued or running. The body is its status.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before reading again.",
                "schema": {
                  "type": "integer",
                  "enum": [
                    2
                  ]
                },
                "example": 2
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "state",
                    "createdAt",
                    "expiresAt",
                    "progress"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "state": {
                      "type": "string",
                      "enum": [
                        "queued",
                        "running",
                        "succeeded",
                        "failed",
                        "cancelled",
                        "invalidated"
                      ]
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Application retention deadline. A queued job expires this long after it was submitted; a running job is kept for as long as its worker is alive; when the job ends, the retention period counts from the end. Results remain encrypted until explicit deletion or expiry; reads do not consume them."
                    },
                    "progress": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "completed",
                        "total"
                      ],
                      "properties": {
                        "completed": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    },
                    "error": {
                      "type": "string"
                    },
                    "reused": {
                      "type": "boolean"
                    }
                  }
                },
                "examples": {
                  "running": {
                    "summary": "Still running",
                    "value": {
                      "id": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c",
                      "state": "running",
                      "createdAt": "2026-10-20T08:00:00.000Z",
                      "expiresAt": "2026-10-20T09:00:00.000Z",
                      "progress": {
                        "completed": 0,
                        "total": 1
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `Bad Request`: The id in the path is not a UUID.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "bad_request": {
                    "summary": "Bad Request",
                    "value": {
                      "error": "Bad Request",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/bad-request"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not issued with it, the person behind a personal key is no longer assigned Redact, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `processing_access_withdrawn`: Access to the job was withdrawn after it was created: the agreement, the processing setup, the organisation's redaction rules or the storage policy for text jobs changed. The job is invalidated and its text and result are deleted. This is final: stop reading the job and do not reuse its Idempotency-Key; submit the text again as a new job when access allows it.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "processing_access_withdrawn": {
                    "summary": "processing_access_withdrawn",
                    "value": {
                      "error": "processing_access_withdrawn",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_access_withdrawn"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.\n\n- `not_found`: No job with this id for this key. A job is visible only to the key that created it, and it disappears when it expires.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "not_found": {
                    "summary": "not_found",
                    "value": {
                      "error": "not_found",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/not_found"
                    }
                  }
                }
              }
            }
          },
          "410": {
            "description": "The job ended without a result. error is the job's code (see GET /v1/redact/jobs/{id}) and state its state.\n\n- `redact_job_cancelled`: The job was deleted or cancelled.\n- `redact_job_failed`: The job failed; GET /v1/redact/jobs/{id} lists the codes a failed job can carry.\n- `redact_job_attempts_exhausted`: The job was invalidated, here because it was interrupted three times; GET /v1/redact/jobs/{id} lists the codes an invalidated job can carry.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "state": {
                      "type": "string"
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "redact_job_cancelled": {
                    "summary": "redact_job_cancelled",
                    "value": {
                      "error": "redact_job_cancelled",
                      "state": "cancelled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_cancelled"
                    }
                  },
                  "redact_job_failed": {
                    "summary": "redact_job_failed",
                    "value": {
                      "error": "redact_job_failed",
                      "state": "failed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_failed"
                    }
                  },
                  "redact_job_attempts_exhausted": {
                    "summary": "redact_job_attempts_exhausted",
                    "value": {
                      "error": "redact_job_attempts_exhausted",
                      "state": "invalidated",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_attempts_exhausted"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `redact_job_integrity_failed`: The job's encrypted data could not be read.\n- `redact_job_service_unavailable`: The job store failed.\n- `invalid_organisation_policy`: Your organisation's redaction rules cannot be applied (more than 500 rules, or an invalid rule).\n- `access_service_unavailable`: Keys and access could not be checked.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "redact_job_integrity_failed": {
                    "summary": "redact_job_integrity_failed",
                    "value": {
                      "error": "redact_job_integrity_failed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_integrity_failed"
                    }
                  },
                  "redact_job_service_unavailable": {
                    "summary": "redact_job_service_unavailable",
                    "value": {
                      "error": "redact_job_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_service_unavailable"
                    }
                  },
                  "invalid_organisation_policy": {
                    "summary": "invalid_organisation_policy",
                    "value": {
                      "error": "invalid_organisation_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_organisation_policy"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/redact/file": {
      "post": {
        "operationId": "redactFile",
        "summary": "Pseudonymise the text of a PDF or Word file (beta)",
        "description": "Beta. Send a PDF with a text layer or a Word .docx as base64 in JSON; komplai extracts its text and runs the same Redact processing as for text, with the same options. The response is pseudonymised text, not a redacted file. No OCR runs: a scanned page is refused (422 scanned_pages_not_supported, with the page numbers) unless allowPagesWithoutText is true. The text is extracted on komplai's API servers (EU · Germany) in memory; the file is not stored. File problems: 400 unsupported_file_type, file_type_mismatch, invalid_file_encoding, empty_file; 413 file_too_large, too_many_pages, text_too_large, file_too_complex; 422 invalid_file, encrypted_file, no_text_found, scanned_pages_not_supported; 429 processing_capacity_busy while other files are being read. No method guarantees anonymity; check the result. At most 750,000 UTF-16 code units of extracted text, and work that finishes within 300 seconds: a run whose estimate exceeds that is refused with 413 use_job and its estimate before anything is processed, and one the estimate cannot cover (an EU language model, a wait in the shared queue) can still end with 504 processing_timeout, as on POST /v1/redact. Longer files go to POST /v1/redact/file/jobs, or send this call with Prefer: respond-async to get 202 and a job. pages gives each page's offsets in the extracted text and in redacted. Agentic processing has the same requirements as on POST /v1/redact.\n\nOrganisation integration keys cannot use engine agentic: 403 integration_not_permitted.",
        "tags": [
          "Redact"
        ],
        "x-komplai-stability": "beta",
        "security": [
          {
            "organisationKey": []
          },
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "redact:process"
        ],
        "x-komplai-principals": [
          "person",
          "integration"
        ],
        "parameters": [
          {
            "in": "header",
            "name": "prefer",
            "required": false,
            "description": "Optional. respond-async answers 202 with a text job, exactly as POST /v1/redact/file/jobs, instead of holding the connection. Without it the call never becomes a job.",
            "schema": {
              "type": "string",
              "enum": [
                "respond-async"
              ]
            }
          },
          {
            "in": "header",
            "name": "idempotency-key",
            "required": false,
            "description": "Optional. A new UUID for each job, in upper or lower case. If the connection drops, send the same request again with the same key: within the job's lifetime the same key and body return the same job (reused true) instead of a second one, and the same key with another body answers 409 idempotency_conflict. Never reuse a key for another text. The key is bound to your organisation, the owner (person or integration) and the API key. The default {{$guid}} is Postman's variable for a new UUID on each request; in other tools, generate a UUID for each job or leave the header out.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "default": "{{$guid}}"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "filename",
                      "data"
                    ],
                    "properties": {
                      "filename": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 255,
                        "description": "The file's name. Its extension (.pdf or .docx) must match the content. Used only to check the type; not stored, logged or returned."
                      },
                      "contentType": {
                        "type": "string",
                        "enum": [
                          "application/pdf",
                          "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                          "application/octet-stream"
                        ],
                        "description": "Optional. Must agree with the content when it names PDF or Word.",
                        "default": "application/octet-stream"
                      },
                      "data": {
                        "type": "string",
                        "format": "byte",
                        "description": "The file's bytes, base64 encoded (standard or URL-safe; line breaks are ignored). At most 50 MiB decoded and 2,000 PDF pages. PDF needs a text layer; Word .docx only, not .doc."
                      }
                    }
                  },
                  "detectors": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 3,
                    "uniqueItems": true,
                    "default": [
                      "regex",
                      "ner"
                    ],
                    "items": {
                      "type": "string",
                      "enum": [
                        "regex",
                        "ner",
                        "llm"
                      ]
                    },
                    "description": "The layers to run, in any non-empty combination. regex: rules for CPR numbers, phone numbers, e-mail addresses and more. ner: a model for names, organisations and addresses. llm: a language model for what rules and NER miss, such as health data in free text; it can take up to a minute. It reads the text in parts of at most 12,000 characters. When its answer for a part cannot be used, that answer is discarded and the part is asked again in two overlapping halves, down to halves of 1,500 characters: at most 14 extra calls for a part. If an answer still cannot be used, the call fails with 502 invalid_detection or incomplete_detection and returns no result. Default regex and ner. The older field mode is still accepted but is not listed here: leave it out, because if you send both, they must agree (400 unsupported_engine_mode). engine agentic needs regex and llm, with optional ner."
                  },
                  "processing": {
                    "type": "string",
                    "enum": [
                      "local",
                      "eu-cloud"
                    ],
                    "default": "local",
                    "description": "Where the language model (llm) runs. local (default): komplai's local infrastructure in Denmark, with no AI charge. eu-cloud: komplai's EU cloud, which answers faster. It needs llm in detectors and EU processing approved for your organisation, and its usage counts towards the organisation's monthly AI spending cap (429 ai_credit_exhausted when the cap is reached). Every call to the model, including a part asked again in halves, is checked against the cap and counted on its own. Name the model in model, or leave it out for the default model there. Rules always run on komplai's servers in Germany (EU), and NER at the place in nerDeployment or, without one, at the default place GET /v1/access marks. GET /v1/access names where each layer processes text for your organisation, including eu-cloud, in services[].hosting.processing.layers. The API never switches between local and eu-cloud by itself."
                  },
                  "nerDeployment": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "eu-de",
                      "local",
                      null
                    ],
                    "default": null,
                    "description": "Where the ner layer runs: eu-de (komplai's servers in Germany, EU) or local (komplai's local infrastructure in Denmark). Leave it out, or send null, for the default place: the one GET /v1/access marks with default true in the ner layer's places (services[].hosting.processing.layers), eu-de unless komplai has changed it. Each place there has available and a reason for the operations with detectors, and detect with the same for POST /v1/redact/detect and the operations without detectors, so check that the place you name is available for the operation you call. A place that cannot be used now answers 503 ner_location_unavailable and names the place in nerDeployment; NER never moves to another place. Needs ner in detectors (400 ner_deployment_without_ner); any other value answers 400 invalid_ner_deployment. The response names the place in configuration.nerDeployment (nerDeployment on POST /v1/redact/detect)."
                  },
                  "nerModel": {
                    "type": "string",
                    "nullable": true,
                    "default": null,
                    "description": "The NER model, by id from the ner layer's models in GET /v1/access. Leave it out, or send null, for the default model. An unknown id answers 400 ner_model_unknown, and a retired one or one that does not run at the place you name 400 ner_model_unavailable; a preview model needs your organisation's Redact preview, which an organisation integration key does not have (403 ner_model_preview_required). POST /v1/redact/detect, and the text operations without detectors, run only the default model (400 ner_model_unavailable for another). A model that is not set up at the default place on this server answers 503 ner_model_unconfigured; NER never runs another model. Needs ner in detectors (400 ner_model_without_ner). The response names the model in configuration.nerModel (nerModel on POST /v1/redact/detect)."
                  },
                  "model": {
                    "type": "string",
                    "nullable": true,
                    "default": null,
                    "description": "The language model for llm, by id from GET /v1/access: the models of the llm layer for the processing you choose. Leave it out, or send null, for the default model there. Only models validated for Redact at that processing are accepted (400 llm_model_not_validated): qwen3.8-27b-q8 for local and mistral-small-3.2-24b-instruct-2506 for eu-cloud. GET /v1/access also lists models with reason not_validated, which cannot be chosen. A local model that local infrastructure does not offer right now answers 503 local_model_unavailable, and an EU model your organisation's EU route does not have answers 409 model_not_enabled. Without model, the default must be validated too: on local the server's default model, on eu-cloud the EU model the person behind a personal key saved, else the model on your organisation's EU route (409 llm_model_not_validated). The API never picks another model. Needs llm in detectors and engine pipeline (400 llm_model_not_applicable). configuration.llmModel names the model that ran."
                  },
                  "policy": {
                    "type": "string",
                    "enum": [
                      "all-detected"
                    ],
                    "default": "all-detected",
                    "description": "Always all-detected: everything found is replaced, under your organisation's redaction and allow rules."
                  },
                  "engine": {
                    "type": "string",
                    "enum": [
                      "pipeline",
                      "agentic"
                    ],
                    "default": "pipeline",
                    "description": "Preview. Leave out. pipeline (default) runs the chosen detectors. agentic is an experimental method for organisations whose administrator has turned on the Redact Agentic preview (403 agentic_preview_not_enabled otherwise); an organisation integration key gets 403 integration_not_permitted. It needs processing local and detectors regex and llm, with optional ner. It reads the text in windows, so it takes the operation's own text limit; a long document belongs in a text job.",
                    "x-komplai-stability": "preview"
                  },
                  "sources": {
                    "type": "array",
                    "maxItems": 5,
                    "uniqueItems": true,
                    "default": [],
                    "items": {
                      "type": "string",
                      "enum": [
                        "cvr-official",
                        "bbr",
                        "web",
                        "dar",
                        "wikidata-local"
                      ]
                    },
                    "description": "Preview. Leave out. Lookup sources for engine agentic, chosen for each call. Each needs the administrator's approval and configured access; an unapproved or unavailable source refuses the call (403 agentic_lookup_not_allowed or 503 agentic_lookup_unavailable) and is never silently dropped. Default none.",
                    "x-komplai-stability": "preview"
                  },
                  "include": {
                    "type": "array",
                    "maxItems": 5,
                    "uniqueItems": true,
                    "default": [],
                    "items": {
                      "type": "string",
                      "enum": [
                        "mapping",
                        "findings",
                        "changes",
                        "skipped",
                        "source"
                      ]
                    },
                    "description": "As POST /v1/redact, plus source: the extracted text the offsets refer to. Every report except the bare result contains originals; protect it as personal information."
                  },
                  "allowPagesWithoutText": {
                    "type": "boolean",
                    "default": false,
                    "description": "A PDF page without a text layer (a scan, an image or a blank page) refuses the file with scanned_pages_not_supported, because no OCR runs. true processes the other pages and reports those pages with hasText false."
                  }
                }
              },
              "examples": {
                "pdf": {
                  "summary": "A two-page PDF with rules and NER",
                  "description": "A fictitious PDF with a text layer, base64 encoded.",
                  "value": {
                    "file": {
                      "filename": "afgoerelse.pdf",
                      "contentType": "application/pdf",
                      "data": "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFs0IDAgUiA2IDAgUl0gL0NvdW50IDIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhIC9FbmNvZGluZyAvV2luQW5zaUVuY29kaW5nID4+CmVuZG9iago0IDAgb2JqCjw8IC9UeXBlIC9QYWdlIC9QYXJlbnQgMiAwIFIgL01lZGlhQm94IFswIDAgNTk1IDg0Ml0gL1Jlc291cmNlcyA8PCAvRm9udCA8PCAvRjEgMyAwIFIgPj4gPj4gL0NvbnRlbnRzIDUgMCBSID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggMTMxID4+CnN0cmVhbQpCVCAvRjEgMTIgVGYgNzIgNzIwIFRkIDE2IFRMIChBZmf4cmVsc2UgdGlsIE1ldHRlIEtq5nJnYWFyZCBIb2xtKSBUaiBUKiAoY3ByLW5yLiAxMjAzODUtMTIzNCwgRmlrdGl2dmVqIDEyLCA4MDAwIEFhcmh1cyBDKSBUaiBUKiBFVAplbmRzdHJlYW0KZW5kb2JqCjYgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCA1OTUgODQyXSAvUmVzb3VyY2VzIDw8IC9Gb250IDw8IC9GMSAzIDAgUiA+PiA+PiAvQ29udGVudHMgNyAwIFIgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0xlbmd0aCA5NCA+PgpzdHJlYW0KQlQgL0YxIDEyIFRmIDcyIDcyMCBUZCAxNiBUTCAoVGVsZWZvbiAyMSAzNCA1NiA3OCkgVGogVCogKG1ldHRlLmhvbG1AZXhhbXBsZS5pbnZhbGlkKSBUaiBUKiBFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA4CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNSAwMDAwMCBuIAowMDAwMDAwMDY0IDAwMDAwIG4gCjAwMDAwMDAxMjcgMDAwMDAgbiAKMDAwMDAwMDIyNCAwMDAwMCBuIAowMDAwMDAwMzUwIDAwMDAwIG4gCjAwMDAwMDA1MzIgMDAwMDAgbiAKMDAwMDAwMDY1OCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDggL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjgwMgolJUVPRgo="
                    },
                    "detectors": [
                      "regex",
                      "ner"
                    ],
                    "processing": "local",
                    "include": [
                      "mapping"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The pseudonymised text of the file, with each page's offsets in the extracted text and in redacted.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "redacted",
                    "mode",
                    "engine",
                    "processing",
                    "policy",
                    "policyVersion",
                    "offsetEncoding",
                    "charsIn",
                    "charsOut",
                    "truncated",
                    "degraded",
                    "file",
                    "pages"
                  ],
                  "properties": {
                    "engine": {
                      "type": "string",
                      "enum": [
                        "pipeline",
                        "agentic"
                      ]
                    },
                    "redacted": {
                      "type": "string",
                      "description": "The pseudonymised text, with labels such as [PERSON-1]. The same original gets the same label throughout the text."
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "regex",
                        "ner",
                        "semantic"
                      ]
                    },
                    "processing": {
                      "type": "string",
                      "enum": [
                        "local",
                        "eu-cloud"
                      ],
                      "description": "Where the language model ran, or would have run."
                    },
                    "policy": {
                      "type": "string",
                      "enum": [
                        "all-detected"
                      ]
                    },
                    "policyVersion": {
                      "type": "string",
                      "enum": [
                        "redact-text-v1"
                      ]
                    },
                    "offsetEncoding": {
                      "type": "string",
                      "enum": [
                        "utf-16"
                      ],
                      "description": "Every offset counts UTF-16 code units."
                    },
                    "charsIn": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 2000000,
                      "description": "Length of your text in UTF-16 code units."
                    },
                    "charsOut": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Length of redacted in UTF-16 code units."
                    },
                    "truncated": {
                      "type": "boolean",
                      "enum": [
                        false
                      ],
                      "description": "Always false: a truncated detection fails the call instead."
                    },
                    "degraded": {
                      "type": "boolean",
                      "enum": [
                        false
                      ],
                      "description": "Always false: a degraded detection fails the call instead."
                    },
                    "configuration": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "engine",
                        "mode",
                        "detectors",
                        "sources",
                        "processing"
                      ],
                      "description": "The choices that took effect. llmModel names the language model when it is known, and llmReasoningEffort the reasoning effort it was sent: by the llm layer, and on eu-cloud by mode semantic without detectors too. nerModel and nerDeployment name the NER model and place when you chose them, and nerDeployment also when NER ran at another place than eu-de.",
                      "properties": {
                        "engine": {
                          "type": "string",
                          "enum": [
                            "pipeline",
                            "agentic"
                          ]
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "regex",
                            "ner",
                            "semantic"
                          ]
                        },
                        "detectors": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "llm"
                            ]
                          }
                        },
                        "sources": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "cvr-official",
                              "bbr",
                              "web",
                              "dar",
                              "wikidata-local"
                            ]
                          }
                        },
                        "processing": {
                          "type": "string",
                          "enum": [
                            "local",
                            "eu-cloud"
                          ]
                        },
                        "nerModel": {
                          "type": "string",
                          "description": "The NER model you named in nerModel."
                        },
                        "nerDeployment": {
                          "type": "string",
                          "enum": [
                            "eu-de",
                            "local"
                          ],
                          "description": "Where NER ran: present when you named the place, or when NER ran somewhere other than eu-de."
                        },
                        "llmModel": {
                          "type": "string",
                          "description": "The language model that answered, when it is known."
                        },
                        "llmReasoningEffort": {
                          "type": "string",
                          "enum": [
                            "none",
                            "low",
                            "medium",
                            "high",
                            "xhigh",
                            "max"
                          ],
                          "description": "The reasoning effort the language model was sent: by the llm layer of the pipeline engine, and on eu-cloud by mode semantic without detectors too. komplai sets it per model when it validates the model for Redact; none means no reasoning."
                        }
                      }
                    },
                    "execution": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "completed",
                        "detectorsRun"
                      ],
                      "description": "What actually ran: detectorsRun lists the layers, and the counts appear when they were measured. A missing count does not mean zero. llmOccurrenceRepairs counts llm findings whose quote occurs once in the text but whose occurrence number pointed past it; each was placed on that one match. llmResolvedFindings counts llm findings whose quote could not be placed exactly and that were placed on every span equal to the quote once spacing and line breaks, invisible characters, typographic quotes and dashes, and for quotes of two or more words or with a digit, letter case are ignored; each is an exact span of the text. llmWindows counts the language model's answers the result is made of. llmSplits counts answers that could not be used and were discarded; each made two more calls, one for each half of its part of the text.",
                      "properties": {
                        "completed": {
                          "type": "boolean",
                          "enum": [
                            true
                          ]
                        },
                        "detectorsRun": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "llm"
                            ]
                          }
                        },
                        "detectionPasses": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "nerCalls": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmCalls": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmOccurrenceRepairs": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmResolvedFindings": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmWindows": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "llmSplits": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "toolCalls": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "rounds": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "documentCoverageComplete": {
                          "type": "boolean"
                        },
                        "releaseReviewComplete": {
                          "type": "boolean"
                        }
                      }
                    },
                    "mapping": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "Label to canonical original mention. Use changes for exact source reconstruction."
                    },
                    "entries": {
                      "type": "array",
                      "description": "Returned with mapping: one entry per label, with its type, original, confidence and source layer.",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "placeholder",
                          "type",
                          "value",
                          "confidence",
                          "source"
                        ],
                        "properties": {
                          "placeholder": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          },
                          "value": {
                            "type": "string"
                          },
                          "confidence": {
                            "type": "string",
                            "enum": [
                              "high",
                              "medium",
                              "low"
                            ]
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "llm",
                              "custom"
                            ],
                            "description": "The layer that found it: regex (the rules), ner, llm (the language model) or custom (your organisation's own redaction rules). Always present."
                          }
                        }
                      }
                    },
                    "findings": {
                      "type": "array",
                      "description": "Everything the layers found, with offsets in your text and the layer that found each in source.",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "start",
                          "end",
                          "type",
                          "value",
                          "confidence",
                          "source"
                        ],
                        "properties": {
                          "start": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          },
                          "value": {
                            "type": "string"
                          },
                          "confidence": {
                            "type": "string",
                            "enum": [
                              "high",
                              "medium",
                              "low"
                            ]
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "regex",
                              "ner",
                              "llm",
                              "custom"
                            ],
                            "description": "The layer that found it: regex (the rules), ner, llm (the language model) or custom (your organisation's own redaction rules). Always present."
                          },
                          "customLabel": {
                            "type": "string",
                            "maxLength": 64
                          },
                          "replacementToken": {
                            "type": "string",
                            "maxLength": 128
                          },
                          "entityId": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "changes": {
                      "type": "array",
                      "description": "Each replacement with its offsets in your text (start, end) and in redacted (outputStart, outputEnd). Apply them in reverse order of outputStart to rebuild your exact text.",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "start",
                          "end",
                          "outputStart",
                          "outputEnd",
                          "original",
                          "replacement",
                          "type"
                        ],
                        "properties": {
                          "start": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "outputStart": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "outputEnd": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "original": {
                            "type": "string"
                          },
                          "replacement": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          }
                        }
                      }
                    },
                    "skipped": {
                      "type": "array",
                      "description": "CPR-shaped numbers the regex layer declined and no final change covers, with a reason code and an English explanation. They remain in redacted and can still identify a person, for example an erstatningspersonnummer with day + 60 (reported as cpr_invalid_date). Empty when the regex layer did not run (NER- or LLM-only detectors, or engine agentic).",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "start",
                          "end",
                          "value",
                          "type",
                          "reason",
                          "detail"
                        ],
                        "properties": {
                          "start": {
                            "type": "integer",
                            "minimum": 0
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "value": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "cpr",
                              "cvr",
                              "pno",
                              "phone",
                              "email",
                              "iban",
                              "account",
                              "person",
                              "address",
                              "organisation",
                              "dob",
                              "demographic",
                              "sensitive",
                              "quasi",
                              "health",
                              "criminal",
                              "social",
                              "special",
                              "url",
                              "creditcard",
                              "ip",
                              "jwt",
                              "passport",
                              "driverslicense",
                              "employee_id",
                              "matrikel",
                              "bfe",
                              "ejendomsnummer",
                              "dgu",
                              "custom"
                            ]
                          },
                          "reason": {
                            "type": "string",
                            "enum": [
                              "cpr_unknown_century",
                              "cpr_birth_year_out_of_range",
                              "cpr_invalid_date"
                            ]
                          },
                          "detail": {
                            "type": "string",
                            "maxLength": 200
                          }
                        }
                      }
                    },
                    "file": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "type",
                        "bytes",
                        "pageCount"
                      ],
                      "properties": {
                        "type": {
                          "type": "string",
                          "enum": [
                            "pdf",
                            "docx"
                          ]
                        },
                        "bytes": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "pageCount": {
                          "type": "integer",
                          "minimum": 1
                        }
                      }
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "number",
                          "start",
                          "end",
                          "redactedStart",
                          "redactedEnd",
                          "hasText"
                        ],
                        "properties": {
                          "number": {
                            "type": "integer",
                            "minimum": 1,
                            "description": "PDF page number. For .docx: body parts split at the page breaks Word recorded, then headers, footers, footnotes and endnotes, one part each."
                          },
                          "start": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Start of the page in the extracted text (the source the other offsets refer to)."
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Exclusive end in the extracted text."
                          },
                          "hasText": {
                            "type": "boolean",
                            "description": "false for a page without a text layer, only with allowPagesWithoutText."
                          },
                          "redactedStart": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Start of the page in redacted. A label that spans a page boundary belongs to the earlier page."
                          },
                          "redactedEnd": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Exclusive end in redacted."
                          }
                        }
                      }
                    },
                    "source": {
                      "type": "string",
                      "maxLength": 2000000,
                      "description": "Only with include source: the extracted text, pages separated by a blank line. Contains personal information."
                    }
                  }
                },
                "examples": {
                  "pdf": {
                    "summary": "A two-page PDF with rules and NER",
                    "value": {
                      "redacted": "Afgørelse til [PERSON-1]\ncpr-nr. [CPR-1], [ADRESSE-1]\n\nTelefon [TLF-1]\n[EMAIL-1]",
                      "engine": "pipeline",
                      "mode": "ner",
                      "processing": "local",
                      "policy": "all-detected",
                      "policyVersion": "redact-text-v1",
                      "offsetEncoding": "utf-16",
                      "charsIn": 131,
                      "charsOut": 80,
                      "truncated": false,
                      "degraded": false,
                      "configuration": {
                        "engine": "pipeline",
                        "mode": "ner",
                        "detectors": [
                          "regex",
                          "ner"
                        ],
                        "sources": [],
                        "processing": "local"
                      },
                      "execution": {
                        "completed": true,
                        "detectorsRun": [
                          "regex",
                          "ner"
                        ],
                        "detectionPasses": 1,
                        "nerCalls": 1,
                        "llmCalls": 0
                      },
                      "mapping": {
                        "[PERSON-1]": "Mette Kjærgaard Holm",
                        "[CPR-1]": "120385-1234",
                        "[ADRESSE-1]": "Fiktivvej 12, 8000 Aarhus C",
                        "[TLF-1]": "21 34 56 78",
                        "[EMAIL-1]": "mette.holm@example.invalid"
                      },
                      "entries": [
                        {
                          "placeholder": "[PERSON-1]",
                          "type": "person",
                          "value": "Mette Kjærgaard Holm",
                          "confidence": "medium",
                          "source": "ner"
                        },
                        {
                          "placeholder": "[CPR-1]",
                          "type": "cpr",
                          "value": "120385-1234",
                          "confidence": "high",
                          "source": "regex"
                        },
                        {
                          "placeholder": "[ADRESSE-1]",
                          "type": "address",
                          "value": "Fiktivvej 12, 8000 Aarhus C",
                          "confidence": "high",
                          "source": "ner"
                        },
                        {
                          "placeholder": "[TLF-1]",
                          "type": "phone",
                          "value": "21 34 56 78",
                          "confidence": "medium",
                          "source": "regex"
                        },
                        {
                          "placeholder": "[EMAIL-1]",
                          "type": "email",
                          "value": "mette.holm@example.invalid",
                          "confidence": "high",
                          "source": "regex"
                        }
                      ],
                      "file": {
                        "type": "pdf",
                        "bytes": 1025,
                        "pageCount": 2
                      },
                      "pages": [
                        {
                          "number": 1,
                          "start": 0,
                          "end": 83,
                          "hasText": true,
                          "redactedStart": 0,
                          "redactedEnd": 53
                        },
                        {
                          "number": 2,
                          "start": 85,
                          "end": 131,
                          "hasText": true,
                          "redactedStart": 55,
                          "redactedEnd": 80
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Only with Prefer: respond-async: the file's text is a job, exactly as POST /v1/redact/file/jobs answers.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Location": {
                "description": "The job's status: /v1/redact/jobs/{id}.",
                "schema": {
                  "type": "string"
                },
                "example": "/v1/redact/jobs/3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c"
              },
              "Preference-Applied": {
                "description": "respond-async",
                "schema": {
                  "type": "string",
                  "enum": [
                    "respond-async"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "state",
                    "createdAt",
                    "expiresAt",
                    "progress",
                    "file",
                    "pages"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "state": {
                      "type": "string",
                      "enum": [
                        "queued",
                        "running",
                        "succeeded",
                        "failed",
                        "cancelled",
                        "invalidated"
                      ]
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Application retention deadline. A queued job expires this long after it was submitted; a running job is kept for as long as its worker is alive; when the job ends, the retention period counts from the end. Results remain encrypted until explicit deletion or expiry; reads do not consume them."
                    },
                    "progress": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "completed",
                        "total"
                      ],
                      "properties": {
                        "completed": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    },
                    "error": {
                      "type": "string"
                    },
                    "reused": {
                      "type": "boolean"
                    },
                    "file": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "type",
                        "bytes",
                        "pageCount"
                      ],
                      "properties": {
                        "type": {
                          "type": "string",
                          "enum": [
                            "pdf",
                            "docx"
                          ]
                        },
                        "bytes": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "pageCount": {
                          "type": "integer",
                          "minimum": 1
                        }
                      }
                    },
                    "pages": {
                      "type": "array",
                      "description": "Offsets in the extracted text, which is the job's source. Read the result from GET /v1/redact/jobs/{id}/result; with include changes, its changes map these offsets into redacted.",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "number",
                          "start",
                          "end",
                          "hasText"
                        ],
                        "properties": {
                          "number": {
                            "type": "integer",
                            "minimum": 1,
                            "description": "PDF page number. For .docx: body parts split at the page breaks Word recorded, then headers, footers, footnotes and endnotes, one part each."
                          },
                          "start": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Start of the page in the extracted text (the source the other offsets refer to)."
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Exclusive end in the extracted text."
                          },
                          "hasText": {
                            "type": "boolean",
                            "description": "false for a page without a text layer, only with allowPagesWithoutText."
                          }
                        }
                      }
                    },
                    "source": {
                      "type": "string",
                      "maxLength": 2000000,
                      "description": "Only with include source: the extracted text, pages separated by a blank line. Contains personal information."
                    }
                  }
                },
                "examples": {
                  "pdf": {
                    "summary": "A new job for a two-page PDF",
                    "value": {
                      "id": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c",
                      "state": "queued",
                      "createdAt": "2026-10-20T08:00:00.000Z",
                      "expiresAt": "2026-10-20T09:00:00.000Z",
                      "progress": {
                        "completed": 0,
                        "total": 0
                      },
                      "reused": false,
                      "file": {
                        "type": "pdf",
                        "bytes": 1025,
                        "pageCount": 2
                      },
                      "pages": [
                        {
                          "number": 1,
                          "start": 0,
                          "end": 83,
                          "hasText": true
                        },
                        {
                          "number": 2,
                          "start": 85,
                          "end": 131,
                          "hasText": true
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `invalid_body`: The body is not a JSON object with the documented fields: invalid JSON, Content-Type text/plain, an unknown field, a field of the wrong type, or a required field that is missing or blank.\n- `invalid_file`: file is missing or malformed (filename missing, blank, longer than 255 characters or with control characters; data not a string; an unknown key), or the PDF or Word file itself is damaged or not a Word document.\n- `invalid_include`: include has an unknown or repeated value.\n- `unsupported_redact_engine`: engine is not one of the documented values.\n- `engine_retired`: engine review, the separate LLM engine, is retired. Send engine pipeline, or leave engine out, with detectors [\"regex\", \"llm\"]; hint says the same. Nothing was reserved or processed.\n- `unsupported_redact_mode`: mode is not regex, ner or semantic.\n- `invalid_detectors`: detectors is empty, has an unknown or repeated value, or more than three values.\n- `unsupported_engine_mode`: mode disagrees with detectors. Leave mode out when you send detectors.\n- `invalid_processing`: processing is not local or eu-cloud.\n- `unsupported_processing`: processing eu-cloud needs llm in detectors (and engine pipeline). Add llm to detectors.\n- `unsupported_redact_policy`: policy is not all-detected.\n- `invalid_ner_deployment`: nerDeployment is not eu-de or local.\n- `ner_deployment_without_ner`: nerDeployment was sent, but ner is not in detectors (or mode is regex).\n- `ner_model_without_ner`: nerModel was sent, but ner is not in detectors (or mode is regex).\n- `ner_model_unknown`: No NER model has the id in nerModel. GET /v1/access lists the models you may choose.\n- `ner_model_unavailable`: The NER model in nerModel is retired or does not run at the chosen place. A model other than the default also needs detectors.\n- `llm_model_not_applicable`: model was sent, but llm is not in detectors, or engine is not pipeline.\n- `llm_model_not_validated`: The language model in model is not validated for Redact at the processing you chose. GET /v1/access lists the models you may name.\n- `unsupported_engine_detectors`: The detectors do not fit engine agentic: it needs regex and llm, with optional ner.\n- `invalid_sources`: sources has an unknown or repeated value.\n- `unsupported_engine_sources`: sources was sent without engine agentic.\n- `invalid_file_encoding`: file.data is empty or not base64.\n- `empty_file`: file.data decodes to nothing.\n- `unsupported_file_type`: The file is neither a PDF nor a Word .docx, by its content, its extension or its contentType.\n- `file_type_mismatch`: The extension or contentType disagrees with the file's content.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_body": {
                    "summary": "invalid_body",
                    "value": {
                      "error": "invalid_body",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_body"
                    }
                  },
                  "invalid_file": {
                    "summary": "invalid_file",
                    "value": {
                      "error": "invalid_file",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_file"
                    }
                  },
                  "invalid_include": {
                    "summary": "invalid_include",
                    "value": {
                      "error": "invalid_include",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_include"
                    }
                  },
                  "unsupported_redact_engine": {
                    "summary": "unsupported_redact_engine",
                    "value": {
                      "error": "unsupported_redact_engine",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_engine"
                    }
                  },
                  "engine_retired": {
                    "summary": "engine_retired",
                    "value": {
                      "error": "engine_retired",
                      "hint": "engine review is retired. Send engine pipeline, or leave engine out, with detectors [\"regex\", \"llm\"].",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/engine_retired"
                    }
                  },
                  "unsupported_redact_mode": {
                    "summary": "unsupported_redact_mode",
                    "value": {
                      "error": "unsupported_redact_mode",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_mode"
                    }
                  },
                  "invalid_detectors": {
                    "summary": "invalid_detectors",
                    "value": {
                      "error": "invalid_detectors",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_detectors"
                    }
                  },
                  "unsupported_engine_mode": {
                    "summary": "unsupported_engine_mode",
                    "value": {
                      "error": "unsupported_engine_mode",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_mode"
                    }
                  },
                  "invalid_processing": {
                    "summary": "invalid_processing",
                    "value": {
                      "error": "invalid_processing",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_processing"
                    }
                  },
                  "unsupported_processing": {
                    "summary": "unsupported_processing",
                    "value": {
                      "error": "unsupported_processing",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_processing"
                    }
                  },
                  "unsupported_redact_policy": {
                    "summary": "unsupported_redact_policy",
                    "value": {
                      "error": "unsupported_redact_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_policy"
                    }
                  },
                  "invalid_ner_deployment": {
                    "summary": "invalid_ner_deployment",
                    "value": {
                      "error": "invalid_ner_deployment",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_ner_deployment"
                    }
                  },
                  "ner_deployment_without_ner": {
                    "summary": "ner_deployment_without_ner",
                    "value": {
                      "error": "ner_deployment_without_ner",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_deployment_without_ner"
                    }
                  },
                  "ner_model_without_ner": {
                    "summary": "ner_model_without_ner",
                    "value": {
                      "error": "ner_model_without_ner",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_without_ner"
                    }
                  },
                  "ner_model_unknown": {
                    "summary": "ner_model_unknown",
                    "value": {
                      "error": "ner_model_unknown",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unknown"
                    }
                  },
                  "ner_model_unavailable": {
                    "summary": "ner_model_unavailable",
                    "value": {
                      "error": "ner_model_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unavailable"
                    }
                  },
                  "llm_model_not_applicable": {
                    "summary": "llm_model_not_applicable",
                    "value": {
                      "error": "llm_model_not_applicable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_applicable"
                    }
                  },
                  "llm_model_not_validated": {
                    "summary": "llm_model_not_validated",
                    "value": {
                      "error": "llm_model_not_validated",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_validated"
                    }
                  },
                  "unsupported_engine_detectors": {
                    "summary": "unsupported_engine_detectors",
                    "value": {
                      "error": "unsupported_engine_detectors",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_detectors"
                    }
                  },
                  "invalid_sources": {
                    "summary": "invalid_sources",
                    "value": {
                      "error": "invalid_sources",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_sources"
                    }
                  },
                  "unsupported_engine_sources": {
                    "summary": "unsupported_engine_sources",
                    "value": {
                      "error": "unsupported_engine_sources",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_sources"
                    }
                  },
                  "invalid_file_encoding": {
                    "summary": "invalid_file_encoding",
                    "value": {
                      "error": "invalid_file_encoding",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_file_encoding"
                    }
                  },
                  "empty_file": {
                    "summary": "empty_file",
                    "value": {
                      "error": "empty_file",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/empty_file"
                    }
                  },
                  "unsupported_file_type": {
                    "summary": "unsupported_file_type",
                    "value": {
                      "error": "unsupported_file_type",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_file_type"
                    }
                  },
                  "file_type_mismatch": {
                    "summary": "file_type_mismatch",
                    "value": {
                      "error": "file_type_mismatch",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/file_type_mismatch"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not issued with it, the person behind a personal key is no longer assigned Redact, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `processing_choice_not_allowed`: Your organisation does not allow this processing (local or eu-cloud) through the API. NER on local infrastructure is local processing, also when llm runs on eu-cloud; that refusal names the place in nerDeployment.\n- `processing_not_enabled`: processing eu-cloud was requested, but EU processing is not enabled for Redact in your organisation, or its agreement keeps processing local.\n- `processing_not_approved`: The organisation's processing approval for Redact was withdrawn.\n- `app_not_assigned`: The person behind a personal key lost Redact or their membership while the request was admitted. A person who is no longer assigned Redact normally gets 401 invalid_api_key or 403 insufficient_scope instead.\n- `processing_access_changed`: The organisation's processing setup changed while the request was handled. Send it again.\n- `ai_credit_not_granted`: No monthly AI spending cap is set up for this month, so eu-cloud cannot run.\n- `ner_model_preview_required`: The NER model in nerModel is a preview. It needs your organisation's Redact preview, which an organisation integration key does not have.\n- `integration_not_permitted`: An organisation integration key sent engine agentic.\n- `agentic_preview_not_enabled`: The organisation's administrator has not turned on the Redact Agentic preview.\n- `agentic_lookup_not_allowed`: A source in sources is not approved by the organisation's administrator.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "processing_choice_not_allowed": {
                    "summary": "processing_choice_not_allowed",
                    "value": {
                      "error": "processing_choice_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_choice_not_allowed"
                    }
                  },
                  "processing_not_enabled": {
                    "summary": "processing_not_enabled",
                    "value": {
                      "error": "processing_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_enabled"
                    }
                  },
                  "processing_not_approved": {
                    "summary": "processing_not_approved",
                    "value": {
                      "error": "processing_not_approved",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_approved"
                    }
                  },
                  "app_not_assigned": {
                    "summary": "app_not_assigned",
                    "value": {
                      "error": "app_not_assigned",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/app_not_assigned"
                    }
                  },
                  "processing_access_changed": {
                    "summary": "processing_access_changed",
                    "value": {
                      "error": "processing_access_changed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_access_changed"
                    }
                  },
                  "ai_credit_not_granted": {
                    "summary": "ai_credit_not_granted",
                    "value": {
                      "error": "ai_credit_not_granted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ai_credit_not_granted"
                    }
                  },
                  "ner_model_preview_required": {
                    "summary": "ner_model_preview_required",
                    "value": {
                      "error": "ner_model_preview_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_preview_required"
                    }
                  },
                  "integration_not_permitted": {
                    "summary": "integration_not_permitted",
                    "value": {
                      "error": "integration_not_permitted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/integration_not_permitted"
                    }
                  },
                  "agentic_preview_not_enabled": {
                    "summary": "agentic_preview_not_enabled",
                    "value": {
                      "error": "agentic_preview_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_preview_not_enabled"
                    }
                  },
                  "agentic_lookup_not_allowed": {
                    "summary": "agentic_lookup_not_allowed",
                    "value": {
                      "error": "agentic_lookup_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_lookup_not_allowed"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict with the current state.\n\n- `processing_policy_changed`: Your organisation's redaction rules changed while the request ran. Send it again.\n- `model_not_enabled`: The EU cloud model set up for your organisation is not available, or the EU model you named in model is not on your organisation's EU route.\n- `llm_model_not_validated`: No model was named, and the default model where llm runs is not validated for Redact: the local language model this server uses by default or, with eu-cloud, the EU model the person behind a personal key saved, else the model on your organisation's EU route. Name a model from GET /v1/access.\n- `reasoning_effort_not_supported`: The reasoning effort komplai set for this EU language model in Redact is not one the model accepts on your organisation's EU route. Nothing was sent to the model, and the API does not send another effort.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "processing_policy_changed": {
                    "summary": "processing_policy_changed",
                    "value": {
                      "error": "processing_policy_changed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_policy_changed"
                    }
                  },
                  "model_not_enabled": {
                    "summary": "model_not_enabled",
                    "value": {
                      "error": "model_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_not_enabled"
                    }
                  },
                  "llm_model_not_validated": {
                    "summary": "llm_model_not_validated",
                    "value": {
                      "error": "llm_model_not_validated",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_validated"
                    }
                  },
                  "reasoning_effort_not_supported": {
                    "summary": "reasoning_effort_not_supported",
                    "value": {
                      "error": "reasoning_effort_not_supported",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/reasoning_effort_not_supported"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Too large.\n\n- `file_too_large`: The file is over 50 MiB, or the body is over its byte limit.\n- `too_many_pages`: The PDF has more than 2,000 pages.\n- `file_too_complex`: Reading the file took more than 120 seconds or more resources than allowed (memory, ZIP entries, XML size or depth).\n- `text_too_large`: The extracted text is over 750,000 UTF-16 code units. Send the file as a job.\n- `use_job`: The run cannot finish within the 300 seconds a synchronous call may take. estimatedSeconds is komplai's estimate from its measured speeds, with an empty queue; holdSeconds is the limit. Nothing was processed or charged. Send the same body as a text job (POST /v1/redact/jobs), or this call with Prefer: respond-async.\n- `processing_complexity_exceeded`: The text needs more work than its length allows: more candidate matches or matched characters than the per-character bounds in /v1/access, coreference matching work beyond its per-character bound (for example one name nested inside itself thousands of times), or more time for the organisation rules than their limit, which grows with the text and the number of rules. The bounds grow with the text, so splitting it rarely helps. Nothing partial is returned.\n- `result_too_large`: The result would be over 32 MiB. Ask for fewer include reports or split the text.\n- `context_limit`: A part of the text does not fit the language model's context, even after it was asked again in halves down to 1,500 characters.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "file_too_large": {
                    "summary": "file_too_large",
                    "value": {
                      "error": "file_too_large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/file_too_large"
                    }
                  },
                  "too_many_pages": {
                    "summary": "too_many_pages",
                    "value": {
                      "error": "too_many_pages",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/too_many_pages"
                    }
                  },
                  "file_too_complex": {
                    "summary": "file_too_complex",
                    "value": {
                      "error": "file_too_complex",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/file_too_complex"
                    }
                  },
                  "text_too_large": {
                    "summary": "text_too_large",
                    "value": {
                      "error": "text_too_large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/text_too_large"
                    }
                  },
                  "use_job": {
                    "summary": "use_job",
                    "value": {
                      "error": "use_job",
                      "estimatedSeconds": 540,
                      "holdSeconds": 300,
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/use_job"
                    }
                  },
                  "processing_complexity_exceeded": {
                    "summary": "processing_complexity_exceeded",
                    "value": {
                      "error": "processing_complexity_exceeded",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_complexity_exceeded"
                    }
                  },
                  "result_too_large": {
                    "summary": "result_too_large",
                    "value": {
                      "error": "result_too_large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/result_too_large"
                    }
                  },
                  "context_limit": {
                    "summary": "context_limit",
                    "value": {
                      "error": "context_limit",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/context_limit"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.\n\n- `invalid_body`: The body has no Content-Type, or one the API does not read, such as XML or form data. text/plain is read and answers 400 invalid_body.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_body": {
                    "summary": "invalid_body",
                    "value": {
                      "error": "invalid_body",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_body"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "The file cannot be read.\n\n- `invalid_file`: The PDF or Word file is damaged, is a ZIP that is not a Word document, or its XML declares a DOCTYPE or entities.\n- `encrypted_file`: The PDF is password protected.\n- `scanned_pages_not_supported`: A page has no text layer, for example a scan. No OCR runs. pages lists the pages; allowPagesWithoutText true processes the rest.\n- `no_text_found`: The file contains no text at all.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_file": {
                    "summary": "invalid_file",
                    "value": {
                      "error": "invalid_file",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_file"
                    }
                  },
                  "encrypted_file": {
                    "summary": "encrypted_file",
                    "value": {
                      "error": "encrypted_file",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/encrypted_file"
                    }
                  },
                  "scanned_pages_not_supported": {
                    "summary": "scanned_pages_not_supported",
                    "value": {
                      "error": "scanned_pages_not_supported",
                      "pages": [
                        2
                      ],
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/scanned_pages_not_supported"
                    }
                  },
                  "no_text_found": {
                    "summary": "no_text_found",
                    "value": {
                      "error": "no_text_found",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/no_text_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `processing_capacity_busy`: Two files are already being read in this API process, or all organisation-rule workers are busy. There is no queue: try again after Retry-After.\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.\n- `ai_credit_exhausted`: The organisation's monthly AI spending cap for EU cloud is reached, or its own lower limit, or on a pilot the pilot's total allowance. Local processing keeps working.\n- `person_ai_credit_exhausted`: Your personal AI limit is reached (personal keys only).",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "processing_capacity_busy": {
                    "summary": "processing_capacity_busy",
                    "value": {
                      "error": "processing_capacity_busy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_capacity_busy"
                    }
                  },
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  },
                  "ai_credit_exhausted": {
                    "summary": "ai_credit_exhausted",
                    "value": {
                      "error": "ai_credit_exhausted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ai_credit_exhausted"
                    }
                  },
                  "person_ai_credit_exhausted": {
                    "summary": "person_ai_credit_exhausted",
                    "value": {
                      "error": "person_ai_credit_exhausted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/person_ai_credit_exhausted"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "A processing service failed. No partial result is returned.\n\n- `incomplete_detection`: A detection layer did not finish the whole text. No partial result is returned.\n- `invalid_detection`: A detection layer returned output that failed validation. For llm: an answer that still could not be used after its part of the text was asked again in halves down to 1,500 characters.\n- `detection_service_unavailable`: The detection service could not be reached or failed.\n- `unsafe_placeholder`: A fixed replacement token from your organisation's rules occurs in the text or collides with a label.\n- `service_unavailable`: The EU cloud model failed or could not be reached.\n- `invalid_response`: The EU cloud model's answer failed validation.\n- `agent_no_progress`: Redact Agentic stopped making progress: several rounds in a row read nothing new, found nothing and moved to no new step. No partial result is returned.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "incomplete_detection": {
                    "summary": "incomplete_detection",
                    "value": {
                      "error": "incomplete_detection",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/incomplete_detection"
                    }
                  },
                  "invalid_detection": {
                    "summary": "invalid_detection",
                    "value": {
                      "error": "invalid_detection",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_detection"
                    }
                  },
                  "detection_service_unavailable": {
                    "summary": "detection_service_unavailable",
                    "value": {
                      "error": "detection_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/detection_service_unavailable"
                    }
                  },
                  "unsafe_placeholder": {
                    "summary": "unsafe_placeholder",
                    "value": {
                      "error": "unsafe_placeholder",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsafe_placeholder"
                    }
                  },
                  "service_unavailable": {
                    "summary": "service_unavailable",
                    "value": {
                      "error": "service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/service_unavailable"
                    }
                  },
                  "invalid_response": {
                    "summary": "invalid_response",
                    "value": {
                      "error": "invalid_response",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_response"
                    }
                  },
                  "agent_no_progress": {
                    "summary": "agent_no_progress",
                    "value": {
                      "error": "agent_no_progress",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agent_no_progress"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `redact_file_not_enabled`: File input is switched off on this server.\n- `file_extraction_unavailable`: The file reader could not start or stopped. This is not caused by the file.\n- `ner_location_unavailable`: The NER place cannot be used now: komplai has not switched it on for the API, or it did not answer. nerDeployment names the place. NER never moves to another place; GET /v1/access shows which places are available.\n- `ner_model_unconfigured`: The NER model in nerModel is not set up at the default place on this server.\n- `local_model_unavailable`: The local language model in model is validated for Redact, but local infrastructure does not offer it right now. The API never picks another model.\n- `redact_runtime_unavailable`: A selected layer (NER or the language model) is not set up or not available right now.\n- `semantic_runtime_not_configured`: No local language model is set up for llm.\n- `detector_unavailable`: detectors includes ner, but NER is not set up.\n- `detection_service_unavailable`: Unexpected failure during detection.\n- `access_service_unavailable`: Keys and access could not be checked.\n- `invalid_organisation_policy`: Your organisation's redaction rules cannot be applied (more than 500 rules, or an invalid rule).\n- `chat_cloud_unavailable`: The organisation's EU cloud setup cannot be used right now.\n- `gateway_misconfigured`: The connection to the detection service or the language model is misconfigured.\n- `incoherent_redaction_policy`: Your organisation's redaction and allow rules conflict for this text.\n- `agentic_disabled`: engine agentic is switched off on this server.\n- `agentic_lookup_unavailable`: A source in sources is not available right now.\n- `model_busy`: The language model on local infrastructure cannot take this call right now: its shared queue is full, or no node can run the model (for example while it restarts). Wait Retry-After seconds; a text job waits by itself.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "redact_file_not_enabled": {
                    "summary": "redact_file_not_enabled",
                    "value": {
                      "error": "redact_file_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_file_not_enabled"
                    }
                  },
                  "file_extraction_unavailable": {
                    "summary": "file_extraction_unavailable",
                    "value": {
                      "error": "file_extraction_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/file_extraction_unavailable"
                    }
                  },
                  "ner_location_unavailable": {
                    "summary": "ner_location_unavailable",
                    "value": {
                      "error": "ner_location_unavailable",
                      "nerDeployment": "local",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_location_unavailable"
                    }
                  },
                  "ner_model_unconfigured": {
                    "summary": "ner_model_unconfigured",
                    "value": {
                      "error": "ner_model_unconfigured",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unconfigured"
                    }
                  },
                  "local_model_unavailable": {
                    "summary": "local_model_unavailable",
                    "value": {
                      "error": "local_model_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/local_model_unavailable"
                    }
                  },
                  "redact_runtime_unavailable": {
                    "summary": "redact_runtime_unavailable",
                    "value": {
                      "error": "redact_runtime_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_runtime_unavailable"
                    }
                  },
                  "semantic_runtime_not_configured": {
                    "summary": "semantic_runtime_not_configured",
                    "value": {
                      "error": "semantic_runtime_not_configured",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/semantic_runtime_not_configured"
                    }
                  },
                  "detector_unavailable": {
                    "summary": "detector_unavailable",
                    "value": {
                      "error": "detector_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/detector_unavailable"
                    }
                  },
                  "detection_service_unavailable": {
                    "summary": "detection_service_unavailable",
                    "value": {
                      "error": "detection_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/detection_service_unavailable"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  },
                  "invalid_organisation_policy": {
                    "summary": "invalid_organisation_policy",
                    "value": {
                      "error": "invalid_organisation_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_organisation_policy"
                    }
                  },
                  "chat_cloud_unavailable": {
                    "summary": "chat_cloud_unavailable",
                    "value": {
                      "error": "chat_cloud_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/chat_cloud_unavailable"
                    }
                  },
                  "gateway_misconfigured": {
                    "summary": "gateway_misconfigured",
                    "value": {
                      "error": "gateway_misconfigured",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/gateway_misconfigured"
                    }
                  },
                  "incoherent_redaction_policy": {
                    "summary": "incoherent_redaction_policy",
                    "value": {
                      "error": "incoherent_redaction_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/incoherent_redaction_policy"
                    }
                  },
                  "agentic_disabled": {
                    "summary": "agentic_disabled",
                    "value": {
                      "error": "agentic_disabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_disabled"
                    }
                  },
                  "agentic_lookup_unavailable": {
                    "summary": "agentic_lookup_unavailable",
                    "value": {
                      "error": "agentic_lookup_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_lookup_unavailable"
                    }
                  },
                  "model_busy": {
                    "summary": "model_busy",
                    "value": {
                      "error": "model_busy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_busy"
                    }
                  }
                }
              }
            }
          },
          "504": {
            "description": "The call timed out.\n\n- `processing_timeout`: The call took more than 300 seconds, or the client disconnected. Send long texts as text jobs.\n- `model_stalled`: The language model stopped answering: its connection was silent, or it ran past the time its answer needs at the model's measured speed. No partial result is returned.\n- `model_queue_expired`: The language model's shared queue on local infrastructure did not reach this call before its deadline. Try again later, or send it as a text job, which waits for its turn.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "processing_timeout": {
                    "summary": "processing_timeout",
                    "value": {
                      "error": "processing_timeout",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_timeout"
                    }
                  },
                  "model_stalled": {
                    "summary": "model_stalled",
                    "value": {
                      "error": "model_stalled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_stalled"
                    }
                  },
                  "model_queue_expired": {
                    "summary": "model_queue_expired",
                    "value": {
                      "error": "model_queue_expired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_queue_expired"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/redact/file/jobs": {
      "post": {
        "operationId": "submitRedactFileJob",
        "summary": "Submit a PDF or Word file as a text job (beta)",
        "description": "Beta. Send a PDF with a text layer or a Word .docx as base64 in JSON; komplai extracts its text and runs the same Redact processing as for text, with the same options. The response is pseudonymised text, not a redacted file. No OCR runs: a scanned page is refused (422 scanned_pages_not_supported, with the page numbers) unless allowPagesWithoutText is true. The text is extracted on komplai's API servers (EU · Germany) in memory; the file is not stored. File problems: 400 unsupported_file_type, file_type_mismatch, invalid_file_encoding, empty_file; 413 file_too_large, too_many_pages, text_too_large, file_too_complex; 422 invalid_file, encrypted_file, no_text_found, scanned_pages_not_supported; 429 processing_capacity_busy while other files are being read. No method guarantees anonymity; check the result. The text is extracted while you wait and submitted as a text job, exactly as POST /v1/redact/jobs, up to 2,000,000 UTF-16 code units. The 202 response adds the file's page offsets in the extracted text; poll and read the result, restore and delete with the /v1/redact/jobs/{id} operations.\n\nOrganisation integration keys cannot use engine agentic: 403 integration_not_permitted.",
        "tags": [
          "Redact"
        ],
        "x-komplai-stability": "beta",
        "security": [
          {
            "organisationKey": []
          },
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "redact:process"
        ],
        "x-komplai-principals": [
          "person",
          "integration"
        ],
        "parameters": [
          {
            "in": "header",
            "name": "idempotency-key",
            "required": false,
            "description": "Optional. A new UUID for each job, in upper or lower case. If the connection drops, send the same request again with the same key: within the job's lifetime the same key and body return the same job (reused true) instead of a second one, and the same key with another body answers 409 idempotency_conflict. Never reuse a key for another text. The key is bound to your organisation, the owner (person or integration) and the API key. The default {{$guid}} is Postman's variable for a new UUID on each request; in other tools, generate a UUID for each job or leave the header out.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "default": "{{$guid}}"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "filename",
                      "data"
                    ],
                    "properties": {
                      "filename": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 255,
                        "description": "The file's name. Its extension (.pdf or .docx) must match the content. Used only to check the type; not stored, logged or returned."
                      },
                      "contentType": {
                        "type": "string",
                        "enum": [
                          "application/pdf",
                          "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                          "application/octet-stream"
                        ],
                        "description": "Optional. Must agree with the content when it names PDF or Word.",
                        "default": "application/octet-stream"
                      },
                      "data": {
                        "type": "string",
                        "format": "byte",
                        "description": "The file's bytes, base64 encoded (standard or URL-safe; line breaks are ignored). At most 50 MiB decoded and 2,000 PDF pages. PDF needs a text layer; Word .docx only, not .doc."
                      }
                    }
                  },
                  "detectors": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 3,
                    "uniqueItems": true,
                    "default": [
                      "regex",
                      "ner"
                    ],
                    "items": {
                      "type": "string",
                      "enum": [
                        "regex",
                        "ner",
                        "llm"
                      ]
                    },
                    "description": "The layers to run, in any non-empty combination. regex: rules for CPR numbers, phone numbers, e-mail addresses and more. ner: a model for names, organisations and addresses. llm: a language model for what rules and NER miss, such as health data in free text; it can take up to a minute. It reads the text in parts of at most 12,000 characters. When its answer for a part cannot be used, that answer is discarded and the part is asked again in two overlapping halves, down to halves of 1,500 characters: at most 14 extra calls for a part. If an answer still cannot be used, the call fails with 502 invalid_detection or incomplete_detection and returns no result. Default regex and ner. The older field mode is still accepted but is not listed here: leave it out, because if you send both, they must agree (400 unsupported_engine_mode). engine agentic needs regex and llm, with optional ner."
                  },
                  "processing": {
                    "type": "string",
                    "enum": [
                      "local",
                      "eu-cloud"
                    ],
                    "default": "local",
                    "description": "Where the language model (llm) runs. local (default): komplai's local infrastructure in Denmark, with no AI charge. eu-cloud: komplai's EU cloud, which answers faster. It needs llm in detectors and EU processing approved for your organisation, and its usage counts towards the organisation's monthly AI spending cap (429 ai_credit_exhausted when the cap is reached). Every call to the model, including a part asked again in halves, is checked against the cap and counted on its own. Name the model in model, or leave it out for the default model there. Rules always run on komplai's servers in Germany (EU), and NER at the place in nerDeployment or, without one, at the default place GET /v1/access marks. GET /v1/access names where each layer processes text for your organisation, including eu-cloud, in services[].hosting.processing.layers. The API never switches between local and eu-cloud by itself."
                  },
                  "nerDeployment": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "eu-de",
                      "local",
                      null
                    ],
                    "default": null,
                    "description": "Where the ner layer runs: eu-de (komplai's servers in Germany, EU) or local (komplai's local infrastructure in Denmark). Leave it out, or send null, for the default place: the one GET /v1/access marks with default true in the ner layer's places (services[].hosting.processing.layers), eu-de unless komplai has changed it. Each place there has available and a reason for the operations with detectors, and detect with the same for POST /v1/redact/detect and the operations without detectors, so check that the place you name is available for the operation you call. A place that cannot be used now answers 503 ner_location_unavailable and names the place in nerDeployment; NER never moves to another place. Needs ner in detectors (400 ner_deployment_without_ner); any other value answers 400 invalid_ner_deployment. The response names the place in configuration.nerDeployment (nerDeployment on POST /v1/redact/detect)."
                  },
                  "nerModel": {
                    "type": "string",
                    "nullable": true,
                    "default": null,
                    "description": "The NER model, by id from the ner layer's models in GET /v1/access. Leave it out, or send null, for the default model. An unknown id answers 400 ner_model_unknown, and a retired one or one that does not run at the place you name 400 ner_model_unavailable; a preview model needs your organisation's Redact preview, which an organisation integration key does not have (403 ner_model_preview_required). POST /v1/redact/detect, and the text operations without detectors, run only the default model (400 ner_model_unavailable for another). A model that is not set up at the default place on this server answers 503 ner_model_unconfigured; NER never runs another model. Needs ner in detectors (400 ner_model_without_ner). The response names the model in configuration.nerModel (nerModel on POST /v1/redact/detect)."
                  },
                  "model": {
                    "type": "string",
                    "nullable": true,
                    "default": null,
                    "description": "The language model for llm, by id from GET /v1/access: the models of the llm layer for the processing you choose. Leave it out, or send null, for the default model there. Only models validated for Redact at that processing are accepted (400 llm_model_not_validated): qwen3.8-27b-q8 for local and mistral-small-3.2-24b-instruct-2506 for eu-cloud. GET /v1/access also lists models with reason not_validated, which cannot be chosen. A local model that local infrastructure does not offer right now answers 503 local_model_unavailable, and an EU model your organisation's EU route does not have answers 409 model_not_enabled. Without model, the default must be validated too: on local the server's default model, on eu-cloud the EU model the person behind a personal key saved, else the model on your organisation's EU route (409 llm_model_not_validated). The API never picks another model. Needs llm in detectors and engine pipeline (400 llm_model_not_applicable). configuration.llmModel names the model that ran."
                  },
                  "policy": {
                    "type": "string",
                    "enum": [
                      "all-detected"
                    ],
                    "default": "all-detected",
                    "description": "Always all-detected: everything found is replaced, under your organisation's redaction and allow rules."
                  },
                  "engine": {
                    "type": "string",
                    "enum": [
                      "pipeline",
                      "agentic"
                    ],
                    "default": "pipeline",
                    "description": "Preview. Leave out. pipeline (default) runs the chosen detectors. agentic is an experimental method for organisations whose administrator has turned on the Redact Agentic preview (403 agentic_preview_not_enabled otherwise); an organisation integration key gets 403 integration_not_permitted. It needs processing local and detectors regex and llm, with optional ner. It reads the text in windows, so it takes the operation's own text limit; a long document belongs in a text job.",
                    "x-komplai-stability": "preview"
                  },
                  "sources": {
                    "type": "array",
                    "maxItems": 5,
                    "uniqueItems": true,
                    "default": [],
                    "items": {
                      "type": "string",
                      "enum": [
                        "cvr-official",
                        "bbr",
                        "web",
                        "dar",
                        "wikidata-local"
                      ]
                    },
                    "description": "Preview. Leave out. Lookup sources for engine agentic, chosen for each call. Each needs the administrator's approval and configured access; an unapproved or unavailable source refuses the call (403 agentic_lookup_not_allowed or 503 agentic_lookup_unavailable) and is never silently dropped. Default none.",
                    "x-komplai-stability": "preview"
                  },
                  "include": {
                    "type": "array",
                    "maxItems": 5,
                    "uniqueItems": true,
                    "default": [],
                    "items": {
                      "type": "string",
                      "enum": [
                        "mapping",
                        "findings",
                        "changes",
                        "skipped",
                        "source"
                      ]
                    },
                    "description": "As POST /v1/redact, plus source: the extracted text the offsets refer to. Every report except the bare result contains originals; protect it as personal information."
                  },
                  "allowPagesWithoutText": {
                    "type": "boolean",
                    "default": false,
                    "description": "A PDF page without a text layer (a scan, an image or a blank page) refuses the file with scanned_pages_not_supported, because no OCR runs. true processes the other pages and reports those pages with hasText false."
                  }
                }
              },
              "examples": {
                "pdf": {
                  "summary": "A two-page PDF as a text job",
                  "description": "A fictitious PDF with a text layer, base64 encoded.",
                  "value": {
                    "file": {
                      "filename": "afgoerelse.pdf",
                      "contentType": "application/pdf",
                      "data": "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFs0IDAgUiA2IDAgUl0gL0NvdW50IDIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5cGUxIC9CYXNlRm9udCAvSGVsdmV0aWNhIC9FbmNvZGluZyAvV2luQW5zaUVuY29kaW5nID4+CmVuZG9iago0IDAgb2JqCjw8IC9UeXBlIC9QYWdlIC9QYXJlbnQgMiAwIFIgL01lZGlhQm94IFswIDAgNTk1IDg0Ml0gL1Jlc291cmNlcyA8PCAvRm9udCA8PCAvRjEgMyAwIFIgPj4gPj4gL0NvbnRlbnRzIDUgMCBSID4+CmVuZG9iago1IDAgb2JqCjw8IC9MZW5ndGggMTMxID4+CnN0cmVhbQpCVCAvRjEgMTIgVGYgNzIgNzIwIFRkIDE2IFRMIChBZmf4cmVsc2UgdGlsIE1ldHRlIEtq5nJnYWFyZCBIb2xtKSBUaiBUKiAoY3ByLW5yLiAxMjAzODUtMTIzNCwgRmlrdGl2dmVqIDEyLCA4MDAwIEFhcmh1cyBDKSBUaiBUKiBFVAplbmRzdHJlYW0KZW5kb2JqCjYgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCA1OTUgODQyXSAvUmVzb3VyY2VzIDw8IC9Gb250IDw8IC9GMSAzIDAgUiA+PiA+PiAvQ29udGVudHMgNyAwIFIgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0xlbmd0aCA5NCA+PgpzdHJlYW0KQlQgL0YxIDEyIFRmIDcyIDcyMCBUZCAxNiBUTCAoVGVsZWZvbiAyMSAzNCA1NiA3OCkgVGogVCogKG1ldHRlLmhvbG1AZXhhbXBsZS5pbnZhbGlkKSBUaiBUKiBFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA4CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNSAwMDAwMCBuIAowMDAwMDAwMDY0IDAwMDAwIG4gCjAwMDAwMDAxMjcgMDAwMDAgbiAKMDAwMDAwMDIyNCAwMDAwMCBuIAowMDAwMDAwMzUwIDAwMDAwIG4gCjAwMDAwMDA1MzIgMDAwMDAgbiAKMDAwMDAwMDY1OCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDggL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjgwMgolJUVPRgo="
                    },
                    "detectors": [
                      "regex",
                      "ner"
                    ],
                    "processing": "local",
                    "include": [
                      "mapping"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The file's text was extracted and submitted as a text job. Location points to its status.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Location": {
                "description": "The job's status: /v1/redact/jobs/{id}.",
                "schema": {
                  "type": "string"
                },
                "example": "/v1/redact/jobs/3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "state",
                    "createdAt",
                    "expiresAt",
                    "progress",
                    "file",
                    "pages"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "state": {
                      "type": "string",
                      "enum": [
                        "queued",
                        "running",
                        "succeeded",
                        "failed",
                        "cancelled",
                        "invalidated"
                      ]
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Application retention deadline. A queued job expires this long after it was submitted; a running job is kept for as long as its worker is alive; when the job ends, the retention period counts from the end. Results remain encrypted until explicit deletion or expiry; reads do not consume them."
                    },
                    "progress": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "completed",
                        "total"
                      ],
                      "properties": {
                        "completed": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "total": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    },
                    "error": {
                      "type": "string"
                    },
                    "reused": {
                      "type": "boolean"
                    },
                    "file": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "type",
                        "bytes",
                        "pageCount"
                      ],
                      "properties": {
                        "type": {
                          "type": "string",
                          "enum": [
                            "pdf",
                            "docx"
                          ]
                        },
                        "bytes": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "pageCount": {
                          "type": "integer",
                          "minimum": 1
                        }
                      }
                    },
                    "pages": {
                      "type": "array",
                      "description": "Offsets in the extracted text, which is the job's source. Read the result from GET /v1/redact/jobs/{id}/result; with include changes, its changes map these offsets into redacted.",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "number",
                          "start",
                          "end",
                          "hasText"
                        ],
                        "properties": {
                          "number": {
                            "type": "integer",
                            "minimum": 1,
                            "description": "PDF page number. For .docx: body parts split at the page breaks Word recorded, then headers, footers, footnotes and endnotes, one part each."
                          },
                          "start": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Start of the page in the extracted text (the source the other offsets refer to)."
                          },
                          "end": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Exclusive end in the extracted text."
                          },
                          "hasText": {
                            "type": "boolean",
                            "description": "false for a page without a text layer, only with allowPagesWithoutText."
                          }
                        }
                      }
                    },
                    "source": {
                      "type": "string",
                      "maxLength": 2000000,
                      "description": "Only with include source: the extracted text, pages separated by a blank line. Contains personal information."
                    }
                  }
                },
                "examples": {
                  "pdf": {
                    "summary": "A new job for a two-page PDF",
                    "value": {
                      "id": "3f2b8c1e-7d4a-4e9b-a6c5-2d1e0f9a8b7c",
                      "state": "queued",
                      "createdAt": "2026-10-20T08:00:00.000Z",
                      "expiresAt": "2026-10-20T09:00:00.000Z",
                      "progress": {
                        "completed": 0,
                        "total": 0
                      },
                      "reused": false,
                      "file": {
                        "type": "pdf",
                        "bytes": 1025,
                        "pageCount": 2
                      },
                      "pages": [
                        {
                          "number": 1,
                          "start": 0,
                          "end": 83,
                          "hasText": true
                        },
                        {
                          "number": 2,
                          "start": 85,
                          "end": 131,
                          "hasText": true
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `invalid_body`: The body is not a JSON object with the documented fields: invalid JSON, Content-Type text/plain, an unknown field, a field of the wrong type, or a required field that is missing or blank.\n- `invalid_file`: file is missing or malformed (filename missing, blank, longer than 255 characters or with control characters; data not a string; an unknown key), or the PDF or Word file itself is damaged or not a Word document.\n- `invalid_include`: include has an unknown or repeated value.\n- `unsupported_redact_engine`: engine is not one of the documented values.\n- `engine_retired`: engine review, the separate LLM engine, is retired. Send engine pipeline, or leave engine out, with detectors [\"regex\", \"llm\"]; hint says the same. Nothing was reserved or processed.\n- `unsupported_redact_mode`: mode is not regex, ner or semantic.\n- `invalid_detectors`: detectors is empty, has an unknown or repeated value, or more than three values.\n- `unsupported_engine_mode`: mode disagrees with detectors. Leave mode out when you send detectors.\n- `invalid_processing`: processing is not local or eu-cloud.\n- `unsupported_processing`: processing eu-cloud needs llm in detectors (and engine pipeline). Add llm to detectors.\n- `unsupported_redact_policy`: policy is not all-detected.\n- `invalid_ner_deployment`: nerDeployment is not eu-de or local.\n- `ner_deployment_without_ner`: nerDeployment was sent, but ner is not in detectors (or mode is regex).\n- `ner_model_without_ner`: nerModel was sent, but ner is not in detectors (or mode is regex).\n- `ner_model_unknown`: No NER model has the id in nerModel. GET /v1/access lists the models you may choose.\n- `ner_model_unavailable`: The NER model in nerModel is retired or does not run at the chosen place. A model other than the default also needs detectors.\n- `llm_model_not_applicable`: model was sent, but llm is not in detectors, or engine is not pipeline.\n- `llm_model_not_validated`: The language model in model is not validated for Redact at the processing you chose. GET /v1/access lists the models you may name.\n- `unsupported_engine_detectors`: The detectors do not fit engine agentic: it needs regex and llm, with optional ner.\n- `invalid_sources`: sources has an unknown or repeated value.\n- `unsupported_engine_sources`: sources was sent without engine agentic.\n- `invalid_idempotency_key`: Idempotency-Key is not a UUID, or it was sent more than once.\n- `invalid_file_encoding`: file.data is empty or not base64.\n- `empty_file`: file.data decodes to nothing.\n- `unsupported_file_type`: The file is neither a PDF nor a Word .docx, by its content, its extension or its contentType.\n- `file_type_mismatch`: The extension or contentType disagrees with the file's content.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_body": {
                    "summary": "invalid_body",
                    "value": {
                      "error": "invalid_body",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_body"
                    }
                  },
                  "invalid_file": {
                    "summary": "invalid_file",
                    "value": {
                      "error": "invalid_file",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_file"
                    }
                  },
                  "invalid_include": {
                    "summary": "invalid_include",
                    "value": {
                      "error": "invalid_include",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_include"
                    }
                  },
                  "unsupported_redact_engine": {
                    "summary": "unsupported_redact_engine",
                    "value": {
                      "error": "unsupported_redact_engine",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_engine"
                    }
                  },
                  "engine_retired": {
                    "summary": "engine_retired",
                    "value": {
                      "error": "engine_retired",
                      "hint": "engine review is retired. Send engine pipeline, or leave engine out, with detectors [\"regex\", \"llm\"].",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/engine_retired"
                    }
                  },
                  "unsupported_redact_mode": {
                    "summary": "unsupported_redact_mode",
                    "value": {
                      "error": "unsupported_redact_mode",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_mode"
                    }
                  },
                  "invalid_detectors": {
                    "summary": "invalid_detectors",
                    "value": {
                      "error": "invalid_detectors",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_detectors"
                    }
                  },
                  "unsupported_engine_mode": {
                    "summary": "unsupported_engine_mode",
                    "value": {
                      "error": "unsupported_engine_mode",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_mode"
                    }
                  },
                  "invalid_processing": {
                    "summary": "invalid_processing",
                    "value": {
                      "error": "invalid_processing",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_processing"
                    }
                  },
                  "unsupported_processing": {
                    "summary": "unsupported_processing",
                    "value": {
                      "error": "unsupported_processing",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_processing"
                    }
                  },
                  "unsupported_redact_policy": {
                    "summary": "unsupported_redact_policy",
                    "value": {
                      "error": "unsupported_redact_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_policy"
                    }
                  },
                  "invalid_ner_deployment": {
                    "summary": "invalid_ner_deployment",
                    "value": {
                      "error": "invalid_ner_deployment",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_ner_deployment"
                    }
                  },
                  "ner_deployment_without_ner": {
                    "summary": "ner_deployment_without_ner",
                    "value": {
                      "error": "ner_deployment_without_ner",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_deployment_without_ner"
                    }
                  },
                  "ner_model_without_ner": {
                    "summary": "ner_model_without_ner",
                    "value": {
                      "error": "ner_model_without_ner",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_without_ner"
                    }
                  },
                  "ner_model_unknown": {
                    "summary": "ner_model_unknown",
                    "value": {
                      "error": "ner_model_unknown",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unknown"
                    }
                  },
                  "ner_model_unavailable": {
                    "summary": "ner_model_unavailable",
                    "value": {
                      "error": "ner_model_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unavailable"
                    }
                  },
                  "llm_model_not_applicable": {
                    "summary": "llm_model_not_applicable",
                    "value": {
                      "error": "llm_model_not_applicable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_applicable"
                    }
                  },
                  "llm_model_not_validated": {
                    "summary": "llm_model_not_validated",
                    "value": {
                      "error": "llm_model_not_validated",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_validated"
                    }
                  },
                  "unsupported_engine_detectors": {
                    "summary": "unsupported_engine_detectors",
                    "value": {
                      "error": "unsupported_engine_detectors",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_detectors"
                    }
                  },
                  "invalid_sources": {
                    "summary": "invalid_sources",
                    "value": {
                      "error": "invalid_sources",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_sources"
                    }
                  },
                  "unsupported_engine_sources": {
                    "summary": "unsupported_engine_sources",
                    "value": {
                      "error": "unsupported_engine_sources",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_sources"
                    }
                  },
                  "invalid_idempotency_key": {
                    "summary": "invalid_idempotency_key",
                    "value": {
                      "error": "invalid_idempotency_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_idempotency_key"
                    }
                  },
                  "invalid_file_encoding": {
                    "summary": "invalid_file_encoding",
                    "value": {
                      "error": "invalid_file_encoding",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_file_encoding"
                    }
                  },
                  "empty_file": {
                    "summary": "empty_file",
                    "value": {
                      "error": "empty_file",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/empty_file"
                    }
                  },
                  "unsupported_file_type": {
                    "summary": "unsupported_file_type",
                    "value": {
                      "error": "unsupported_file_type",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported_file_type"
                    }
                  },
                  "file_type_mismatch": {
                    "summary": "file_type_mismatch",
                    "value": {
                      "error": "file_type_mismatch",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/file_type_mismatch"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not issued with it, the person behind a personal key is no longer assigned Redact, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `approved_redact_storage_policy_required`: Your organisation processes personal data but has no approved storage policy for text jobs.\n- `processing_access_withdrawn`: Access to the job was withdrawn after it was created: the agreement, the processing setup, the organisation's redaction rules or the storage policy for text jobs changed. The job is invalidated and its text and result are deleted. This is final: stop reading the job and do not reuse its Idempotency-Key; submit the text again as a new job when access allows it.\n- `processing_choice_not_allowed`: Your organisation does not allow this processing (local or eu-cloud) through the API. NER on local infrastructure is local processing, also when llm runs on eu-cloud; that refusal names the place in nerDeployment.\n- `processing_not_enabled`: processing eu-cloud was requested, but EU processing is not enabled for Redact in your organisation, or its agreement keeps processing local.\n- `processing_not_approved`: The organisation's processing approval for Redact was withdrawn.\n- `app_not_assigned`: The person behind a personal key lost Redact or their membership while the request was admitted. A person who is no longer assigned Redact normally gets 401 invalid_api_key or 403 insufficient_scope instead.\n- `ner_model_preview_required`: The NER model in nerModel is a preview. It needs your organisation's Redact preview, which an organisation integration key does not have.\n- `integration_not_permitted`: An organisation integration key sent engine agentic.\n- `agentic_preview_not_enabled`: The organisation's administrator has not turned on the Redact Agentic preview.\n- `agentic_lookup_not_allowed`: A source in sources is not approved by the organisation's administrator.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "approved_redact_storage_policy_required": {
                    "summary": "approved_redact_storage_policy_required",
                    "value": {
                      "error": "approved_redact_storage_policy_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/approved_redact_storage_policy_required"
                    }
                  },
                  "processing_access_withdrawn": {
                    "summary": "processing_access_withdrawn",
                    "value": {
                      "error": "processing_access_withdrawn",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_access_withdrawn"
                    }
                  },
                  "processing_choice_not_allowed": {
                    "summary": "processing_choice_not_allowed",
                    "value": {
                      "error": "processing_choice_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_choice_not_allowed"
                    }
                  },
                  "processing_not_enabled": {
                    "summary": "processing_not_enabled",
                    "value": {
                      "error": "processing_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_enabled"
                    }
                  },
                  "processing_not_approved": {
                    "summary": "processing_not_approved",
                    "value": {
                      "error": "processing_not_approved",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_approved"
                    }
                  },
                  "app_not_assigned": {
                    "summary": "app_not_assigned",
                    "value": {
                      "error": "app_not_assigned",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/app_not_assigned"
                    }
                  },
                  "ner_model_preview_required": {
                    "summary": "ner_model_preview_required",
                    "value": {
                      "error": "ner_model_preview_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_preview_required"
                    }
                  },
                  "integration_not_permitted": {
                    "summary": "integration_not_permitted",
                    "value": {
                      "error": "integration_not_permitted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/integration_not_permitted"
                    }
                  },
                  "agentic_preview_not_enabled": {
                    "summary": "agentic_preview_not_enabled",
                    "value": {
                      "error": "agentic_preview_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_preview_not_enabled"
                    }
                  },
                  "agentic_lookup_not_allowed": {
                    "summary": "agentic_lookup_not_allowed",
                    "value": {
                      "error": "agentic_lookup_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_lookup_not_allowed"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict with the current state.\n\n- `idempotency_conflict`: This Idempotency-Key was used with a different body. Use a new UUID for a new job.\n- `processing_access_changed`: The processing setup changed between admission and submission. Send it again.\n- `model_not_enabled`: The EU cloud model set up for your organisation is not available, or the EU model you named in model is not on your organisation's EU route.\n- `llm_model_not_validated`: No model was named, and the default model where llm runs is not validated for Redact: the local language model this server uses by default or, with eu-cloud, the EU model the person behind a personal key saved, else the model on your organisation's EU route. Name a model from GET /v1/access.\n- `reasoning_effort_not_supported`: The reasoning effort komplai set for this EU language model in Redact is not one the model accepts on your organisation's EU route. Nothing was sent to the model, and the API does not send another effort.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "idempotency_conflict": {
                    "summary": "idempotency_conflict",
                    "value": {
                      "error": "idempotency_conflict",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/idempotency_conflict"
                    }
                  },
                  "processing_access_changed": {
                    "summary": "processing_access_changed",
                    "value": {
                      "error": "processing_access_changed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_access_changed"
                    }
                  },
                  "model_not_enabled": {
                    "summary": "model_not_enabled",
                    "value": {
                      "error": "model_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_not_enabled"
                    }
                  },
                  "llm_model_not_validated": {
                    "summary": "llm_model_not_validated",
                    "value": {
                      "error": "llm_model_not_validated",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_validated"
                    }
                  },
                  "reasoning_effort_not_supported": {
                    "summary": "reasoning_effort_not_supported",
                    "value": {
                      "error": "reasoning_effort_not_supported",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/reasoning_effort_not_supported"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Too large.\n\n- `file_too_large`: The file is over 50 MiB, or the body is over its byte limit.\n- `too_many_pages`: The PDF has more than 2,000 pages.\n- `file_too_complex`: Reading the file took more than 120 seconds or more resources than allowed (memory, ZIP entries, XML size or depth).\n- `text_too_large`: The extracted text is over 2,000,000 UTF-16 code units.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "file_too_large": {
                    "summary": "file_too_large",
                    "value": {
                      "error": "file_too_large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/file_too_large"
                    }
                  },
                  "too_many_pages": {
                    "summary": "too_many_pages",
                    "value": {
                      "error": "too_many_pages",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/too_many_pages"
                    }
                  },
                  "file_too_complex": {
                    "summary": "file_too_complex",
                    "value": {
                      "error": "file_too_complex",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/file_too_complex"
                    }
                  },
                  "text_too_large": {
                    "summary": "text_too_large",
                    "value": {
                      "error": "text_too_large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/text_too_large"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.\n\n- `invalid_body`: The body has no Content-Type, or one the API does not read, such as XML or form data. text/plain is read and answers 400 invalid_body.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_body": {
                    "summary": "invalid_body",
                    "value": {
                      "error": "invalid_body",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_body"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "The file cannot be read.\n\n- `invalid_file`: The PDF or Word file is damaged, is a ZIP that is not a Word document, or its XML declares a DOCTYPE or entities.\n- `encrypted_file`: The PDF is password protected.\n- `scanned_pages_not_supported`: A page has no text layer, for example a scan. No OCR runs. pages lists the pages; allowPagesWithoutText true processes the rest.\n- `no_text_found`: The file contains no text at all.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_file": {
                    "summary": "invalid_file",
                    "value": {
                      "error": "invalid_file",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_file"
                    }
                  },
                  "encrypted_file": {
                    "summary": "encrypted_file",
                    "value": {
                      "error": "encrypted_file",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/encrypted_file"
                    }
                  },
                  "scanned_pages_not_supported": {
                    "summary": "scanned_pages_not_supported",
                    "value": {
                      "error": "scanned_pages_not_supported",
                      "pages": [
                        2
                      ],
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/scanned_pages_not_supported"
                    }
                  },
                  "no_text_found": {
                    "summary": "no_text_found",
                    "value": {
                      "error": "no_text_found",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/no_text_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `processing_capacity_busy`: Two files are already being read in this API process. There is no queue: try again after Retry-After.\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "processing_capacity_busy": {
                    "summary": "processing_capacity_busy",
                    "value": {
                      "error": "processing_capacity_busy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_capacity_busy"
                    }
                  },
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `redact_file_not_enabled`: File input is switched off on this server.\n- `file_extraction_unavailable`: The file reader could not start or stopped. This is not caused by the file.\n- `redact_jobs_not_enabled`: Text jobs are not accepted right now; GET /v1/access shows text_contract.jobs_accepting_submissions.\n- `redact_job_service_unavailable`: The job store failed.\n- `redact_job_integrity_failed`: The job's encrypted data could not be read.\n- `invalid_organisation_policy`: Your organisation's redaction rules cannot be applied (more than 500 rules, or an invalid rule).\n- `chat_cloud_unavailable`: The organisation's EU cloud setup cannot be used right now.\n- `access_service_unavailable`: Keys and access could not be checked.\n- `ner_location_unavailable`: The NER place the job names is not set up here. Whether a set-up place answers is checked when the job runs; one that does not then fails the job with this code.\n- `ner_model_unconfigured`: The NER model in nerModel is not set up at the default place on this server.\n- `redact_runtime_unavailable`: The server's default NER place is not readable.\n- `agentic_disabled`: engine agentic is switched off on this server.\n- `agentic_lookup_unavailable`: A source in sources is not available right now.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "With scanned_pages_not_supported: the pages without a text layer."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "redact_file_not_enabled": {
                    "summary": "redact_file_not_enabled",
                    "value": {
                      "error": "redact_file_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_file_not_enabled"
                    }
                  },
                  "file_extraction_unavailable": {
                    "summary": "file_extraction_unavailable",
                    "value": {
                      "error": "file_extraction_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/file_extraction_unavailable"
                    }
                  },
                  "redact_jobs_not_enabled": {
                    "summary": "redact_jobs_not_enabled",
                    "value": {
                      "error": "redact_jobs_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_jobs_not_enabled"
                    }
                  },
                  "redact_job_service_unavailable": {
                    "summary": "redact_job_service_unavailable",
                    "value": {
                      "error": "redact_job_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_service_unavailable"
                    }
                  },
                  "redact_job_integrity_failed": {
                    "summary": "redact_job_integrity_failed",
                    "value": {
                      "error": "redact_job_integrity_failed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_job_integrity_failed"
                    }
                  },
                  "invalid_organisation_policy": {
                    "summary": "invalid_organisation_policy",
                    "value": {
                      "error": "invalid_organisation_policy",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_organisation_policy"
                    }
                  },
                  "chat_cloud_unavailable": {
                    "summary": "chat_cloud_unavailable",
                    "value": {
                      "error": "chat_cloud_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/chat_cloud_unavailable"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  },
                  "ner_location_unavailable": {
                    "summary": "ner_location_unavailable",
                    "value": {
                      "error": "ner_location_unavailable",
                      "nerDeployment": "local",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_location_unavailable"
                    }
                  },
                  "ner_model_unconfigured": {
                    "summary": "ner_model_unconfigured",
                    "value": {
                      "error": "ner_model_unconfigured",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ner_model_unconfigured"
                    }
                  },
                  "redact_runtime_unavailable": {
                    "summary": "redact_runtime_unavailable",
                    "value": {
                      "error": "redact_runtime_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_runtime_unavailable"
                    }
                  },
                  "agentic_disabled": {
                    "summary": "agentic_disabled",
                    "value": {
                      "error": "agentic_disabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_disabled"
                    }
                  },
                  "agentic_lookup_unavailable": {
                    "summary": "agentic_lookup_unavailable",
                    "value": {
                      "error": "agentic_lookup_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/agentic_lookup_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/access": {
      "get": {
        "operationId": "readAccess",
        "summary": "Read what this key can reach",
        "description": "Lists the services this key reaches in its organisation, with their scopes, hosting, processing and limits. Call it first: for Redact, hosting.processing.layers names where each layer processes text, text_contract the options and limits that apply, and text_contract.jobs_accepting_submissions whether text jobs are accepted. Works for personal keys and for organisation integration keys, which reach Redact only; an organisation integration key's response also names the integration in principal. It sends no text, runs no model and counts as one request. services[].edition is deprecated and is always \"custom\"; read scopes and processing instead.",
        "tags": [
          "Account"
        ],
        "x-komplai-stability": "stable",
        "security": [
          {
            "organisationKey": []
          },
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "access:read"
        ],
        "x-komplai-principals": [
          "person",
          "integration"
        ],
        "responses": {
          "200": {
            "description": "What this key can reach, where each Redact layer processes text, and the limits.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "organisation_id",
                    "scopes",
                    "services",
                    "limits"
                  ],
                  "properties": {
                    "organisation_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "principal": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "type",
                        "integration_id",
                        "name"
                      ],
                      "description": "Only for an organisation integration key: the integration that owns it. A personal key's answer has no principal.",
                      "properties": {
                        "type": {
                          "type": "string",
                          "enum": [
                            "integration"
                          ]
                        },
                        "integration_id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "name": {
                          "description": "The integration's name. A string, or null."
                        }
                      }
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Every scope the key has."
                    },
                    "services": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "service",
                          "edition",
                          "scopes",
                          "hosting",
                          "processing",
                          "limits"
                        ],
                        "properties": {
                          "service": {
                            "type": "string",
                            "description": "redact for an organisation integration key. A personal key can also reach chat, decide and mcp."
                          },
                          "edition": {
                            "type": "string",
                            "enum": [
                              "custom"
                            ],
                            "deprecated": true,
                            "description": "Always custom. Read scopes and processing instead."
                          },
                          "scopes": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "This key's scopes for the service."
                          },
                          "hosting": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "Where the service runs. For Redact, processing.layers names where each layer processes text.",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "environment": {
                                "type": "string"
                              },
                              "shared": {
                                "type": "boolean"
                              },
                              "api": {
                                "type": "object",
                                "additionalProperties": true,
                                "properties": {
                                  "provider": {
                                    "type": "string"
                                  },
                                  "name": {
                                    "type": "string"
                                  },
                                  "region": {
                                    "type": "string"
                                  }
                                }
                              },
                              "processing": {
                                "type": "object",
                                "additionalProperties": true,
                                "properties": {
                                  "kind": {
                                    "type": "string",
                                    "description": "per-layer for Redact."
                                  },
                                  "provider": {
                                    "type": "string"
                                  },
                                  "layers": {
                                    "type": "array",
                                    "items": {
                                      "type": "object",
                                      "additionalProperties": false,
                                      "required": [
                                        "detector",
                                        "processing",
                                        "provider",
                                        "location",
                                        "configured"
                                      ],
                                      "properties": {
                                        "detector": {
                                          "type": "string",
                                          "enum": [
                                            "regex",
                                            "ner",
                                            "llm"
                                          ],
                                          "description": "The detectors value this layer serves."
                                        },
                                        "processing": {
                                          "type": "string",
                                          "enum": [
                                            "local",
                                            "eu-cloud"
                                          ],
                                          "description": "The processing value that selects this layer."
                                        },
                                        "provider": {
                                          "type": "string",
                                          "enum": [
                                            "komplai"
                                          ]
                                        },
                                        "location": {
                                          "type": "string",
                                          "description": "Where the layer processes text, for example EU · Germany or Local infrastructure · Denmark. For ner: its default place."
                                        },
                                        "configured": {
                                          "type": "boolean",
                                          "description": "Whether the layer is set up for you now. It reports setup, not live health."
                                        },
                                        "ner_deployment": {
                                          "type": "string",
                                          "enum": [
                                            "eu-de",
                                            "local"
                                          ],
                                          "description": "ner: the place NER runs when a request names none. Missing when the server has no default place set up: then configured is false, no place has default true, and a request must name its place."
                                        },
                                        "places": {
                                          "type": "array",
                                          "items": {
                                            "type": "object",
                                            "additionalProperties": false,
                                            "required": [
                                              "id",
                                              "location",
                                              "available",
                                              "reason",
                                              "default",
                                              "detect"
                                            ],
                                            "properties": {
                                              "id": {
                                                "type": "string",
                                                "enum": [
                                                  "eu-de",
                                                  "local"
                                                ],
                                                "description": "The value for nerDeployment."
                                              },
                                              "location": {
                                                "type": "string",
                                                "description": "Where NER processes text there: EU · Germany or Local infrastructure · Denmark."
                                              },
                                              "available": {
                                                "type": "boolean",
                                                "description": "Whether a text operation with detectors can use the place now. While more than one place is set up, this is the answer of a health check made in the last 30 seconds."
                                              },
                                              "reason": {
                                                "type": "string",
                                                "nullable": true,
                                                "enum": [
                                                  "unconfigured",
                                                  "unavailable",
                                                  "not_allowed",
                                                  null
                                                ],
                                                "description": "null when available. unconfigured: komplai has not switched the place on for the API. unavailable: it is set up but did not answer its health check. not_allowed: your organisation does not allow local processing through the API, and NER on local infrastructure is local processing (403 processing_choice_not_allowed)."
                                              },
                                              "default": {
                                                "type": "boolean",
                                                "description": "Where NER runs when a request names no place."
                                              },
                                              "detect": {
                                                "type": "object",
                                                "additionalProperties": false,
                                                "required": [
                                                  "available",
                                                  "reason"
                                                ],
                                                "description": "The same for POST /v1/redact/detect and the text operations without detectors, which run NER through another komplai service that reaches each place with its own settings. A place can be available for one and not the other.",
                                                "properties": {
                                                  "available": {
                                                    "type": "boolean",
                                                    "description": "Whether those operations can use the place now."
                                                  },
                                                  "reason": {
                                                    "type": "string",
                                                    "nullable": true,
                                                    "enum": [
                                                      "unconfigured",
                                                      "unavailable",
                                                      "not_allowed",
                                                      null
                                                    ],
                                                    "description": "null when available. unconfigured: the place is not switched on for them. unavailable: it is set up but did not answer its health check. not_allowed: as for the operations with detectors."
                                                  }
                                                }
                                              }
                                            }
                                          },
                                          "description": "ner: every place NER can run, with whether a run can use it now. Send one as nerDeployment."
                                        },
                                        "models": {
                                          "type": "array",
                                          "items": {
                                            "anyOf": [
                                              {
                                                "type": "object",
                                                "additionalProperties": false,
                                                "required": [
                                                  "id",
                                                  "name",
                                                  "status",
                                                  "default",
                                                  "places"
                                                ],
                                                "properties": {
                                                  "id": {
                                                    "type": "string",
                                                    "description": "The value for nerModel."
                                                  },
                                                  "name": {
                                                    "type": "string"
                                                  },
                                                  "status": {
                                                    "type": "string",
                                                    "enum": [
                                                      "stable",
                                                      "beta",
                                                      "alpha"
                                                    ]
                                                  },
                                                  "default": {
                                                    "type": "boolean",
                                                    "description": "The model a run uses when it names none."
                                                  },
                                                  "places": {
                                                    "type": "array",
                                                    "items": {
                                                      "type": "string",
                                                      "enum": [
                                                        "eu-de",
                                                        "local"
                                                      ]
                                                    },
                                                    "description": "The places the model runs at."
                                                  }
                                                }
                                              },
                                              {
                                                "type": "object",
                                                "additionalProperties": false,
                                                "required": [
                                                  "id",
                                                  "name",
                                                  "available",
                                                  "reason",
                                                  "default"
                                                ],
                                                "properties": {
                                                  "id": {
                                                    "type": "string",
                                                    "description": "The value for model."
                                                  },
                                                  "name": {
                                                    "type": "string"
                                                  },
                                                  "available": {
                                                    "type": "boolean",
                                                    "description": "Whether a run can name the model now."
                                                  },
                                                  "reason": {
                                                    "type": "string",
                                                    "nullable": true,
                                                    "enum": [
                                                      "not_validated",
                                                      "not_offered",
                                                      "unreachable",
                                                      "unconfigured",
                                                      null
                                                    ],
                                                    "description": "null when available. not_validated: not validated for Redact, so it cannot be chosen. not_offered: validated, but not offered right now. unreachable: the list of local models could not be read. unconfigured: the place is not set up."
                                                  },
                                                  "default": {
                                                    "type": "boolean",
                                                    "description": "The model a run uses here when it names none."
                                                  }
                                                }
                                              }
                                            ]
                                          },
                                          "description": "ner: the NER models you may choose (nerModel). llm: the language models at this processing (model), including models that are not validated for Redact, which cannot be chosen. The eu-cloud entry lists models only while your organisation's EU route is enabled."
                                        },
                                        "available": {
                                          "type": "boolean",
                                          "description": "llm with processing local: whether a local model can run Redact now."
                                        },
                                        "reason": {
                                          "type": "string",
                                          "nullable": true,
                                          "enum": [
                                            "unconfigured",
                                            "unreachable",
                                            "not_offered",
                                            null
                                          ],
                                          "description": "llm with processing local: null when available, else why not."
                                        }
                                      }
                                    },
                                    "description": "Redact: one entry per layer and processing choice. The eu-cloud entry appears only when your organisation has an EU route."
                                  }
                                }
                              }
                            }
                          },
                          "processing": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "What the organisation's agreement allows for this service.",
                            "properties": {
                              "data_mode": {
                                "type": "string",
                                "enum": [
                                  "synthetic",
                                  "personal"
                                ],
                                "description": "synthetic: use test data only. personal: personal data under the agreement in agreement_reference."
                              },
                              "agreement_reference": {
                                "description": "The data processing agreement that covers personal data. A string, or null."
                              },
                              "modes": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              },
                              "text_contract": {
                                "type": "object",
                                "additionalProperties": true,
                                "description": "Redact with redact:process: the request options and limits that apply to this key.",
                                "properties": {
                                  "supported_engines": {
                                    "type": "array",
                                    "items": {
                                      "type": "string"
                                    },
                                    "description": "pipeline, plus agentic for a person whose organisation has the preview."
                                  },
                                  "detector_choices": {
                                    "type": "array",
                                    "items": {
                                      "type": "string",
                                      "enum": [
                                        "regex",
                                        "ner",
                                        "llm"
                                      ]
                                    }
                                  },
                                  "local_configured_detectors": {
                                    "type": "array",
                                    "items": {
                                      "type": "string",
                                      "enum": [
                                        "regex",
                                        "ner",
                                        "llm"
                                      ]
                                    },
                                    "description": "The layers set up on local infrastructure now."
                                  },
                                  "default_mode": {
                                    "type": "string"
                                  },
                                  "default_processing": {
                                    "type": "string",
                                    "enum": [
                                      "local"
                                    ]
                                  },
                                  "synchronous_max_utf16_chars": {
                                    "type": "integer"
                                  },
                                  "job_max_utf16_chars": {
                                    "type": "integer"
                                  },
                                  "file_input": {
                                    "type": "object",
                                    "additionalProperties": true,
                                    "description": "PDF and Word input (beta). Missing when it is switched off."
                                  },
                                  "jobs_accepting_submissions": {
                                    "type": "boolean",
                                    "description": "Whether POST /v1/redact/jobs accepts text jobs now."
                                  }
                                }
                              },
                              "storage": {
                                "type": "object",
                                "additionalProperties": true,
                                "description": "What is stored, and for how long."
                              }
                            }
                          },
                          "limits": {
                            "type": "object",
                            "additionalProperties": false,
                            "properties": {
                              "requests_per_minute": {
                                "type": "integer",
                                "description": "Requests per minute for the service: per person for a personal key, shared with that person's other keys and use of the hub; per integration for an organisation integration key, shared by its keys and kept across rotation."
                              }
                            }
                          }
                        }
                      }
                    },
                    "hosting": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "Only when the key reaches one service: a summary of its hosting.",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "environment": {
                          "type": "string"
                        },
                        "shared": {
                          "type": "boolean"
                        },
                        "api": {
                          "type": "object",
                          "additionalProperties": true,
                          "properties": {
                            "provider": {
                              "type": "string"
                            },
                            "name": {
                              "type": "string"
                            },
                            "region": {
                              "type": "string"
                            }
                          }
                        },
                        "ner": {
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "provider",
                            "name",
                            "region",
                            "deployment",
                            "places"
                          ],
                          "description": "Redact: where NER runs when a request names no place, and every place with whether a run can use it now. The same places as the ner layer in services[].hosting.processing.layers. Missing when the server has no default place set up.",
                          "properties": {
                            "provider": {
                              "type": "string",
                              "enum": [
                                "komplai"
                              ]
                            },
                            "name": {
                              "type": "string"
                            },
                            "region": {
                              "type": "string",
                              "description": "The default place's location: EU · Germany or Local infrastructure · Denmark."
                            },
                            "deployment": {
                              "type": "string",
                              "enum": [
                                "eu-de",
                                "local"
                              ],
                              "description": "The default place: the nerDeployment a request gets when it names none."
                            },
                            "places": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "additionalProperties": false,
                                "required": [
                                  "id",
                                  "location",
                                  "available",
                                  "reason",
                                  "default",
                                  "detect"
                                ],
                                "properties": {
                                  "id": {
                                    "type": "string",
                                    "enum": [
                                      "eu-de",
                                      "local"
                                    ],
                                    "description": "The value for nerDeployment."
                                  },
                                  "location": {
                                    "type": "string",
                                    "description": "Where NER processes text there: EU · Germany or Local infrastructure · Denmark."
                                  },
                                  "available": {
                                    "type": "boolean",
                                    "description": "Whether a text operation with detectors can use the place now. While more than one place is set up, this is the answer of a health check made in the last 30 seconds."
                                  },
                                  "reason": {
                                    "type": "string",
                                    "nullable": true,
                                    "enum": [
                                      "unconfigured",
                                      "unavailable",
                                      "not_allowed",
                                      null
                                    ],
                                    "description": "null when available. unconfigured: komplai has not switched the place on for the API. unavailable: it is set up but did not answer its health check. not_allowed: your organisation does not allow local processing through the API, and NER on local infrastructure is local processing (403 processing_choice_not_allowed)."
                                  },
                                  "default": {
                                    "type": "boolean",
                                    "description": "Where NER runs when a request names no place."
                                  },
                                  "detect": {
                                    "type": "object",
                                    "additionalProperties": false,
                                    "required": [
                                      "available",
                                      "reason"
                                    ],
                                    "description": "The same for POST /v1/redact/detect and the text operations without detectors, which run NER through another komplai service that reaches each place with its own settings. A place can be available for one and not the other.",
                                    "properties": {
                                      "available": {
                                        "type": "boolean",
                                        "description": "Whether those operations can use the place now."
                                      },
                                      "reason": {
                                        "type": "string",
                                        "nullable": true,
                                        "enum": [
                                          "unconfigured",
                                          "unavailable",
                                          "not_allowed",
                                          null
                                        ],
                                        "description": "null when available. unconfigured: the place is not switched on for them. unavailable: it is set up but did not answer its health check. not_allowed: as for the operations with detectors."
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "processing": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "Only when the key reaches one service.",
                      "properties": {
                        "data_mode": {
                          "type": "string",
                          "enum": [
                            "synthetic",
                            "personal"
                          ]
                        }
                      }
                    },
                    "limits": {
                      "type": "object",
                      "additionalProperties": false,
                      "properties": {
                        "requests_per_minute": {
                          "type": "integer",
                          "description": "Only when the key reaches one service. Requests per minute for the service: per person for a personal key, shared with that person's other keys and use of the hub; per integration for an organisation integration key, shared by its keys and kept across rotation."
                        },
                        "organisation_requests_per_minute": {
                          "type": "integer"
                        },
                        "shared_requests_per_minute": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "organisationKey": {
                    "summary": "An organisation integration key with Redact",
                    "value": {
                      "organisation_id": "5b0d6c2e-8f3a-4d1b-9c7e-2a4f6b8d0e13",
                      "principal": {
                        "type": "integration",
                        "integration_id": "7a9c1e3f-5b7d-4f9a-8c2e-4d6f8a0b2c4e",
                        "name": "Sagsbehandlingsrobot"
                      },
                      "scopes": [
                        "access:read",
                        "redact:detect",
                        "redact:process"
                      ],
                      "services": [
                        {
                          "service": "redact",
                          "edition": "custom",
                          "scopes": [
                            "access:read",
                            "redact:detect",
                            "redact:process"
                          ],
                          "hosting": {
                            "id": "komplai-local",
                            "environment": "prod",
                            "shared": true,
                            "api": {
                              "provider": "komplai",
                              "name": "Komplai EU cloud",
                              "region": "EU · Germany"
                            },
                            "processing": {
                              "kind": "per-layer",
                              "provider": "komplai",
                              "layers": [
                                {
                                  "detector": "regex",
                                  "processing": "local",
                                  "provider": "komplai",
                                  "location": "EU · Germany",
                                  "configured": true
                                },
                                {
                                  "detector": "ner",
                                  "processing": "local",
                                  "provider": "komplai",
                                  "location": "EU · Germany",
                                  "configured": true,
                                  "ner_deployment": "eu-de",
                                  "places": [
                                    {
                                      "id": "eu-de",
                                      "location": "EU · Germany",
                                      "available": true,
                                      "reason": null,
                                      "default": true,
                                      "detect": {
                                        "available": true,
                                        "reason": null
                                      }
                                    },
                                    {
                                      "id": "local",
                                      "location": "Local infrastructure · Denmark",
                                      "available": false,
                                      "reason": "unconfigured",
                                      "default": false,
                                      "detect": {
                                        "available": false,
                                        "reason": "unconfigured"
                                      }
                                    }
                                  ],
                                  "models": [
                                    {
                                      "id": "danish-pii-ner-v1.3.0",
                                      "name": "Dansk NER 1.3.0",
                                      "status": "stable",
                                      "default": true,
                                      "places": [
                                        "eu-de",
                                        "local"
                                      ]
                                    }
                                  ]
                                },
                                {
                                  "detector": "llm",
                                  "processing": "local",
                                  "provider": "komplai",
                                  "location": "Local infrastructure · Denmark",
                                  "configured": true,
                                  "available": true,
                                  "reason": null,
                                  "models": [
                                    {
                                      "id": "qwen3.8-27b-q8",
                                      "name": "Qwen 3.8 · 27B (Q8)",
                                      "available": true,
                                      "reason": null,
                                      "default": true
                                    }
                                  ]
                                },
                                {
                                  "detector": "llm",
                                  "processing": "eu-cloud",
                                  "provider": "komplai",
                                  "location": "EU · France",
                                  "configured": true,
                                  "models": [
                                    {
                                      "id": "mistral-small-3.2-24b-instruct-2506",
                                      "name": "Mistral Small 3.2 (24B)",
                                      "available": true,
                                      "reason": null,
                                      "default": true
                                    },
                                    {
                                      "id": "qwen3.5-397b-a17b",
                                      "name": "Qwen3.5 (397B-A17B)",
                                      "available": false,
                                      "reason": "not_validated",
                                      "default": false
                                    }
                                  ]
                                }
                              ]
                            }
                          },
                          "processing": {
                            "data_mode": "synthetic",
                            "agreement_reference": null,
                            "modes": [
                              "regex",
                              "ner",
                              "semantic"
                            ],
                            "text_contract": {
                              "supported_engines": [
                                "pipeline"
                              ],
                              "detector_choices": [
                                "regex",
                                "ner",
                                "llm"
                              ],
                              "explicit_detector_selection": true,
                              "true_ner_only": true,
                              "local_configured_detectors": [
                                "regex",
                                "ner",
                                "llm"
                              ],
                              "default_mode": "ner",
                              "default_processing": "local",
                              "policy": "all-detected",
                              "offset_encoding": "utf-16",
                              "synchronous_max_utf16_chars": 750000,
                              "job_max_utf16_chars": 2000000,
                              "file_input": {
                                "beta": true,
                                "operations": [
                                  "POST /v1/redact/file",
                                  "POST /v1/redact/file/jobs"
                                ],
                                "formats": [
                                  "pdf",
                                  "docx"
                                ],
                                "max_file_bytes": 52428800,
                                "max_pdf_pages": 2000,
                                "extraction_timeout_ms": 120000,
                                "ocr": false,
                                "pages_without_text": "refused unless allowPagesWithoutText",
                                "extraction": {
                                  "step": "file-extraction",
                                  "processing": "local",
                                  "provider": "komplai",
                                  "location": "EU · Germany",
                                  "storage": "memory",
                                  "ocr": false,
                                  "retention": "The file and its extracted text are held in memory while the request runs and are not stored. POST /v1/redact/file/jobs stores the extracted text encrypted as a text job, with that job's retention."
                                }
                              },
                              "result_max_bytes": 33554432,
                              "complexity_limits": {
                                "candidate_matches_per_utf16_char": 2,
                                "candidate_matches_base": 1000,
                                "matched_utf16_chars_per_utf16_char": 16,
                                "matched_utf16_chars_base": 16000,
                                "coreference_work_per_utf16_char": 4,
                                "coreference_work_base": 4000,
                                "organisation_rule_timeout_base_ms": 5000,
                                "organisation_rule_timeout_ns_per_utf16_char_per_rule": 20,
                                "organisation_rule_workers_per_process": 2,
                                "organisation_rule_queue": false,
                                "exceeded_error": "processing_complexity_exceeded"
                              },
                              "jobs_accepting_submissions": true,
                              "semantic_requires_configured_approved_runtime": true,
                              "eu_cloud_requires_explicit_selection_and_shared_ai_budget": true
                            },
                            "storage": {
                              "synchronous_input": "transient_processor_memory",
                              "synchronous_result": "response_only",
                              "job_input": "encrypted_while_queued_or_running",
                              "job_result": "encrypted_until_agreed_expiry_or_deletion",
                              "result_read_deletes": false,
                              "personal_job_storage_policy_required": true,
                              "backup_retention": "separate_infrastructure_agreement",
                              "usage_and_audit": "metadata_only_separate_retention"
                            }
                          },
                          "limits": {
                            "requests_per_minute": 60
                          }
                        }
                      ],
                      "hosting": {
                        "id": "komplai-local",
                        "environment": "prod",
                        "shared": true,
                        "api": {
                          "provider": "komplai",
                          "name": "Komplai EU cloud",
                          "region": "EU · Germany"
                        },
                        "ner": {
                          "provider": "komplai",
                          "name": "Komplai EU cloud",
                          "region": "EU · Germany",
                          "deployment": "eu-de",
                          "places": [
                            {
                              "id": "eu-de",
                              "location": "EU · Germany",
                              "available": true,
                              "reason": null,
                              "default": true,
                              "detect": {
                                "available": true,
                                "reason": null
                              }
                            },
                            {
                              "id": "local",
                              "location": "Local infrastructure · Denmark",
                              "available": false,
                              "reason": "unconfigured",
                              "default": false,
                              "detect": {
                                "available": false,
                                "reason": "unconfigured"
                              }
                            }
                          ]
                        }
                      },
                      "processing": {
                        "data_mode": "synthetic"
                      },
                      "limits": {
                        "requests_per_minute": 60,
                        "organisation_requests_per_minute": 120,
                        "shared_requests_per_minute": 300
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not issued with it, the person behind a personal key is no longer assigned Redact, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `access_service_unavailable`: Keys and access could not be checked.\n- `chat_cloud_unavailable`: The organisation's EU cloud setup cannot be used right now.\n- `redact_capabilities_unavailable`: Redact's processing details could not be read.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "nerDeployment": {
                      "type": "string",
                      "enum": [
                        "eu-de",
                        "local"
                      ],
                      "description": "With ner_location_unavailable: the NER place that cannot be used now."
                    },
                    "estimatedSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long the run is expected to take with an empty queue, from komplai's measured speeds."
                    },
                    "holdSeconds": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "With use_job: how long a synchronous call may take."
                    },
                    "hint": {
                      "type": "string",
                      "description": "With engine_retired: what to send instead."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  },
                  "chat_cloud_unavailable": {
                    "summary": "chat_cloud_unavailable",
                    "value": {
                      "error": "chat_cloud_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/chat_cloud_unavailable"
                    }
                  },
                  "redact_capabilities_unavailable": {
                    "summary": "redact_capabilities_unavailable",
                    "value": {
                      "error": "redact_capabilities_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/redact_capabilities_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/models": {
      "get": {
        "operationId": "listChatModels",
        "summary": "List the permitted chat models",
        "description": "The models this key may use for POST /v1/chat/completions, with their context and output limits, the reasoning efforts they take and, in komplai's EU cloud, the provider's token rates. processing chooses which processing to list: komplai's local infrastructure, komplai's EU cloud or one of your organisation's own connections. It sends no text and runs no model. Needs a personal key with chat:complete.",
        "tags": [
          "Chat"
        ],
        "x-komplai-stability": "stable",
        "security": [
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "chat:complete"
        ],
        "x-komplai-principals": [
          "person"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "processing",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^(komplai-local|local|eu-cloud|connection:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12})$"
            },
            "description": "Which processing to list. komplai-local: komplai's local infrastructure in Denmark, with no AI charge (local is accepted too). eu-cloud: komplai's EU cloud, when your organisation has EU processing approved for Chat; usage is metered at the provider's price and counts towards the organisation's monthly AI spending cap. connection:<id>: one of your organisation's own connections that its administrator has made available through the API, shown with its value on the hub's AI processing page; the organisation pays the provider itself, and the usage does not count towards the cap. Leave it out to list your organisation's Chat processing: local infrastructure, or EU cloud when that is what your organisation's Chat uses.",
            "examples": {
              "local": {
                "value": "komplai-local"
              },
              "euCloud": {
                "value": "eu-cloud"
              },
              "connection": {
                "value": "connection:6d3f9a2e-1b4c-4e7d-8a5f-0c2b9e4d7a31"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The models this key may use at that processing. On local infrastructure: the models your organisation has switched on, or every selectable model while it has switched on none. In EU cloud: the models on your organisation's EU route, with their rates. For a connection: the one model or router it names. The list grants no access of its own.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "object",
                    "data"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "id",
                          "object",
                          "owned_by",
                          "name"
                        ],
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "The value for model in POST /v1/chat/completions."
                          },
                          "object": {
                            "type": "string",
                            "enum": [
                              "model"
                            ]
                          },
                          "owned_by": {
                            "type": "string",
                            "description": "komplai for komplai's processing; for one of the organisation's own connections, the provider it uses, such as openrouter."
                          },
                          "name": {
                            "type": "string",
                            "description": "The name people see, or the connection's name."
                          },
                          "context_tokens": {
                            "type": "integer",
                            "description": "The model's context in tokens: input and output together. Missing for a connection."
                          },
                          "max_output_tokens": {
                            "type": "integer",
                            "description": "The most max_tokens the model takes, at most 32,768. Missing for a connection."
                          },
                          "reasoning_efforts": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "none",
                                "low",
                                "medium",
                                "high",
                                "xhigh",
                                "max"
                              ]
                            },
                            "description": "The reasoning_effort values the model accepts. none means it does not reason. Missing for a connection."
                          },
                          "rates": {
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "currency",
                              "unit",
                              "input",
                              "output",
                              "cached_input"
                            ],
                            "description": "Only for komplai's EU cloud: the provider's prices, which usage is metered at and counted towards the organisation's monthly AI spending cap. Local infrastructure has no AI charge and no rates.",
                            "properties": {
                              "currency": {
                                "type": "string",
                                "enum": [
                                  "DKK"
                                ]
                              },
                              "unit": {
                                "type": "string",
                                "enum": [
                                  "micro_dkk_per_million_tokens"
                                ],
                                "description": "Each rate is in millionths of a krone per million tokens, so 1000000 is one krone per million tokens."
                              },
                              "input": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Per million input tokens."
                              },
                              "output": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Per million output tokens. Reasoning counts as output."
                              },
                              "cached_input": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Per million cached input tokens; the input rate where the provider has no cached rate."
                              }
                            }
                          },
                          "processing": {
                            "type": "string",
                            "description": "Only for a connection: the value to send as processing."
                          }
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "local": {
                    "summary": "Local infrastructure",
                    "value": {
                      "object": "list",
                      "data": [
                        {
                          "id": "qwen3.8-27b",
                          "object": "model",
                          "owned_by": "komplai",
                          "name": "Qwen3.8 27B",
                          "context_tokens": 32768,
                          "max_output_tokens": 4000,
                          "reasoning_efforts": [
                            "none",
                            "low",
                            "medium",
                            "high"
                          ]
                        },
                        {
                          "id": "qwen3.8-27b-q8",
                          "object": "model",
                          "owned_by": "komplai",
                          "name": "Qwen3.8 27B (Q8)",
                          "context_tokens": 32768,
                          "max_output_tokens": 4000,
                          "reasoning_efforts": [
                            "none",
                            "low",
                            "medium",
                            "high"
                          ]
                        }
                      ]
                    }
                  },
                  "euCloud": {
                    "summary": "komplai's EU cloud (two of the models)",
                    "description": "An EU route lists every model on it; two are shown here.",
                    "value": {
                      "object": "list",
                      "data": [
                        {
                          "id": "mistral-small-3.2-24b-instruct-2506",
                          "object": "model",
                          "owned_by": "komplai",
                          "name": "Mistral Small 3.2 (24B)",
                          "context_tokens": 128000,
                          "max_output_tokens": 32768,
                          "reasoning_efforts": [
                            "none"
                          ],
                          "rates": {
                            "currency": "DKK",
                            "unit": "micro_dkk_per_million_tokens",
                            "input": 1119000,
                            "output": 2611000,
                            "cached_input": 1119000
                          }
                        },
                        {
                          "id": "qwen3.5-397b-a17b",
                          "object": "model",
                          "owned_by": "komplai",
                          "name": "Qwen3.5 (397B-A17B)",
                          "context_tokens": 250000,
                          "max_output_tokens": 16384,
                          "reasoning_efforts": [
                            "none",
                            "low",
                            "medium",
                            "high"
                          ],
                          "rates": {
                            "currency": "DKK",
                            "unit": "micro_dkk_per_million_tokens",
                            "input": 4476000,
                            "output": 26856000,
                            "cached_input": 4476000
                          }
                        }
                      ]
                    }
                  },
                  "connection": {
                    "summary": "One of the organisation's own connections",
                    "value": {
                      "object": "list",
                      "data": [
                        {
                          "id": "openrouter/free",
                          "object": "model",
                          "owned_by": "openrouter",
                          "name": "OpenRouter, gratis modeller",
                          "processing": "connection:6d3f9a2e-1b4c-4e7d-8a5f-0c2b9e4d7a31"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `invalid_input`: processing is not komplai-local, local, eu-cloud or connection: followed by a connection id.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_input": {
                    "summary": "invalid_input",
                    "value": {
                      "error": "invalid_input",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_input"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `integration_not_permitted`: An organisation integration key cannot use this operation; it needs a personal key.\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not created with it, its person is no longer assigned the app, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `processing_not_enabled`: processing eu-cloud was asked for, but EU processing is not approved and enabled for Chat in your organisation, or its agreement keeps processing local; or, for a connection, your Chat is not set up.\n- `processing_choice_not_allowed`: Your organisation does not allow this processing through the API.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "integration_not_permitted": {
                    "summary": "integration_not_permitted",
                    "value": {
                      "error": "integration_not_permitted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/integration_not_permitted"
                    }
                  },
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "processing_not_enabled": {
                    "summary": "processing_not_enabled",
                    "value": {
                      "error": "processing_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_enabled"
                    }
                  },
                  "processing_choice_not_allowed": {
                    "summary": "processing_choice_not_allowed",
                    "value": {
                      "error": "processing_choice_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_choice_not_allowed"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.\n\n- `connection_not_found`: No connection with this id is available through the API for you.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "connection_not_found": {
                    "summary": "connection_not_found",
                    "value": {
                      "error": "connection_not_found",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/connection_not_found"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `chat_cloud_unavailable`: Your organisation's EU cloud setup or the model list cannot be read right now.\n- `access_service_unavailable`: Keys and access could not be checked.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "chat_cloud_unavailable": {
                    "summary": "chat_cloud_unavailable",
                    "value": {
                      "error": "chat_cloud_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/chat_cloud_unavailable"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/chat/completions": {
      "post": {
        "operationId": "createChatCompletion",
        "summary": "Create a chat completion",
        "description": "An OpenAI-compatible chat completion with text only, returned whole (stream false). It runs where processing says: komplai's local infrastructure in Denmark, komplai's EU cloud when your organisation has it approved for Chat, or one of your organisation's own connections; without processing, where your organisation's Chat runs. The API never moves a call to other processing or another model, also not when the chosen one fails. At most 32 messages and 80,000 UTF-16 code units of text; the model has 120 seconds to answer. In EU cloud, usage is metered at the provider's price and counts towards the organisation's monthly AI spending cap; the call stops when the cap, the organisation's own limit or your personal limit is reached. Needs a personal key with chat:complete.",
        "tags": [
          "Chat"
        ],
        "x-komplai-stability": "stable",
        "security": [
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "chat:complete"
        ],
        "x-komplai-principals": [
          "person"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "messages"
                ],
                "description": "An OpenAI-compatible chat request with text only. A field that is not listed here, stream true, tools, images or files answer 400 invalid_input.",
                "properties": {
                  "messages": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 32,
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "role",
                        "content"
                      ],
                      "properties": {
                        "role": {
                          "type": "string",
                          "enum": [
                            "system",
                            "user",
                            "assistant"
                          ]
                        },
                        "content": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 80000,
                          "pattern": "\\S",
                          "description": "Text only, with at least one character that is not white space. Images, files and tool results are refused."
                        }
                      }
                    },
                    "description": "The conversation, oldest first: at most 32 messages and 80,000 UTF-16 code units of content together."
                  },
                  "model": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "pattern": "\\S",
                    "description": "A model id from GET /v1/models at the same processing, with at least one character that is not white space. Leave it out for the default there: the model you chose in the hub, else your organisation's default. A model you may not use answers 409 model_not_enabled; the API never picks another one. For a connection, leave it out or send the model the connection names."
                  },
                  "processing": {
                    "type": "string",
                    "pattern": "^(komplai-local|local|eu-cloud|connection:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12})$",
                    "description": "Where the model runs. komplai-local: komplai's local infrastructure in Denmark, with no AI charge (local is accepted too). eu-cloud: komplai's EU cloud, when your organisation has EU processing approved for Chat; usage is metered at the provider's price and counts towards the organisation's monthly AI spending cap. connection:<id>: one of your organisation's own connections that its administrator has made available through the API, shown with its value on the hub's AI processing page; the organisation pays the provider itself, and the usage does not count towards the cap. Leave it out for your organisation's Chat processing. The API never moves a call to other processing, also not when the chosen one fails."
                  },
                  "max_tokens": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 32768,
                    "default": 2000,
                    "description": "The most tokens the answer may have. The model's own max_output_tokens (GET /v1/models) is lower for most models, and the input and max_tokens together must fit its context; otherwise 413 context_limit."
                  },
                  "temperature": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 2,
                    "description": "Sampling temperature. Leave it out for the model's default. A connection to Anthropic takes at most 1."
                  },
                  "stream": {
                    "type": "boolean",
                    "enum": [
                      false
                    ],
                    "default": false,
                    "description": "Always false: the whole answer is returned at once."
                  },
                  "response_format": {
                    "description": "Leave it out for plain text. json_object asks for a JSON object; json_schema asks for JSON that follows your schema. A connection to Anthropic takes only text.",
                    "oneOf": [
                      {
                        "title": "Text or JSON object",
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "type"
                        ],
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "text",
                              "json_object"
                            ]
                          }
                        }
                      },
                      {
                        "title": "JSON schema",
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "type",
                          "json_schema"
                        ],
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "json_schema"
                            ]
                          },
                          "json_schema": {
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "name",
                              "schema"
                            ],
                            "properties": {
                              "name": {
                                "type": "string",
                                "pattern": "^[A-Za-z0-9_-]{1,64}$"
                              },
                              "schema": {
                                "type": "object",
                                "additionalProperties": true,
                                "description": "A JSON Schema for the answer: at most 32,768 bytes for the whole response_format, 24 levels deep and 2,048 values. A $ref must point inside the schema (start with #)."
                              },
                              "strict": {
                                "type": "boolean",
                                "description": "Ask the model to follow the schema exactly."
                              },
                              "description": {
                                "type": "string",
                                "maxLength": 1024
                              }
                            }
                          }
                        }
                      }
                    ]
                  },
                  "reasoning_effort": {
                    "type": "string",
                    "enum": [
                      "none",
                      "low",
                      "medium",
                      "high",
                      "xhigh",
                      "max"
                    ],
                    "description": "How much the model reasons before it answers, from the model's reasoning_efforts in GET /v1/models. Leave it out for the model's default. A value the model does not take answers 409 reasoning_not_enabled; a connection takes only none."
                  }
                }
              },
              "examples": {
                "accountDefault": {
                  "summary": "Your default model and processing",
                  "description": "Without model and processing, the call runs where your organisation's Chat runs, with your default model there.",
                  "value": {
                    "messages": [
                      {
                        "role": "system",
                        "content": "Svar kort og på dansk."
                      },
                      {
                        "role": "user",
                        "content": "Hvor lang tid har jeg til at klage over en afgørelse om boligstøtte?"
                      }
                    ],
                    "max_tokens": 256
                  }
                },
                "localModel": {
                  "summary": "A chosen model on local infrastructure",
                  "description": "processing komplai-local runs the model on komplai's local infrastructure in Denmark, with no AI charge. The answer can take up to a minute while the shared queue is busy.",
                  "value": {
                    "processing": "komplai-local",
                    "model": "qwen3.8-27b",
                    "messages": [
                      {
                        "role": "user",
                        "content": "Hvor lang tid har jeg til at klage over en afgørelse om boligstøtte?"
                      }
                    ],
                    "max_tokens": 256,
                    "reasoning_effort": "none"
                  }
                },
                "structured": {
                  "summary": "JSON that follows a schema",
                  "description": "response_format json_schema asks for JSON that follows the schema; the answer is in choices[0].message.content as text. Parse it, and check it against your schema before you rely on it.",
                  "value": {
                    "processing": "komplai-local",
                    "model": "qwen3.8-27b",
                    "max_tokens": 256,
                    "messages": [
                      {
                        "role": "user",
                        "content": "Svar med JSON. Hvor lang tid har jeg til at klage over en afgørelse om boligstøtte?"
                      }
                    ],
                    "response_format": {
                      "type": "json_schema",
                      "json_schema": {
                        "name": "klagefrist",
                        "strict": true,
                        "schema": {
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "uger",
                            "kilde"
                          ],
                          "properties": {
                            "uger": {
                              "type": "integer"
                            },
                            "kilde": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "connection": {
                  "summary": "One of the organisation's own connections",
                  "description": "A connection to OpenRouter's free models. model in the answer is the model the router chose, and requested_model the router the connection names.",
                  "value": {
                    "processing": "connection:6d3f9a2e-1b4c-4e7d-8a5f-0c2b9e4d7a31",
                    "messages": [
                      {
                        "role": "user",
                        "content": "Hvor lang tid har jeg til at klage over en afgørelse om boligstøtte?"
                      }
                    ],
                    "max_tokens": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The whole answer, as an OpenAI-compatible chat.completion. Nothing is stored but the call's metadata (who, when, model, tokens).",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "object",
                    "created",
                    "model",
                    "choices",
                    "usage"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "chatcmpl- followed by the id komplai records the call under."
                    },
                    "object": {
                      "type": "string",
                      "enum": [
                        "chat.completion"
                      ]
                    },
                    "created": {
                      "type": "integer",
                      "description": "When the answer was ready, in seconds since 1970 (UTC)."
                    },
                    "model": {
                      "type": "string",
                      "description": "The model that answered. For a connection to a router, the model the router chose; requested_model is the connection's own."
                    },
                    "requested_model": {
                      "type": "string",
                      "description": "Only for a connection: the model or router it names."
                    },
                    "processing": {
                      "type": "string",
                      "description": "Only for a connection: connection:<id>."
                    },
                    "choices": {
                      "type": "array",
                      "minItems": 1,
                      "maxItems": 1,
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "index",
                          "message",
                          "finish_reason"
                        ],
                        "properties": {
                          "index": {
                            "type": "integer",
                            "enum": [
                              0
                            ]
                          },
                          "message": {
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "role",
                              "content"
                            ],
                            "properties": {
                              "role": {
                                "type": "string",
                                "enum": [
                                  "assistant"
                                ]
                              },
                              "content": {
                                "type": "string",
                                "description": "The answer, never empty."
                              }
                            }
                          },
                          "finish_reason": {
                            "type": "string",
                            "enum": [
                              "stop",
                              "length"
                            ],
                            "description": "length: the answer reached max_tokens and is cut off."
                          }
                        }
                      }
                    },
                    "usage": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "prompt_tokens",
                        "completion_tokens"
                      ],
                      "properties": {
                        "prompt_tokens": {
                          "type": "integer",
                          "nullable": true,
                          "description": "Input tokens, as the model reported them; null when it did not."
                        },
                        "completion_tokens": {
                          "type": "integer",
                          "nullable": true,
                          "description": "Output tokens, reasoning included; null when the model did not report them."
                        },
                        "prompt_tokens_details": {
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "cached_tokens"
                          ],
                          "description": "Only when the model reports cached input.",
                          "properties": {
                            "cached_tokens": {
                              "type": "integer",
                              "minimum": 0
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "accountDefault": {
                    "summary": "Your default model and processing",
                    "value": {
                      "id": "chatcmpl-8c1d5e3a-2f7b-4a9c-b6e0-3d4f5a6b7c8d",
                      "object": "chat.completion",
                      "created": 1792483200,
                      "model": "qwen3.8-27b",
                      "choices": [
                        {
                          "index": 0,
                          "message": {
                            "role": "assistant",
                            "content": "Du har normalt 4 uger til at klage, regnet fra den dag du fik afgørelsen. Fristen står i afgørelsens klagevejledning."
                          },
                          "finish_reason": "stop"
                        }
                      ],
                      "usage": {
                        "prompt_tokens": 41,
                        "completion_tokens": 33
                      }
                    }
                  },
                  "localModel": {
                    "summary": "A chosen model on local infrastructure",
                    "value": {
                      "id": "chatcmpl-8c1d5e3a-2f7b-4a9c-b6e0-3d4f5a6b7c8d",
                      "object": "chat.completion",
                      "created": 1792483200,
                      "model": "qwen3.8-27b",
                      "choices": [
                        {
                          "index": 0,
                          "message": {
                            "role": "assistant",
                            "content": "Du har normalt 4 uger til at klage, regnet fra den dag du fik afgørelsen. Fristen står i afgørelsens klagevejledning."
                          },
                          "finish_reason": "stop"
                        }
                      ],
                      "usage": {
                        "prompt_tokens": 27,
                        "completion_tokens": 33
                      }
                    }
                  },
                  "structured": {
                    "summary": "JSON that follows a schema",
                    "value": {
                      "id": "chatcmpl-8c1d5e3a-2f7b-4a9c-b6e0-3d4f5a6b7c8d",
                      "object": "chat.completion",
                      "created": 1792483200,
                      "model": "qwen3.8-27b",
                      "choices": [
                        {
                          "index": 0,
                          "message": {
                            "role": "assistant",
                            "content": "{\"uger\":4,\"kilde\":\"Afgørelsens klagevejledning\"}"
                          },
                          "finish_reason": "stop"
                        }
                      ],
                      "usage": {
                        "prompt_tokens": 64,
                        "completion_tokens": 15
                      }
                    }
                  },
                  "connection": {
                    "summary": "One of the organisation's own connections",
                    "value": {
                      "id": "chatcmpl-8c1d5e3a-2f7b-4a9c-b6e0-3d4f5a6b7c8d",
                      "object": "chat.completion",
                      "created": 1792483200,
                      "model": "meta-llama/llama-3.3-70b-instruct:free",
                      "requested_model": "openrouter/free",
                      "processing": "connection:6d3f9a2e-1b4c-4e7d-8a5f-0c2b9e4d7a31",
                      "choices": [
                        {
                          "index": 0,
                          "message": {
                            "role": "assistant",
                            "content": "Du har normalt 4 uger til at klage, regnet fra den dag du fik afgørelsen. Fristen står i afgørelsens klagevejledning."
                          },
                          "finish_reason": "stop"
                        }
                      ],
                      "usage": {
                        "prompt_tokens": 29,
                        "completion_tokens": 35
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `invalid_input`: The body is not a chat request as documented: invalid JSON, Content-Type text/plain, a field that is not listed, a value of the wrong type or out of range, stream true, a message that is blank or not text, more than 32 messages or 80,000 UTF-16 code units, or a response_format schema over its limits. For a connection to Anthropic also response_format other than text, a temperature over 1, or only system messages.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_input": {
                    "summary": "invalid_input",
                    "value": {
                      "error": "invalid_input",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_input"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `integration_not_permitted`: An organisation integration key cannot use this operation; it needs a personal key.\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not created with it, its person is no longer assigned the app, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `app_not_assigned`: The key's person lost the app or their membership while the request was admitted.\n- `processing_not_enabled`: processing eu-cloud was asked for, but EU processing is not approved and enabled for Chat in your organisation, or its agreement keeps processing local.\n- `processing_not_approved`: Your organisation's processing approval for Chat is missing or was withdrawn.\n- `processing_choice_not_allowed`: Your organisation does not allow this processing through the API, or the administrator has not made this connection available through the API.\n- `ai_credit_not_granted`: processing eu-cloud: no monthly AI spending cap is set up for this month.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "integration_not_permitted": {
                    "summary": "integration_not_permitted",
                    "value": {
                      "error": "integration_not_permitted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/integration_not_permitted"
                    }
                  },
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "app_not_assigned": {
                    "summary": "app_not_assigned",
                    "value": {
                      "error": "app_not_assigned",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/app_not_assigned"
                    }
                  },
                  "processing_not_enabled": {
                    "summary": "processing_not_enabled",
                    "value": {
                      "error": "processing_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_enabled"
                    }
                  },
                  "processing_not_approved": {
                    "summary": "processing_not_approved",
                    "value": {
                      "error": "processing_not_approved",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_approved"
                    }
                  },
                  "processing_choice_not_allowed": {
                    "summary": "processing_choice_not_allowed",
                    "value": {
                      "error": "processing_choice_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_choice_not_allowed"
                    }
                  },
                  "ai_credit_not_granted": {
                    "summary": "ai_credit_not_granted",
                    "value": {
                      "error": "ai_credit_not_granted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ai_credit_not_granted"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.\n\n- `connection_not_found`: No connection with this id in your organisation.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "connection_not_found": {
                    "summary": "connection_not_found",
                    "value": {
                      "error": "connection_not_found",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/connection_not_found"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict with the current state.\n\n- `model_not_enabled`: You may not use the model in model at this processing, or it is not offered there right now. For a connection: model is not the model the connection names.\n- `reasoning_not_enabled`: The model does not take this reasoning_effort, or a connection was sent a reasoning_effort other than none.\n- `connection_model_unavailable`: The provider behind the connection does not know the model it names.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "model_not_enabled": {
                    "summary": "model_not_enabled",
                    "value": {
                      "error": "model_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_not_enabled"
                    }
                  },
                  "reasoning_not_enabled": {
                    "summary": "reasoning_not_enabled",
                    "value": {
                      "error": "reasoning_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/reasoning_not_enabled"
                    }
                  },
                  "connection_model_unavailable": {
                    "summary": "connection_model_unavailable",
                    "value": {
                      "error": "connection_model_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/connection_model_unavailable"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Too large.\n\n- `context_limit`: max_tokens is over the model's max_output_tokens, or the input and max_tokens together do not fit the model's context.\n- `Payload Too Large`: The body is over 400,000 bytes.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "context_limit": {
                    "summary": "context_limit",
                    "value": {
                      "error": "context_limit",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/context_limit"
                    }
                  },
                  "payload_too_large": {
                    "summary": "Payload Too Large",
                    "value": {
                      "error": "Payload Too Large",
                      "statusCode": 413,
                      "code": "FST_ERR_CTP_BODY_TOO_LARGE",
                      "message": "Request body is too large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/payload-too-large"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.\n\n- `Unsupported Media Type`: The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "unsupported_media_type": {
                    "summary": "Unsupported Media Type",
                    "value": {
                      "error": "Unsupported Media Type",
                      "statusCode": 415,
                      "code": "FST_ERR_CTP_INVALID_MEDIA_TYPE",
                      "message": "Unsupported Media Type",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported-media-type"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.\n- `ai_credit_exhausted`: processing eu-cloud: the organisation's monthly AI spending cap or its own lower limit is reached. Local infrastructure keeps working.\n- `person_ai_credit_exhausted`: Your personal AI limit is reached (personal keys only).\n- `connection_rate_limited`: The provider behind the connection refused the call for now. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60). Sent with rate_limited and with a provider's own limit; ai_credit_exhausted and person_ai_credit_exhausted carry none, because waiting does not lift a spending limit.",
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  },
                  "ai_credit_exhausted": {
                    "summary": "ai_credit_exhausted",
                    "value": {
                      "error": "ai_credit_exhausted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ai_credit_exhausted"
                    }
                  },
                  "person_ai_credit_exhausted": {
                    "summary": "person_ai_credit_exhausted",
                    "value": {
                      "error": "person_ai_credit_exhausted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/person_ai_credit_exhausted"
                    }
                  },
                  "connection_rate_limited": {
                    "summary": "connection_rate_limited",
                    "value": {
                      "error": "connection_rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/connection_rate_limited"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "A processing service failed. No partial result is returned.\n\n- `service_unavailable`: The model failed or could not be reached. The call may have reached the model: do not send it again automatically.\n- `invalid_response`: The model's answer failed validation, for example an empty answer or a tool call.\n- `eu_cloud_refused`: processing eu-cloud: komplai's EU cloud refused the call. Choose local processing or a connection in the meantime.\n- `connection_unauthorized`: The provider refused the connection's key.\n- `connection_payment_required`: The provider wants payment on the organisation's account.\n- `connection_request_refused`: The provider refused the request.\n- `connection_failed`: The provider could not be reached or failed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "service_unavailable": {
                    "summary": "service_unavailable",
                    "value": {
                      "error": "service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/service_unavailable"
                    }
                  },
                  "invalid_response": {
                    "summary": "invalid_response",
                    "value": {
                      "error": "invalid_response",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_response"
                    }
                  },
                  "eu_cloud_refused": {
                    "summary": "eu_cloud_refused",
                    "value": {
                      "error": "eu_cloud_refused",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/eu_cloud_refused"
                    }
                  },
                  "connection_unauthorized": {
                    "summary": "connection_unauthorized",
                    "value": {
                      "error": "connection_unauthorized",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/connection_unauthorized"
                    }
                  },
                  "connection_payment_required": {
                    "summary": "connection_payment_required",
                    "value": {
                      "error": "connection_payment_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/connection_payment_required"
                    }
                  },
                  "connection_request_refused": {
                    "summary": "connection_request_refused",
                    "value": {
                      "error": "connection_request_refused",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/connection_request_refused"
                    }
                  },
                  "connection_failed": {
                    "summary": "connection_failed",
                    "value": {
                      "error": "connection_failed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/connection_failed"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `service_unavailable`: Local infrastructure is not set up for this call right now, or an unexpected failure happened.\n- `chat_cloud_unavailable`: Your organisation's EU cloud setup cannot be used right now.\n- `connection_unavailable`: The connection cannot be used: its key cannot be read or its address is refused.\n- `access_service_unavailable`: Keys and access could not be checked.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "service_unavailable": {
                    "summary": "service_unavailable",
                    "value": {
                      "error": "service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/service_unavailable"
                    }
                  },
                  "chat_cloud_unavailable": {
                    "summary": "chat_cloud_unavailable",
                    "value": {
                      "error": "chat_cloud_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/chat_cloud_unavailable"
                    }
                  },
                  "connection_unavailable": {
                    "summary": "connection_unavailable",
                    "value": {
                      "error": "connection_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/connection_unavailable"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  }
                }
              }
            }
          },
          "504": {
            "description": "The call timed out.\n\n- `timeout`: The model did not answer within 120 seconds. The call may have reached the model: do not send it again automatically.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "timeout": {
                    "summary": "timeout",
                    "value": {
                      "error": "timeout",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/timeout"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/decide": {
      "post": {
        "operationId": "evaluateText",
        "summary": "Evaluate one text",
        "description": "Decide (beta) answers typed questions about one text with probabilities instead of prose: choice picks one option, score places the text on a scale, and noul says how likely a statement is true. Every answer has the full posterior and a confidence. A decision model writes no text, so there is nothing to parse; it runs on komplai's local infrastructure in Denmark, with no AI charge, and has 30 seconds to answer. The text is not stored. Needs a personal key with decide:process.",
        "tags": [
          "Decide"
        ],
        "x-komplai-stability": "beta",
        "security": [
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "decide:process"
        ],
        "x-komplai-principals": [
          "person"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true,
                "required": [
                  "state",
                  "questions"
                ],
                "description": "One text and the questions about it. The request, as JSON, may have at most 80,000 characters, and the body at most 320,000 bytes. Fields that are not listed are ignored.",
                "properties": {
                  "model": {
                    "type": "string",
                    "enum": [
                      "komplai-decide",
                      "komplai-decide-claes"
                    ],
                    "description": "The decision model: komplai-decide (Laya) or komplai-decide-claes (Claes). Leave it out for the default: the model you chose in the hub, else your organisation's default, else komplai-decide (Laya). A model Decide does not offer your organisation answers 409 model_not_enabled; the API never picks another one. Both run on komplai's local infrastructure in Denmark, with no AI charge."
                  },
                  "state": {
                    "description": "What to judge: a text, or a JSON object that komplai turns into text. Arrays and other values are refused.",
                    "oneOf": [
                      {
                        "type": "string",
                        "title": "Text"
                      },
                      {
                        "type": "object",
                        "title": "JSON object",
                        "additionalProperties": true
                      }
                    ]
                  },
                  "questions": {
                    "type": "object",
                    "minProperties": 1,
                    "maxProperties": 64,
                    "additionalProperties": {
                      "description": "One typed question. type says which answer comes back.",
                      "oneOf": [
                        {
                          "title": "choice",
                          "type": "object",
                          "additionalProperties": true,
                          "required": [
                            "type",
                            "instructions",
                            "criteria"
                          ],
                          "properties": {
                            "type": {
                              "type": "string",
                              "enum": [
                                "choice"
                              ]
                            },
                            "instructions": {
                              "type": "string",
                              "minLength": 1,
                              "description": "The question, in your own words."
                            },
                            "criteria": {
                              "type": "object",
                              "minProperties": 2,
                              "maxProperties": 32,
                              "additionalProperties": {
                                "type": "string"
                              },
                              "description": "Pick one option: option key to what that option means, 32 at most. The answer names the key, so keep keys short and machine-friendly."
                            }
                          }
                        },
                        {
                          "title": "score",
                          "type": "object",
                          "additionalProperties": true,
                          "required": [
                            "type",
                            "instructions",
                            "criteria"
                          ],
                          "properties": {
                            "type": {
                              "type": "string",
                              "enum": [
                                "score"
                              ]
                            },
                            "instructions": {
                              "type": "string",
                              "minLength": 1,
                              "description": "The question, in your own words."
                            },
                            "criteria": {
                              "type": "array",
                              "minItems": 2,
                              "maxItems": 32,
                              "items": {
                                "type": "string",
                                "minLength": 1
                              },
                              "description": "A scale: the levels' descriptions, lowest first. The level is the position, starting at 0."
                            }
                          }
                        },
                        {
                          "title": "noul",
                          "type": "object",
                          "additionalProperties": true,
                          "required": [
                            "type",
                            "instructions",
                            "criteria"
                          ],
                          "properties": {
                            "type": {
                              "type": "string",
                              "enum": [
                                "noul"
                              ]
                            },
                            "instructions": {
                              "type": "string",
                              "minLength": 1,
                              "description": "The question, in your own words."
                            },
                            "criteria": {
                              "type": "object",
                              "additionalProperties": true,
                              "required": [
                                "true",
                                "false"
                              ],
                              "properties": {
                                "true": {
                                  "type": "string",
                                  "minLength": 1
                                },
                                "false": {
                                  "type": "string",
                                  "minLength": 1
                                }
                              },
                              "description": "What true and what false mean. Both are required."
                            }
                          }
                        }
                      ]
                    },
                    "description": "Question id to question, from 1 to 64. An id may not be empty (400 invalid_input); it comes back unchanged in answers. The questions share one reading of each text, so several questions cost little more than one."
                  }
                }
              },
              "examples": {
                "threeQuestions": {
                  "summary": "A letter and three questions",
                  "description": "category picks one option, urgency places the text on a scale of three levels, and personal_data asks whether something is true.",
                  "value": {
                    "state": "Hej. Jeg vil gerne have en kopi af min ansøgning om byggetilladelse fra marts. Venlig hilsen Mette Kjærgaard Holm",
                    "questions": {
                      "category": {
                        "type": "choice",
                        "instructions": "Hvad handler henvendelsen om?",
                        "criteria": {
                          "aktindsigt": "Borgeren beder om kopi af dokumenter eller aktindsigt",
                          "klage": "Borgeren klager over en afgørelse",
                          "andet": "Noget andet"
                        }
                      },
                      "urgency": {
                        "type": "score",
                        "instructions": "Hvor hurtigt skal henvendelsen besvares?",
                        "criteria": [
                          "Inden for den normale frist",
                          "Inden for en uge",
                          "Samme dag"
                        ]
                      },
                      "personal_data": {
                        "type": "noul",
                        "instructions": "Indeholder teksten personoplysninger?",
                        "criteria": {
                          "true": "Ja, for eksempel et navn eller et CPR-nummer",
                          "false": "Nej"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One answer for every question, with the probabilities behind it. The text is not stored.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "model",
                    "answers",
                    "usage",
                    "latency_ms"
                  ],
                  "properties": {
                    "model": {
                      "type": "string",
                      "description": "The model that answered, by its checkpoint: convaiinnovations/laya/multilingual for Laya, syvai/claes-preview for Claes."
                    },
                    "answers": {
                      "type": "object",
                      "additionalProperties": {
                        "oneOf": [
                          {
                            "title": "choice",
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "type",
                              "choice",
                              "probabilities",
                              "confidence"
                            ],
                            "properties": {
                              "type": {
                                "type": "string",
                                "enum": [
                                  "choice"
                                ]
                              },
                              "choice": {
                                "type": "string",
                                "description": "The option key with the highest probability."
                              },
                              "probabilities": {
                                "type": "object",
                                "additionalProperties": {
                                  "type": "number",
                                  "minimum": 0,
                                  "maximum": 1
                                },
                                "description": "Every option key with its probability; they add up to 1."
                              },
                              "confidence": {
                                "type": "number",
                                "minimum": 0,
                                "maximum": 1,
                                "description": "How decided the answer is: 1 minus the posterior's normalised entropy. 1 puts everything on one option, 0 spreads it evenly. It says nothing about whether the answer is right."
                              },
                              "action": {
                                "type": "object",
                                "additionalProperties": false,
                                "required": [
                                  "act_probability"
                                ],
                                "description": "When the model gives it: how likely the answer is safe to act on without a person checking it.",
                                "properties": {
                                  "act_probability": {
                                    "type": "number",
                                    "minimum": 0,
                                    "maximum": 1
                                  }
                                }
                              }
                            }
                          },
                          {
                            "title": "score",
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "type",
                              "score",
                              "max",
                              "probabilities",
                              "confidence"
                            ],
                            "properties": {
                              "type": {
                                "type": "string",
                                "enum": [
                                  "score"
                                ]
                              },
                              "score": {
                                "type": "number",
                                "minimum": 0,
                                "description": "The expected level: each level times its probability, added up. 0.32 on a scale of 0 to 2 lies between the two lowest levels."
                              },
                              "max": {
                                "type": "integer",
                                "minimum": 1,
                                "description": "The highest level: the number of levels minus 1."
                              },
                              "probabilities": {
                                "type": "array",
                                "items": {
                                  "type": "number",
                                  "minimum": 0,
                                  "maximum": 1
                                },
                                "description": "Each level's probability, in the order of the question's criteria; they add up to 1."
                              },
                              "confidence": {
                                "type": "number",
                                "minimum": 0,
                                "maximum": 1,
                                "description": "How decided the answer is: 1 minus the posterior's normalised entropy. 1 puts everything on one option, 0 spreads it evenly. It says nothing about whether the answer is right."
                              },
                              "action": {
                                "type": "object",
                                "additionalProperties": false,
                                "required": [
                                  "act_probability"
                                ],
                                "description": "When the model gives it: how likely the answer is safe to act on without a person checking it.",
                                "properties": {
                                  "act_probability": {
                                    "type": "number",
                                    "minimum": 0,
                                    "maximum": 1
                                  }
                                }
                              }
                            }
                          },
                          {
                            "title": "noul",
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "type",
                              "noul",
                              "probabilities",
                              "confidence"
                            ],
                            "properties": {
                              "type": {
                                "type": "string",
                                "enum": [
                                  "noul"
                                ]
                              },
                              "noul": {
                                "type": "number",
                                "minimum": 0,
                                "maximum": 1,
                                "description": "The probability that the statement is true."
                              },
                              "probabilities": {
                                "type": "object",
                                "additionalProperties": false,
                                "required": [
                                  "true",
                                  "false"
                                ],
                                "properties": {
                                  "true": {
                                    "type": "number",
                                    "minimum": 0,
                                    "maximum": 1
                                  },
                                  "false": {
                                    "type": "number",
                                    "minimum": 0,
                                    "maximum": 1
                                  }
                                }
                              },
                              "confidence": {
                                "type": "number",
                                "minimum": 0,
                                "maximum": 1,
                                "description": "How decided the answer is: 1 minus the posterior's normalised entropy. 1 puts everything on one option, 0 spreads it evenly. It says nothing about whether the answer is right."
                              },
                              "action": {
                                "type": "object",
                                "additionalProperties": false,
                                "required": [
                                  "act_probability"
                                ],
                                "description": "When the model gives it: how likely the answer is safe to act on without a person checking it.",
                                "properties": {
                                  "act_probability": {
                                    "type": "number",
                                    "minimum": 0,
                                    "maximum": 1
                                  }
                                }
                              }
                            }
                          }
                        ]
                      },
                      "description": "Question id to answer, one for every question."
                    },
                    "usage": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "input_tokens",
                        "output_tokens"
                      ],
                      "properties": {
                        "input_tokens": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "output_tokens": {
                          "type": "integer",
                          "enum": [
                            0
                          ],
                          "description": "Always 0: a decision model writes no text."
                        }
                      }
                    },
                    "latency_ms": {
                      "type": "number",
                      "minimum": 0,
                      "description": "The model's own time in milliseconds, without the network."
                    }
                  }
                },
                "examples": {
                  "threeQuestions": {
                    "summary": "A letter and three questions",
                    "value": {
                      "model": "convaiinnovations/laya/multilingual",
                      "answers": {
                        "category": {
                          "type": "choice",
                          "choice": "aktindsigt",
                          "probabilities": {
                            "aktindsigt": 0.91,
                            "klage": 0.03,
                            "andet": 0.06
                          },
                          "confidence": 0.672,
                          "action": {
                            "act_probability": 0.88
                          }
                        },
                        "urgency": {
                          "type": "score",
                          "score": 0.32,
                          "max": 2,
                          "probabilities": [
                            0.72,
                            0.24,
                            0.04
                          ],
                          "confidence": 0.356
                        },
                        "personal_data": {
                          "type": "noul",
                          "noul": 0.97,
                          "probabilities": {
                            "true": 0.97,
                            "false": 0.03
                          },
                          "confidence": 0.806
                        }
                      },
                      "usage": {
                        "input_tokens": 118,
                        "output_tokens": 0
                      },
                      "latency_ms": 38
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `invalid_input`: The body is not a Decide request as documented: invalid JSON, Content-Type text/plain, state or a question of the wrong form, too few or too many questions, options or texts, a model that is not a decision model, or a request over its character limit.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_input": {
                    "summary": "invalid_input",
                    "value": {
                      "error": "invalid_input",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_input"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `integration_not_permitted`: An organisation integration key cannot use this operation; it needs a personal key.\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not created with it, its person is no longer assigned the app, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `app_not_assigned`: The key's person lost the app or their membership while the request was admitted.\n- `processing_not_enabled`: Decide is not enabled for your organisation's processing.\n- `processing_not_approved`: Your organisation's processing approval for Decide is missing or was withdrawn.\n- `processing_choice_not_allowed`: Your organisation does not allow Decide through the API.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "integration_not_permitted": {
                    "summary": "integration_not_permitted",
                    "value": {
                      "error": "integration_not_permitted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/integration_not_permitted"
                    }
                  },
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "app_not_assigned": {
                    "summary": "app_not_assigned",
                    "value": {
                      "error": "app_not_assigned",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/app_not_assigned"
                    }
                  },
                  "processing_not_enabled": {
                    "summary": "processing_not_enabled",
                    "value": {
                      "error": "processing_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_enabled"
                    }
                  },
                  "processing_not_approved": {
                    "summary": "processing_not_approved",
                    "value": {
                      "error": "processing_not_approved",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_approved"
                    }
                  },
                  "processing_choice_not_allowed": {
                    "summary": "processing_choice_not_allowed",
                    "value": {
                      "error": "processing_choice_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_choice_not_allowed"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.\n\n- `model_not_enabled`: Local infrastructure does not offer this model right now. Another model may answer; the API never picks one for you.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "model_not_enabled": {
                    "summary": "model_not_enabled",
                    "value": {
                      "error": "model_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_not_enabled"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict with the current state.\n\n- `model_not_enabled`: Decide does not offer the model in model to your organisation.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "model_not_enabled": {
                    "summary": "model_not_enabled",
                    "value": {
                      "error": "model_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_not_enabled"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Too large.\n\n- `Payload Too Large`: The body is over 320,000 bytes.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "payload_too_large": {
                    "summary": "Payload Too Large",
                    "value": {
                      "error": "Payload Too Large",
                      "statusCode": 413,
                      "code": "FST_ERR_CTP_BODY_TOO_LARGE",
                      "message": "Request body is too large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/payload-too-large"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.\n\n- `Unsupported Media Type`: The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "unsupported_media_type": {
                    "summary": "Unsupported Media Type",
                    "value": {
                      "error": "Unsupported Media Type",
                      "statusCode": 415,
                      "code": "FST_ERR_CTP_INVALID_MEDIA_TYPE",
                      "message": "Unsupported Media Type",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported-media-type"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "A processing service failed. No partial result is returned.\n\n- `service_unavailable`: The decision model failed, could not be reached or did not answer within 30 seconds.\n- `invalid_response`: The model's answer failed validation: an answer missing for a question, probabilities that do not add up, or another model's checkpoint.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "service_unavailable": {
                    "summary": "service_unavailable",
                    "value": {
                      "error": "service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/service_unavailable"
                    }
                  },
                  "invalid_response": {
                    "summary": "invalid_response",
                    "value": {
                      "error": "invalid_response",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_response"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `service_unavailable`: Local infrastructure is not set up for Decide right now, or an unexpected failure happened.\n- `access_service_unavailable`: Keys and access could not be checked.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "service_unavailable": {
                    "summary": "service_unavailable",
                    "value": {
                      "error": "service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/service_unavailable"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/decide/batch": {
      "post": {
        "operationId": "evaluateTexts",
        "summary": "Evaluate several texts",
        "description": "Decide (beta): the same questions for up to 100 texts in one call, answered in one pass of the model. The answers come back in the order of states, and the batch is answered whole or refused whole. It counts as one request. Needs a personal key with decide:process.",
        "tags": [
          "Decide"
        ],
        "x-komplai-stability": "beta",
        "security": [
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "decide:process"
        ],
        "x-komplai-principals": [
          "person"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true,
                "required": [
                  "states",
                  "questions"
                ],
                "description": "Up to 100 texts and the questions every one of them is asked. The request, as JSON, may have at most 200,000 characters, and the body at most 800,000 bytes. Fields that are not listed are ignored.",
                "properties": {
                  "model": {
                    "type": "string",
                    "enum": [
                      "komplai-decide",
                      "komplai-decide-claes"
                    ],
                    "description": "The decision model: komplai-decide (Laya) or komplai-decide-claes (Claes). Leave it out for the default: the model you chose in the hub, else your organisation's default, else komplai-decide (Laya). A model Decide does not offer your organisation answers 409 model_not_enabled; the API never picks another one. Both run on komplai's local infrastructure in Denmark, with no AI charge."
                  },
                  "states": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 100,
                    "items": {
                      "description": "What to judge: a text, or a JSON object that komplai turns into text. Arrays and other values are refused.",
                      "oneOf": [
                        {
                          "type": "string",
                          "title": "Text"
                        },
                        {
                          "type": "object",
                          "title": "JSON object",
                          "additionalProperties": true
                        }
                      ]
                    },
                    "description": "The texts, each judged as state would be on its own."
                  },
                  "questions": {
                    "type": "object",
                    "minProperties": 1,
                    "maxProperties": 64,
                    "additionalProperties": {
                      "description": "One typed question. type says which answer comes back.",
                      "oneOf": [
                        {
                          "title": "choice",
                          "type": "object",
                          "additionalProperties": true,
                          "required": [
                            "type",
                            "instructions",
                            "criteria"
                          ],
                          "properties": {
                            "type": {
                              "type": "string",
                              "enum": [
                                "choice"
                              ]
                            },
                            "instructions": {
                              "type": "string",
                              "minLength": 1,
                              "description": "The question, in your own words."
                            },
                            "criteria": {
                              "type": "object",
                              "minProperties": 2,
                              "maxProperties": 32,
                              "additionalProperties": {
                                "type": "string"
                              },
                              "description": "Pick one option: option key to what that option means, 32 at most. The answer names the key, so keep keys short and machine-friendly."
                            }
                          }
                        },
                        {
                          "title": "score",
                          "type": "object",
                          "additionalProperties": true,
                          "required": [
                            "type",
                            "instructions",
                            "criteria"
                          ],
                          "properties": {
                            "type": {
                              "type": "string",
                              "enum": [
                                "score"
                              ]
                            },
                            "instructions": {
                              "type": "string",
                              "minLength": 1,
                              "description": "The question, in your own words."
                            },
                            "criteria": {
                              "type": "array",
                              "minItems": 2,
                              "maxItems": 32,
                              "items": {
                                "type": "string",
                                "minLength": 1
                              },
                              "description": "A scale: the levels' descriptions, lowest first. The level is the position, starting at 0."
                            }
                          }
                        },
                        {
                          "title": "noul",
                          "type": "object",
                          "additionalProperties": true,
                          "required": [
                            "type",
                            "instructions",
                            "criteria"
                          ],
                          "properties": {
                            "type": {
                              "type": "string",
                              "enum": [
                                "noul"
                              ]
                            },
                            "instructions": {
                              "type": "string",
                              "minLength": 1,
                              "description": "The question, in your own words."
                            },
                            "criteria": {
                              "type": "object",
                              "additionalProperties": true,
                              "required": [
                                "true",
                                "false"
                              ],
                              "properties": {
                                "true": {
                                  "type": "string",
                                  "minLength": 1
                                },
                                "false": {
                                  "type": "string",
                                  "minLength": 1
                                }
                              },
                              "description": "What true and what false mean. Both are required."
                            }
                          }
                        }
                      ]
                    },
                    "description": "Question id to question, from 1 to 64. An id may not be empty (400 invalid_input); it comes back unchanged in answers. The questions share one reading of each text, so several questions cost little more than one."
                  }
                }
              },
              "examples": {
                "twoTexts": {
                  "summary": "Two letters, the same three questions",
                  "description": "category picks one option, urgency places the text on a scale of three levels, and personal_data asks whether something is true.",
                  "value": {
                    "states": [
                      "Hej. Jeg vil gerne have en kopi af min ansøgning om byggetilladelse fra marts. Venlig hilsen Mette Kjærgaard Holm",
                      "Jeg klager over afslaget på min ansøgning om boligstøtte. Afgørelsen er forkert, og jeg har brug for svar hurtigt."
                    ],
                    "questions": {
                      "category": {
                        "type": "choice",
                        "instructions": "Hvad handler henvendelsen om?",
                        "criteria": {
                          "aktindsigt": "Borgeren beder om kopi af dokumenter eller aktindsigt",
                          "klage": "Borgeren klager over en afgørelse",
                          "andet": "Noget andet"
                        }
                      },
                      "urgency": {
                        "type": "score",
                        "instructions": "Hvor hurtigt skal henvendelsen besvares?",
                        "criteria": [
                          "Inden for den normale frist",
                          "Inden for en uge",
                          "Samme dag"
                        ]
                      },
                      "personal_data": {
                        "type": "noul",
                        "instructions": "Indeholder teksten personoplysninger?",
                        "criteria": {
                          "true": "Ja, for eksempel et navn eller et CPR-nummer",
                          "false": "Nej"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The answers for every text, in the order of states. The texts are not stored.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "model",
                    "results",
                    "usage",
                    "latency_ms"
                  ],
                  "properties": {
                    "model": {
                      "type": "string",
                      "description": "The model that answered, by its checkpoint: convaiinnovations/laya/multilingual for Laya, syvai/claes-preview for Claes."
                    },
                    "results": {
                      "type": "array",
                      "description": "One per text, in the order of states. A batch is answered whole or refused whole.",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "answers",
                          "usage"
                        ],
                        "properties": {
                          "answers": {
                            "type": "object",
                            "additionalProperties": {
                              "oneOf": [
                                {
                                  "title": "choice",
                                  "type": "object",
                                  "additionalProperties": false,
                                  "required": [
                                    "type",
                                    "choice",
                                    "probabilities",
                                    "confidence"
                                  ],
                                  "properties": {
                                    "type": {
                                      "type": "string",
                                      "enum": [
                                        "choice"
                                      ]
                                    },
                                    "choice": {
                                      "type": "string",
                                      "description": "The option key with the highest probability."
                                    },
                                    "probabilities": {
                                      "type": "object",
                                      "additionalProperties": {
                                        "type": "number",
                                        "minimum": 0,
                                        "maximum": 1
                                      },
                                      "description": "Every option key with its probability; they add up to 1."
                                    },
                                    "confidence": {
                                      "type": "number",
                                      "minimum": 0,
                                      "maximum": 1,
                                      "description": "How decided the answer is: 1 minus the posterior's normalised entropy. 1 puts everything on one option, 0 spreads it evenly. It says nothing about whether the answer is right."
                                    },
                                    "action": {
                                      "type": "object",
                                      "additionalProperties": false,
                                      "required": [
                                        "act_probability"
                                      ],
                                      "description": "When the model gives it: how likely the answer is safe to act on without a person checking it.",
                                      "properties": {
                                        "act_probability": {
                                          "type": "number",
                                          "minimum": 0,
                                          "maximum": 1
                                        }
                                      }
                                    }
                                  }
                                },
                                {
                                  "title": "score",
                                  "type": "object",
                                  "additionalProperties": false,
                                  "required": [
                                    "type",
                                    "score",
                                    "max",
                                    "probabilities",
                                    "confidence"
                                  ],
                                  "properties": {
                                    "type": {
                                      "type": "string",
                                      "enum": [
                                        "score"
                                      ]
                                    },
                                    "score": {
                                      "type": "number",
                                      "minimum": 0,
                                      "description": "The expected level: each level times its probability, added up. 0.32 on a scale of 0 to 2 lies between the two lowest levels."
                                    },
                                    "max": {
                                      "type": "integer",
                                      "minimum": 1,
                                      "description": "The highest level: the number of levels minus 1."
                                    },
                                    "probabilities": {
                                      "type": "array",
                                      "items": {
                                        "type": "number",
                                        "minimum": 0,
                                        "maximum": 1
                                      },
                                      "description": "Each level's probability, in the order of the question's criteria; they add up to 1."
                                    },
                                    "confidence": {
                                      "type": "number",
                                      "minimum": 0,
                                      "maximum": 1,
                                      "description": "How decided the answer is: 1 minus the posterior's normalised entropy. 1 puts everything on one option, 0 spreads it evenly. It says nothing about whether the answer is right."
                                    },
                                    "action": {
                                      "type": "object",
                                      "additionalProperties": false,
                                      "required": [
                                        "act_probability"
                                      ],
                                      "description": "When the model gives it: how likely the answer is safe to act on without a person checking it.",
                                      "properties": {
                                        "act_probability": {
                                          "type": "number",
                                          "minimum": 0,
                                          "maximum": 1
                                        }
                                      }
                                    }
                                  }
                                },
                                {
                                  "title": "noul",
                                  "type": "object",
                                  "additionalProperties": false,
                                  "required": [
                                    "type",
                                    "noul",
                                    "probabilities",
                                    "confidence"
                                  ],
                                  "properties": {
                                    "type": {
                                      "type": "string",
                                      "enum": [
                                        "noul"
                                      ]
                                    },
                                    "noul": {
                                      "type": "number",
                                      "minimum": 0,
                                      "maximum": 1,
                                      "description": "The probability that the statement is true."
                                    },
                                    "probabilities": {
                                      "type": "object",
                                      "additionalProperties": false,
                                      "required": [
                                        "true",
                                        "false"
                                      ],
                                      "properties": {
                                        "true": {
                                          "type": "number",
                                          "minimum": 0,
                                          "maximum": 1
                                        },
                                        "false": {
                                          "type": "number",
                                          "minimum": 0,
                                          "maximum": 1
                                        }
                                      }
                                    },
                                    "confidence": {
                                      "type": "number",
                                      "minimum": 0,
                                      "maximum": 1,
                                      "description": "How decided the answer is: 1 minus the posterior's normalised entropy. 1 puts everything on one option, 0 spreads it evenly. It says nothing about whether the answer is right."
                                    },
                                    "action": {
                                      "type": "object",
                                      "additionalProperties": false,
                                      "required": [
                                        "act_probability"
                                      ],
                                      "description": "When the model gives it: how likely the answer is safe to act on without a person checking it.",
                                      "properties": {
                                        "act_probability": {
                                          "type": "number",
                                          "minimum": 0,
                                          "maximum": 1
                                        }
                                      }
                                    }
                                  }
                                }
                              ]
                            },
                            "description": "Question id to answer, one for every question."
                          },
                          "usage": {
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "input_tokens",
                              "output_tokens"
                            ],
                            "properties": {
                              "input_tokens": {
                                "type": "integer",
                                "minimum": 0
                              },
                              "output_tokens": {
                                "type": "integer",
                                "enum": [
                                  0
                                ],
                                "description": "Always 0: a decision model writes no text."
                              }
                            }
                          }
                        }
                      }
                    },
                    "usage": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "input_tokens",
                        "output_tokens"
                      ],
                      "properties": {
                        "input_tokens": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "output_tokens": {
                          "type": "integer",
                          "enum": [
                            0
                          ],
                          "description": "Always 0: a decision model writes no text."
                        }
                      },
                      "description": "The whole batch."
                    },
                    "latency_ms": {
                      "type": "number",
                      "minimum": 0,
                      "description": "The model's own time for the whole batch, in milliseconds."
                    }
                  }
                },
                "examples": {
                  "twoTexts": {
                    "summary": "Two letters, the same three questions",
                    "value": {
                      "model": "convaiinnovations/laya/multilingual",
                      "results": [
                        {
                          "answers": {
                            "category": {
                              "type": "choice",
                              "choice": "aktindsigt",
                              "probabilities": {
                                "aktindsigt": 0.91,
                                "klage": 0.03,
                                "andet": 0.06
                              },
                              "confidence": 0.672,
                              "action": {
                                "act_probability": 0.88
                              }
                            },
                            "urgency": {
                              "type": "score",
                              "score": 0.32,
                              "max": 2,
                              "probabilities": [
                                0.72,
                                0.24,
                                0.04
                              ],
                              "confidence": 0.356
                            },
                            "personal_data": {
                              "type": "noul",
                              "noul": 0.97,
                              "probabilities": {
                                "true": 0.97,
                                "false": 0.03
                              },
                              "confidence": 0.806
                            }
                          },
                          "usage": {
                            "input_tokens": 118,
                            "output_tokens": 0
                          }
                        },
                        {
                          "answers": {
                            "category": {
                              "type": "choice",
                              "choice": "klage",
                              "probabilities": {
                                "aktindsigt": 0.08,
                                "klage": 0.86,
                                "andet": 0.06
                              },
                              "confidence": 0.544,
                              "action": {
                                "act_probability": 0.79
                              }
                            },
                            "urgency": {
                              "type": "score",
                              "score": 1.15,
                              "max": 2,
                              "probabilities": [
                                0.15,
                                0.55,
                                0.3
                              ],
                              "confidence": 0.113
                            },
                            "personal_data": {
                              "type": "noul",
                              "noul": 0.12,
                              "probabilities": {
                                "true": 0.12,
                                "false": 0.88
                              },
                              "confidence": 0.471
                            }
                          },
                          "usage": {
                            "input_tokens": 112,
                            "output_tokens": 0
                          }
                        }
                      ],
                      "usage": {
                        "input_tokens": 230,
                        "output_tokens": 0
                      },
                      "latency_ms": 52
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `invalid_input`: The body is not a Decide request as documented: invalid JSON, Content-Type text/plain, state or a question of the wrong form, too few or too many questions, options or texts, a model that is not a decision model, or a request over its character limit.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_input": {
                    "summary": "invalid_input",
                    "value": {
                      "error": "invalid_input",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_input"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `integration_not_permitted`: An organisation integration key cannot use this operation; it needs a personal key.\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not created with it, its person is no longer assigned the app, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `app_not_assigned`: The key's person lost the app or their membership while the request was admitted.\n- `processing_not_enabled`: Decide is not enabled for your organisation's processing.\n- `processing_not_approved`: Your organisation's processing approval for Decide is missing or was withdrawn.\n- `processing_choice_not_allowed`: Your organisation does not allow Decide through the API.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "integration_not_permitted": {
                    "summary": "integration_not_permitted",
                    "value": {
                      "error": "integration_not_permitted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/integration_not_permitted"
                    }
                  },
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "app_not_assigned": {
                    "summary": "app_not_assigned",
                    "value": {
                      "error": "app_not_assigned",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/app_not_assigned"
                    }
                  },
                  "processing_not_enabled": {
                    "summary": "processing_not_enabled",
                    "value": {
                      "error": "processing_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_enabled"
                    }
                  },
                  "processing_not_approved": {
                    "summary": "processing_not_approved",
                    "value": {
                      "error": "processing_not_approved",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_approved"
                    }
                  },
                  "processing_choice_not_allowed": {
                    "summary": "processing_choice_not_allowed",
                    "value": {
                      "error": "processing_choice_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_choice_not_allowed"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found.\n\n- `model_not_enabled`: Local infrastructure does not offer this model right now. Another model may answer; the API never picks one for you.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "model_not_enabled": {
                    "summary": "model_not_enabled",
                    "value": {
                      "error": "model_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_not_enabled"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict with the current state.\n\n- `model_not_enabled`: Decide does not offer the model in model to your organisation.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "model_not_enabled": {
                    "summary": "model_not_enabled",
                    "value": {
                      "error": "model_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_not_enabled"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Too large.\n\n- `Payload Too Large`: The body is over 800,000 bytes.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "payload_too_large": {
                    "summary": "Payload Too Large",
                    "value": {
                      "error": "Payload Too Large",
                      "statusCode": 413,
                      "code": "FST_ERR_CTP_BODY_TOO_LARGE",
                      "message": "Request body is too large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/payload-too-large"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.\n\n- `Unsupported Media Type`: The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "unsupported_media_type": {
                    "summary": "Unsupported Media Type",
                    "value": {
                      "error": "Unsupported Media Type",
                      "statusCode": 415,
                      "code": "FST_ERR_CTP_INVALID_MEDIA_TYPE",
                      "message": "Unsupported Media Type",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported-media-type"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "A processing service failed. No partial result is returned.\n\n- `service_unavailable`: The decision model failed, could not be reached or did not answer within 30 seconds.\n- `invalid_response`: The model's answer failed validation: an answer missing for a question, probabilities that do not add up, or another model's checkpoint.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "service_unavailable": {
                    "summary": "service_unavailable",
                    "value": {
                      "error": "service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/service_unavailable"
                    }
                  },
                  "invalid_response": {
                    "summary": "invalid_response",
                    "value": {
                      "error": "invalid_response",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_response"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `service_unavailable`: Local infrastructure is not set up for Decide right now, or an unexpected failure happened.\n- `access_service_unavailable`: Keys and access could not be checked.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "service_unavailable": {
                    "summary": "service_unavailable",
                    "value": {
                      "error": "service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/service_unavailable"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/coding/account": {
      "get": {
        "operationId": "readCodingAccount",
        "summary": "Read the coding account",
        "description": "Your organisation, the current terms as contract, where a coding call's data goes, the organisation's monthly AI spending cap and this month's use (in balance, a name kept for installed clients), the default model and the coding models with the provider's prices. Read it before the first completion and whenever a completion answers 409 terms_changed, and show the terms to the person before they connect. It sends no text and runs no model. Needs a personal key with coding:read.",
        "tags": [
          "Coding"
        ],
        "x-komplai-stability": "beta",
        "security": [
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "coding:read"
        ],
        "x-komplai-principals": [
          "person"
        ],
        "responses": {
          "200": {
            "description": "Your organisation, the current terms (contract) and data terms, the monthly AI spending cap and this month's use (balance), and the coding models with their prices.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "name",
                    "contract",
                    "balance",
                    "defaultModel",
                    "data",
                    "models",
                    "credential"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The organisation's id."
                    },
                    "name": {
                      "type": "string",
                      "description": "The organisation's name."
                    },
                    "contract": {
                      "type": "string",
                      "pattern": "^[0-9a-f]{64}$",
                      "description": "The current terms: provider, region, terms, data mode, agreement and the coding models with their prices, as one SHA-256 value. Send it as X-Komplai-Contract on every completion; it changes when any of them changes."
                    },
                    "balance": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "periodStart",
                        "periodEnd",
                        "packageDkk",
                        "budgetMicros",
                        "spentMicros",
                        "reservedMicros",
                        "availableMicros",
                        "active",
                        "organisationLimitMicros",
                        "limitMicros"
                      ],
                      "description": "The organisation's monthly AI spending cap and how much of it is used this month. The field keeps its name for installed clients; it is a ceiling, not money held on account. When the effective limit is reached, calls stop; the API never moves to another model.",
                      "properties": {
                        "budgetKind": {
                          "type": "string",
                          "enum": [
                            "monthly",
                            "pilot"
                          ],
                          "description": "pilot while komplai pays for a pilot's AI usage from one total allowance that never renews. Missing for a monthly cap."
                        },
                        "periodStart": {
                          "type": "string",
                          "nullable": true,
                          "format": "date-time",
                          "description": "When the current month (or the pilot) began; null without a cap."
                        },
                        "periodEnd": {
                          "type": "string",
                          "nullable": true,
                          "format": "date-time",
                          "description": "When the current month ends; null for a pilot or without a cap."
                        },
                        "packageDkk": {
                          "type": "integer",
                          "nullable": true,
                          "description": "The agreed monthly AI spending cap in whole kroner (for a pilot, its total allowance); null without one. The name is kept for installed clients."
                        },
                        "budgetMicros": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "The agreed cap, in millionths of a krone (1000000 is one krone)."
                        },
                        "spentMicros": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Usage settled so far this month, in millionths of a krone (1000000 is one krone)."
                        },
                        "reservedMicros": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Usage held for calls still running, in millionths of a krone (1000000 is one krone)."
                        },
                        "availableMicros": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "What is left below the effective limit this month, never below 0, in millionths of a krone (1000000 is one krone)."
                        },
                        "active": {
                          "type": "boolean",
                          "description": "Whether the cap applies to the current month."
                        },
                        "organisationLimitMicros": {
                          "type": "integer",
                          "nullable": true,
                          "description": "The organisation's own lower limit, set by its administrator, in millionths of a krone; null without one."
                        },
                        "limitMicros": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "The effective limit: the agreed cap, or the organisation's own limit when that is lower, in millionths of a krone (1000000 is one krone)."
                        }
                      }
                    },
                    "defaultModel": {
                      "type": "string",
                      "nullable": true,
                      "description": "The organisation's default coding model; null when its default is not a coding model."
                    },
                    "data": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "recipients",
                        "region",
                        "training",
                        "retention",
                        "source",
                        "checkedAt",
                        "version",
                        "mode",
                        "accounting",
                        "platformStorage"
                      ],
                      "description": "Where a coding call's data goes and how it is kept, in Danish, as the coding clients show it to the person before they connect.",
                      "properties": {
                        "recipients": {
                          "type": "string"
                        },
                        "region": {
                          "type": "string"
                        },
                        "training": {
                          "type": "string",
                          "description": "The provider's terms on training."
                        },
                        "retention": {
                          "type": "string",
                          "description": "The provider's terms on retention."
                        },
                        "source": {
                          "type": "string",
                          "format": "uri",
                          "description": "The provider's own page with the terms."
                        },
                        "checkedAt": {
                          "type": "string",
                          "format": "date",
                          "description": "When komplai last checked the terms."
                        },
                        "version": {
                          "type": "string"
                        },
                        "mode": {
                          "type": "string",
                          "enum": [
                            "synthetic",
                            "personal"
                          ],
                          "description": "synthetic: use test data only. personal: personal data under the organisation's agreement."
                        },
                        "accounting": {
                          "type": "string"
                        },
                        "platformStorage": {
                          "type": "string"
                        }
                      }
                    },
                    "credential": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "id",
                        "scopes",
                        "expiresAt"
                      ],
                      "description": "The key that made this call.",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "scopes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "expiresAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "models": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "id",
                          "name",
                          "object",
                          "owned_by",
                          "context_length",
                          "max_output_tokens",
                          "capabilities",
                          "reasoning_efforts",
                          "pricing"
                        ],
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "The value for model in POST /v1/coding/chat/completions."
                          },
                          "name": {
                            "type": "string"
                          },
                          "object": {
                            "type": "string",
                            "enum": [
                              "model"
                            ]
                          },
                          "owned_by": {
                            "type": "string",
                            "enum": [
                              "komplai"
                            ]
                          },
                          "context_length": {
                            "type": "integer",
                            "description": "The model's context in tokens: input and output together."
                          },
                          "max_output_tokens": {
                            "type": "integer",
                            "description": "The most max_tokens the model takes."
                          },
                          "capabilities": {
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "function_calling"
                            ],
                            "properties": {
                              "function_calling": {
                                "type": "boolean",
                                "enum": [
                                  true
                                ]
                              }
                            }
                          },
                          "reasoning_efforts": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "The reasoning_effort values the model takes; empty when it takes none."
                          },
                          "pricing": {
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "currency",
                              "prompt",
                              "completion",
                              "input_cache_read"
                            ],
                            "description": "The provider's prices, which usage is metered at and counted towards the organisation's monthly AI spending cap.",
                            "properties": {
                              "currency": {
                                "type": "string",
                                "enum": [
                                  "DKK"
                                ]
                              },
                              "prompt": {
                                "type": "string",
                                "description": "The provider's price per input token in DKK, as a decimal string."
                              },
                              "completion": {
                                "type": "string",
                                "description": "The provider's price per output (reasoning included) token in DKK, as a decimal string."
                              },
                              "input_cache_read": {
                                "type": "string",
                                "description": "The provider's price per cached input token in DKK, as a decimal string."
                              }
                            }
                          }
                        }
                      },
                      "description": "The models a coding client may use."
                    }
                  }
                },
                "examples": {
                  "account": {
                    "summary": "An organisation on komplai's EU route",
                    "value": {
                      "id": "2e7c4b9a-5d1f-4a3e-8b6c-9f0d1e2a3b4c",
                      "name": "Eksempel Kommune",
                      "contract": "86fb88e41acf1ec181a16eaf5d7725059473b626b7d4289ee1b9c3b2aaed45b0",
                      "balance": {
                        "periodStart": "2026-10-01T00:00:00.000Z",
                        "periodEnd": "2026-11-01T00:00:00.000Z",
                        "packageDkk": 2500,
                        "budgetMicros": 2500000000,
                        "spentMicros": 412350000,
                        "reservedMicros": 1200000,
                        "availableMicros": 2086450000,
                        "active": true,
                        "organisationLimitMicros": null,
                        "limitMicros": 2500000000
                      },
                      "defaultModel": "mistral-small-3.2-24b-instruct-2506",
                      "data": {
                        "recipients": "komplai → EU-cloud",
                        "region": "komplais API: Hetzner, Tyskland. Model: komplais EU-cloud, EU · Frankrig.",
                        "training": "komplais EU-cloud bruger ikke input, prompts eller svar til at træne, genoptræne eller forbedre grundmodellerne.",
                        "retention": "Prompt- og svarindhold gemmes som udgangspunkt ikke. Midlertidig, projektisoleret cache af mellemresultater er automatisk og kan ikke fravælges. Ved særlige fejlfindings- eller sikkerhedshændelser, herunder undersøgelser af HTTP 500-fejl, kan hele forespørgslen gemmes i op til to uger. Aggregerede, anonymiserede forbrugsdata kan gemmes i op til seks måneder.",
                        "source": "https://developers.komplai.dk/data-processing",
                        "checkedAt": "2026-10-13",
                        "version": "eu-cloud-2026-10-13",
                        "mode": "synthetic",
                        "accounting": "Forbruget måles til udbyderens pris i DKK og tæller med i organisationens månedlige loft for AI-forbrug. Cachet input har sin egen pris, hvor udbyderen har en. Ræsonnement tæller som outputtokens. Når loftet eller din personlige grænse er nået, stopper kaldene. Der skiftes aldrig automatisk til en anden model.",
                        "platformStorage": "komplai gemmer forbrugs- og adgangsmetadata til regnskab og revision. Kode-API'et gemmer ikke prompts, svar eller værktøjsresultater. Se organisationens aftale for metadataenes slettefrist."
                      },
                      "models": [
                        {
                          "id": "mistral-small-3.2-24b-instruct-2506",
                          "name": "Mistral Small 3.2 (24B)",
                          "object": "model",
                          "owned_by": "komplai",
                          "context_length": 128000,
                          "max_output_tokens": 32768,
                          "capabilities": {
                            "function_calling": true
                          },
                          "reasoning_efforts": [],
                          "pricing": {
                            "currency": "DKK",
                            "prompt": "0.000001119",
                            "completion": "0.000002611",
                            "input_cache_read": "0.000001119"
                          }
                        },
                        {
                          "id": "qwen3.5-397b-a17b",
                          "name": "Qwen3.5 (397B-A17B)",
                          "object": "model",
                          "owned_by": "komplai",
                          "context_length": 250000,
                          "max_output_tokens": 16384,
                          "capabilities": {
                            "function_calling": true
                          },
                          "reasoning_efforts": [
                            "none",
                            "low",
                            "medium",
                            "high"
                          ],
                          "pricing": {
                            "currency": "DKK",
                            "prompt": "0.000004476",
                            "completion": "0.000026856",
                            "input_cache_read": "0.000004476"
                          }
                        }
                      ],
                      "credential": {
                        "id": "a4b6c8d0-1e3f-4a5b-9c7d-2e4f6a8b0c1d",
                        "scopes": [
                          "coding:read",
                          "coding:complete"
                        ],
                        "expiresAt": "2026-11-19T08:00:00.000Z"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `integration_not_permitted`: An organisation integration key cannot use this operation; it needs a personal key.\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not created with it, its person is no longer assigned the app, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `coding_not_enabled`: Coding is not enabled for your organisation, Chat does not run in komplai's EU cloud with an approved setup, or you are not assigned Chat.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "integration_not_permitted": {
                    "summary": "integration_not_permitted",
                    "value": {
                      "error": "integration_not_permitted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/integration_not_permitted"
                    }
                  },
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "coding_not_enabled": {
                    "summary": "coding_not_enabled",
                    "value": {
                      "error": "coding_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/coding_not_enabled"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `chat_cloud_unavailable`: Your organisation's EU cloud setup cannot be used right now.\n- `coding_not_configured`: The EU route has no coding terms or coding models on this server.\n- `coding_service_unavailable`: An unexpected failure while reading the account.\n- `access_service_unavailable`: Keys and access could not be checked.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "chat_cloud_unavailable": {
                    "summary": "chat_cloud_unavailable",
                    "value": {
                      "error": "chat_cloud_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/chat_cloud_unavailable"
                    }
                  },
                  "coding_not_configured": {
                    "summary": "coding_not_configured",
                    "value": {
                      "error": "coding_not_configured",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/coding_not_configured"
                    }
                  },
                  "coding_service_unavailable": {
                    "summary": "coding_service_unavailable",
                    "value": {
                      "error": "coding_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/coding_service_unavailable"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/coding/models": {
      "get": {
        "operationId": "listCodingModels",
        "summary": "List the coding models",
        "description": "The models a coding client may use, with their limits and the provider's prices, in the OpenAI list shape, and the account without its models. Needs a personal key with coding:read.",
        "tags": [
          "Coding"
        ],
        "x-komplai-stability": "beta",
        "security": [
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "coding:read"
        ],
        "x-komplai-principals": [
          "person"
        ],
        "responses": {
          "200": {
            "description": "The coding models with their limits and prices in data, and the account without its models in account.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "object",
                    "data",
                    "account"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "enum": [
                        "list"
                      ]
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "id",
                          "name",
                          "object",
                          "owned_by",
                          "context_length",
                          "max_output_tokens",
                          "capabilities",
                          "reasoning_efforts",
                          "pricing"
                        ],
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "The value for model in POST /v1/coding/chat/completions."
                          },
                          "name": {
                            "type": "string"
                          },
                          "object": {
                            "type": "string",
                            "enum": [
                              "model"
                            ]
                          },
                          "owned_by": {
                            "type": "string",
                            "enum": [
                              "komplai"
                            ]
                          },
                          "context_length": {
                            "type": "integer",
                            "description": "The model's context in tokens: input and output together."
                          },
                          "max_output_tokens": {
                            "type": "integer",
                            "description": "The most max_tokens the model takes."
                          },
                          "capabilities": {
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "function_calling"
                            ],
                            "properties": {
                              "function_calling": {
                                "type": "boolean",
                                "enum": [
                                  true
                                ]
                              }
                            }
                          },
                          "reasoning_efforts": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "The reasoning_effort values the model takes; empty when it takes none."
                          },
                          "pricing": {
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "currency",
                              "prompt",
                              "completion",
                              "input_cache_read"
                            ],
                            "description": "The provider's prices, which usage is metered at and counted towards the organisation's monthly AI spending cap.",
                            "properties": {
                              "currency": {
                                "type": "string",
                                "enum": [
                                  "DKK"
                                ]
                              },
                              "prompt": {
                                "type": "string",
                                "description": "The provider's price per input token in DKK, as a decimal string."
                              },
                              "completion": {
                                "type": "string",
                                "description": "The provider's price per output (reasoning included) token in DKK, as a decimal string."
                              },
                              "input_cache_read": {
                                "type": "string",
                                "description": "The provider's price per cached input token in DKK, as a decimal string."
                              }
                            }
                          }
                        }
                      }
                    },
                    "account": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "id",
                        "name",
                        "contract",
                        "balance",
                        "defaultModel",
                        "data",
                        "credential"
                      ],
                      "description": "The same account as GET /v1/coding/account, without models.",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid",
                          "description": "The organisation's id."
                        },
                        "name": {
                          "type": "string",
                          "description": "The organisation's name."
                        },
                        "contract": {
                          "type": "string",
                          "pattern": "^[0-9a-f]{64}$",
                          "description": "The current terms: provider, region, terms, data mode, agreement and the coding models with their prices, as one SHA-256 value. Send it as X-Komplai-Contract on every completion; it changes when any of them changes."
                        },
                        "balance": {
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "periodStart",
                            "periodEnd",
                            "packageDkk",
                            "budgetMicros",
                            "spentMicros",
                            "reservedMicros",
                            "availableMicros",
                            "active",
                            "organisationLimitMicros",
                            "limitMicros"
                          ],
                          "description": "The organisation's monthly AI spending cap and how much of it is used this month. The field keeps its name for installed clients; it is a ceiling, not money held on account. When the effective limit is reached, calls stop; the API never moves to another model.",
                          "properties": {
                            "budgetKind": {
                              "type": "string",
                              "enum": [
                                "monthly",
                                "pilot"
                              ],
                              "description": "pilot while komplai pays for a pilot's AI usage from one total allowance that never renews. Missing for a monthly cap."
                            },
                            "periodStart": {
                              "type": "string",
                              "nullable": true,
                              "format": "date-time",
                              "description": "When the current month (or the pilot) began; null without a cap."
                            },
                            "periodEnd": {
                              "type": "string",
                              "nullable": true,
                              "format": "date-time",
                              "description": "When the current month ends; null for a pilot or without a cap."
                            },
                            "packageDkk": {
                              "type": "integer",
                              "nullable": true,
                              "description": "The agreed monthly AI spending cap in whole kroner (for a pilot, its total allowance); null without one. The name is kept for installed clients."
                            },
                            "budgetMicros": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "The agreed cap, in millionths of a krone (1000000 is one krone)."
                            },
                            "spentMicros": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "Usage settled so far this month, in millionths of a krone (1000000 is one krone)."
                            },
                            "reservedMicros": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "Usage held for calls still running, in millionths of a krone (1000000 is one krone)."
                            },
                            "availableMicros": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "What is left below the effective limit this month, never below 0, in millionths of a krone (1000000 is one krone)."
                            },
                            "active": {
                              "type": "boolean",
                              "description": "Whether the cap applies to the current month."
                            },
                            "organisationLimitMicros": {
                              "type": "integer",
                              "nullable": true,
                              "description": "The organisation's own lower limit, set by its administrator, in millionths of a krone; null without one."
                            },
                            "limitMicros": {
                              "type": "integer",
                              "minimum": 0,
                              "description": "The effective limit: the agreed cap, or the organisation's own limit when that is lower, in millionths of a krone (1000000 is one krone)."
                            }
                          }
                        },
                        "defaultModel": {
                          "type": "string",
                          "nullable": true,
                          "description": "The organisation's default coding model; null when its default is not a coding model."
                        },
                        "data": {
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "recipients",
                            "region",
                            "training",
                            "retention",
                            "source",
                            "checkedAt",
                            "version",
                            "mode",
                            "accounting",
                            "platformStorage"
                          ],
                          "description": "Where a coding call's data goes and how it is kept, in Danish, as the coding clients show it to the person before they connect.",
                          "properties": {
                            "recipients": {
                              "type": "string"
                            },
                            "region": {
                              "type": "string"
                            },
                            "training": {
                              "type": "string",
                              "description": "The provider's terms on training."
                            },
                            "retention": {
                              "type": "string",
                              "description": "The provider's terms on retention."
                            },
                            "source": {
                              "type": "string",
                              "format": "uri",
                              "description": "The provider's own page with the terms."
                            },
                            "checkedAt": {
                              "type": "string",
                              "format": "date",
                              "description": "When komplai last checked the terms."
                            },
                            "version": {
                              "type": "string"
                            },
                            "mode": {
                              "type": "string",
                              "enum": [
                                "synthetic",
                                "personal"
                              ],
                              "description": "synthetic: use test data only. personal: personal data under the organisation's agreement."
                            },
                            "accounting": {
                              "type": "string"
                            },
                            "platformStorage": {
                              "type": "string"
                            }
                          }
                        },
                        "credential": {
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "id",
                            "scopes",
                            "expiresAt"
                          ],
                          "description": "The key that made this call.",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "scopes": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "expiresAt": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "models": {
                    "summary": "An organisation on komplai's EU route",
                    "value": {
                      "object": "list",
                      "data": [
                        {
                          "id": "mistral-small-3.2-24b-instruct-2506",
                          "name": "Mistral Small 3.2 (24B)",
                          "object": "model",
                          "owned_by": "komplai",
                          "context_length": 128000,
                          "max_output_tokens": 32768,
                          "capabilities": {
                            "function_calling": true
                          },
                          "reasoning_efforts": [],
                          "pricing": {
                            "currency": "DKK",
                            "prompt": "0.000001119",
                            "completion": "0.000002611",
                            "input_cache_read": "0.000001119"
                          }
                        },
                        {
                          "id": "qwen3.5-397b-a17b",
                          "name": "Qwen3.5 (397B-A17B)",
                          "object": "model",
                          "owned_by": "komplai",
                          "context_length": 250000,
                          "max_output_tokens": 16384,
                          "capabilities": {
                            "function_calling": true
                          },
                          "reasoning_efforts": [
                            "none",
                            "low",
                            "medium",
                            "high"
                          ],
                          "pricing": {
                            "currency": "DKK",
                            "prompt": "0.000004476",
                            "completion": "0.000026856",
                            "input_cache_read": "0.000004476"
                          }
                        }
                      ],
                      "account": {
                        "id": "2e7c4b9a-5d1f-4a3e-8b6c-9f0d1e2a3b4c",
                        "name": "Eksempel Kommune",
                        "contract": "86fb88e41acf1ec181a16eaf5d7725059473b626b7d4289ee1b9c3b2aaed45b0",
                        "balance": {
                          "periodStart": "2026-10-01T00:00:00.000Z",
                          "periodEnd": "2026-11-01T00:00:00.000Z",
                          "packageDkk": 2500,
                          "budgetMicros": 2500000000,
                          "spentMicros": 412350000,
                          "reservedMicros": 1200000,
                          "availableMicros": 2086450000,
                          "active": true,
                          "organisationLimitMicros": null,
                          "limitMicros": 2500000000
                        },
                        "defaultModel": "mistral-small-3.2-24b-instruct-2506",
                        "data": {
                          "recipients": "komplai → EU-cloud",
                          "region": "komplais API: Hetzner, Tyskland. Model: komplais EU-cloud, EU · Frankrig.",
                          "training": "komplais EU-cloud bruger ikke input, prompts eller svar til at træne, genoptræne eller forbedre grundmodellerne.",
                          "retention": "Prompt- og svarindhold gemmes som udgangspunkt ikke. Midlertidig, projektisoleret cache af mellemresultater er automatisk og kan ikke fravælges. Ved særlige fejlfindings- eller sikkerhedshændelser, herunder undersøgelser af HTTP 500-fejl, kan hele forespørgslen gemmes i op til to uger. Aggregerede, anonymiserede forbrugsdata kan gemmes i op til seks måneder.",
                          "source": "https://developers.komplai.dk/data-processing",
                          "checkedAt": "2026-10-13",
                          "version": "eu-cloud-2026-10-13",
                          "mode": "synthetic",
                          "accounting": "Forbruget måles til udbyderens pris i DKK og tæller med i organisationens månedlige loft for AI-forbrug. Cachet input har sin egen pris, hvor udbyderen har en. Ræsonnement tæller som outputtokens. Når loftet eller din personlige grænse er nået, stopper kaldene. Der skiftes aldrig automatisk til en anden model.",
                          "platformStorage": "komplai gemmer forbrugs- og adgangsmetadata til regnskab og revision. Kode-API'et gemmer ikke prompts, svar eller værktøjsresultater. Se organisationens aftale for metadataenes slettefrist."
                        },
                        "credential": {
                          "id": "a4b6c8d0-1e3f-4a5b-9c7d-2e4f6a8b0c1d",
                          "scopes": [
                            "coding:read",
                            "coding:complete"
                          ],
                          "expiresAt": "2026-11-19T08:00:00.000Z"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `integration_not_permitted`: An organisation integration key cannot use this operation; it needs a personal key.\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not created with it, its person is no longer assigned the app, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `coding_not_enabled`: Coding is not enabled for your organisation, Chat does not run in komplai's EU cloud with an approved setup, or you are not assigned Chat.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "integration_not_permitted": {
                    "summary": "integration_not_permitted",
                    "value": {
                      "error": "integration_not_permitted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/integration_not_permitted"
                    }
                  },
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "coding_not_enabled": {
                    "summary": "coding_not_enabled",
                    "value": {
                      "error": "coding_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/coding_not_enabled"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60).",
                "required": true,
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `chat_cloud_unavailable`: Your organisation's EU cloud setup cannot be used right now.\n- `coding_not_configured`: The EU route has no coding terms or coding models on this server.\n- `coding_service_unavailable`: An unexpected failure while reading the account.\n- `access_service_unavailable`: Keys and access could not be checked.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "chat_cloud_unavailable": {
                    "summary": "chat_cloud_unavailable",
                    "value": {
                      "error": "chat_cloud_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/chat_cloud_unavailable"
                    }
                  },
                  "coding_not_configured": {
                    "summary": "coding_not_configured",
                    "value": {
                      "error": "coding_not_configured",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/coding_not_configured"
                    }
                  },
                  "coding_service_unavailable": {
                    "summary": "coding_service_unavailable",
                    "value": {
                      "error": "coding_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/coding_service_unavailable"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/coding/chat/completions": {
      "post": {
        "operationId": "createCodingChatCompletion",
        "summary": "Stream a coding completion",
        "description": "An OpenAI-compatible chat completion for coding clients, streamed as server-sent events, with text and function calls. komplai never runs a tool: the client runs it, asks the person where needed, and sends the result in the next request. The model runs in komplai's EU cloud on your organisation's approved route. Before the provider is called, the most the call can cost is reserved under the organisation's monthly AI spending cap; usage is metered at the provider's price, and calls stop when the cap, the organisation's own limit or your personal limit is reached. komplai stores no prompts, answers or tool results, only the call's metadata. Send X-Komplai-Contract with the current contract and X-Request-Id with a new UUID for each operation. Needs a personal key with coding:complete.",
        "tags": [
          "Coding"
        ],
        "x-komplai-stability": "beta",
        "security": [
          {
            "personalKey": []
          }
        ],
        "x-komplai-scopes": [
          "coding:complete"
        ],
        "x-komplai-principals": [
          "person"
        ],
        "parameters": [
          {
            "in": "header",
            "name": "X-Komplai-Contract",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            },
            "description": "contract from GET /v1/coding/account, which needs coding:read. When the terms changed since, the call answers 409 terms_changed: read the account again, show the new terms to the person, and send the new contract."
          },
          {
            "in": "header",
            "name": "X-Request-Id",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
            },
            "description": "A new UUID (version 4) for each new logical operation. Send the same id again only to retry an operation whose outcome you do not know: komplai then answers 409 duplicate_request instead of calling the provider a second time. Request-Id in the response repeats it."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "model",
                  "messages",
                  "stream"
                ],
                "description": "An OpenAI-compatible streamed chat request with text and function calls. Send exactly one of max_tokens and max_completion_tokens. The body may have at most 2,000,000 bytes and 2,000,000 characters as JSON. komplai runs no tools: the client runs them and sends their results back. A field that is not listed answers 400 invalid_coding_input.",
                "properties": {
                  "model": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "A model id from GET /v1/coding/models."
                  },
                  "messages": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 512,
                    "items": {
                      "description": "A system, user, assistant or tool message, each with content. A tool message answers a tool call from the assistant message just before it, by tool_call_id, and every tool call must be answered before the next message.",
                      "oneOf": [
                        {
                          "title": "System or user message",
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "role",
                            "content"
                          ],
                          "properties": {
                            "role": {
                              "type": "string",
                              "enum": [
                                "system",
                                "user"
                              ]
                            },
                            "content": {
                              "description": "Text or a list of text parts.",
                              "oneOf": [
                                {
                                  "type": "string",
                                  "maxLength": 1000000
                                },
                                {
                                  "type": "array",
                                  "maxItems": 100,
                                  "description": "A list of text parts.",
                                  "items": {
                                    "type": "object",
                                    "additionalProperties": false,
                                    "required": [
                                      "type",
                                      "text"
                                    ],
                                    "properties": {
                                      "type": {
                                        "type": "string",
                                        "enum": [
                                          "text"
                                        ]
                                      },
                                      "text": {
                                        "type": "string",
                                        "maxLength": 1000000
                                      }
                                    }
                                  }
                                }
                              ]
                            }
                          }
                        },
                        {
                          "title": "Assistant message",
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "role",
                            "content"
                          ],
                          "properties": {
                            "role": {
                              "type": "string",
                              "enum": [
                                "assistant"
                              ]
                            },
                            "content": {
                              "description": "Text, a list of text parts, or null when the message only calls tools.",
                              "oneOf": [
                                {
                                  "type": "string",
                                  "nullable": true,
                                  "maxLength": 1000000
                                },
                                {
                                  "type": "array",
                                  "maxItems": 100,
                                  "description": "A list of text parts.",
                                  "items": {
                                    "type": "object",
                                    "additionalProperties": false,
                                    "required": [
                                      "type",
                                      "text"
                                    ],
                                    "properties": {
                                      "type": {
                                        "type": "string",
                                        "enum": [
                                          "text"
                                        ]
                                      },
                                      "text": {
                                        "type": "string",
                                        "maxLength": 1000000
                                      }
                                    }
                                  }
                                }
                              ]
                            },
                            "tool_calls": {
                              "type": "array",
                              "minItems": 1,
                              "maxItems": 64,
                              "items": {
                                "type": "object",
                                "additionalProperties": false,
                                "required": [
                                  "id",
                                  "type",
                                  "function"
                                ],
                                "properties": {
                                  "id": {
                                    "type": "string",
                                    "pattern": "^[a-zA-Z0-9_-]{1,128}$"
                                  },
                                  "type": {
                                    "type": "string",
                                    "enum": [
                                      "function"
                                    ]
                                  },
                                  "function": {
                                    "type": "object",
                                    "additionalProperties": false,
                                    "required": [
                                      "name",
                                      "arguments"
                                    ],
                                    "properties": {
                                      "name": {
                                        "type": "string",
                                        "pattern": "^[a-zA-Z0-9_-]{1,128}$"
                                      },
                                      "arguments": {
                                        "type": "string",
                                        "maxLength": 1000000,
                                        "description": "A JSON object as text."
                                      }
                                    }
                                  }
                                }
                              },
                              "description": "The functions the model asked the client to call, each with an id that is unique in the conversation."
                            },
                            "reasoning_content": {
                              "type": "string",
                              "maxLength": 1000000,
                              "description": "The model's earlier reasoning, sent back unchanged."
                            }
                          }
                        },
                        {
                          "title": "Tool message",
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "role",
                            "content",
                            "tool_call_id"
                          ],
                          "properties": {
                            "role": {
                              "type": "string",
                              "enum": [
                                "tool"
                              ]
                            },
                            "content": {
                              "description": "The tool's result: text or a list of text parts.",
                              "oneOf": [
                                {
                                  "type": "string",
                                  "maxLength": 1000000
                                },
                                {
                                  "type": "array",
                                  "maxItems": 100,
                                  "description": "A list of text parts.",
                                  "items": {
                                    "type": "object",
                                    "additionalProperties": false,
                                    "required": [
                                      "type",
                                      "text"
                                    ],
                                    "properties": {
                                      "type": {
                                        "type": "string",
                                        "enum": [
                                          "text"
                                        ]
                                      },
                                      "text": {
                                        "type": "string",
                                        "maxLength": 1000000
                                      }
                                    }
                                  }
                                }
                              ]
                            },
                            "tool_call_id": {
                              "type": "string",
                              "pattern": "^[a-zA-Z0-9_-]{1,128}$",
                              "description": "The id of the tool call this message answers."
                            }
                          }
                        }
                      ]
                    }
                  },
                  "tools": {
                    "type": "array",
                    "maxItems": 64,
                    "description": "The functions the model may call.",
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "type",
                        "function"
                      ],
                      "properties": {
                        "type": {
                          "type": "string",
                          "enum": [
                            "function"
                          ]
                        },
                        "function": {
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "name",
                            "parameters"
                          ],
                          "properties": {
                            "name": {
                              "type": "string",
                              "pattern": "^[a-zA-Z0-9_-]{1,128}$"
                            },
                            "description": {
                              "type": "string",
                              "maxLength": 50000
                            },
                            "parameters": {
                              "type": "object",
                              "additionalProperties": true,
                              "description": "A JSON Schema for the arguments."
                            },
                            "strict": {
                              "type": "boolean"
                            }
                          }
                        }
                      }
                    }
                  },
                  "tool_choice": {
                    "description": "auto, none, required, or one function from tools by name.",
                    "oneOf": [
                      {
                        "type": "string",
                        "enum": [
                          "auto",
                          "none",
                          "required"
                        ]
                      },
                      {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "type",
                          "function"
                        ],
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "function"
                            ]
                          },
                          "function": {
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "name"
                            ],
                            "properties": {
                              "name": {
                                "type": "string",
                                "pattern": "^[a-zA-Z0-9_-]{1,128}$"
                              }
                            }
                          }
                        }
                      }
                    ]
                  },
                  "parallel_tool_calls": {
                    "type": "boolean"
                  },
                  "stream": {
                    "type": "boolean",
                    "enum": [
                      true
                    ],
                    "description": "Always true: the answer is streamed as server-sent events."
                  },
                  "stream_options": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "include_usage"
                    ],
                    "properties": {
                      "include_usage": {
                        "type": "boolean",
                        "enum": [
                          true
                        ]
                      }
                    },
                    "description": "Optional; usage is always included in the stream."
                  },
                  "max_tokens": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 32000,
                    "description": "The most tokens the answer may have, within the model's max_output_tokens; the input and max_tokens together must fit its context (413 context_limit). Required unless you send max_completion_tokens instead: send exactly one of the two (400 invalid_coding_input otherwise)."
                  },
                  "max_completion_tokens": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 32000,
                    "description": "The same as max_tokens, for clients that use this name. Send exactly one of max_tokens and max_completion_tokens."
                  },
                  "temperature": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 2
                  },
                  "top_p": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 1
                  },
                  "frequency_penalty": {
                    "type": "number",
                    "minimum": -2,
                    "maximum": 2
                  },
                  "presence_penalty": {
                    "type": "number",
                    "minimum": -2,
                    "maximum": 2
                  },
                  "stop": {
                    "description": "Up to 4 stop sequences of at most 500 characters.",
                    "oneOf": [
                      {
                        "type": "string",
                        "maxLength": 500
                      },
                      {
                        "type": "array",
                        "maxItems": 4,
                        "items": {
                          "type": "string",
                          "maxLength": 500
                        }
                      }
                    ]
                  },
                  "reasoning_effort": {
                    "type": "string",
                    "enum": [
                      "none",
                      "low",
                      "medium",
                      "high",
                      "xhigh",
                      "max"
                    ],
                    "description": "One of the model's reasoning_efforts (400 reasoning_not_supported otherwise)."
                  }
                }
              },
              "examples": {
                "explain": {
                  "summary": "A question about code",
                  "value": {
                    "model": "mistral-small-3.2-24b-instruct-2506",
                    "stream": true,
                    "stream_options": {
                      "include_usage": true
                    },
                    "max_tokens": 256,
                    "messages": [
                      {
                        "role": "system",
                        "content": "You are a careful coding assistant."
                      },
                      {
                        "role": "user",
                        "content": "What does this expression evaluate to: 1 + 1?"
                      }
                    ]
                  }
                },
                "toolCall": {
                  "summary": "With a function the model may call",
                  "description": "The model asks the client to call read_file. The client reads the file and sends the next request with the assistant message, its tool call and a tool message with the result.",
                  "value": {
                    "model": "mistral-small-3.2-24b-instruct-2506",
                    "stream": true,
                    "max_tokens": 256,
                    "tools": [
                      {
                        "type": "function",
                        "function": {
                          "name": "read_file",
                          "description": "Read a file in the project.",
                          "parameters": {
                            "type": "object",
                            "required": [
                              "path"
                            ],
                            "properties": {
                              "path": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    ],
                    "messages": [
                      {
                        "role": "user",
                        "content": "What is in README.md?"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Server-sent events (text/event-stream). Each data line is a chat.completion.chunk from the model, passed on after validation: text in choices[0].delta.content, reasoning in reasoning_content, function calls in tool_calls, and usage in the last chunk. data: [DONE] ends a complete answer. If the call fails after the stream began, the last event is data: {\"error\": {\"message\", \"code\", \"request_id\", \"doc_url\"}} without [DONE]; treat the outcome as unknown.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                },
                "examples": {
                  "explain": {
                    "summary": "A question about code",
                    "value": "data: {\"id\":\"chatcmpl-5f1e9d2c\",\"object\":\"chat.completion.chunk\",\"created\":1792483200,\"model\":\"mistral-small-3.2-24b-instruct-2506\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"It evaluates to 2\"},\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-5f1e9d2c\",\"object\":\"chat.completion.chunk\",\"created\":1792483200,\"model\":\"mistral-small-3.2-24b-instruct-2506\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\": the sum of 1 and 1.\"},\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-5f1e9d2c\",\"object\":\"chat.completion.chunk\",\"created\":1792483200,\"model\":\"mistral-small-3.2-24b-instruct-2506\",\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"stop\"}],\"usage\":{\"prompt_tokens\":31,\"completion_tokens\":14}}\n\ndata: [DONE]\n\n"
                  },
                  "toolCall": {
                    "summary": "The model calls read_file",
                    "value": "data: {\"id\":\"chatcmpl-5f1e9d2c\",\"object\":\"chat.completion.chunk\",\"created\":1792483200,\"model\":\"mistral-small-3.2-24b-instruct-2506\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"tool_calls\":[{\"index\":0,\"id\":\"call_7b2d\",\"type\":\"function\",\"function\":{\"name\":\"read_file\",\"arguments\":\"\"}}]},\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-5f1e9d2c\",\"object\":\"chat.completion.chunk\",\"created\":1792483200,\"model\":\"mistral-small-3.2-24b-instruct-2506\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"function\":{\"arguments\":\"{\\\"path\\\":\\\"README.md\\\"}\"}}]},\"finish_reason\":null}]}\n\ndata: {\"id\":\"chatcmpl-5f1e9d2c\",\"object\":\"chat.completion.chunk\",\"created\":1792483200,\"model\":\"mistral-small-3.2-24b-instruct-2506\",\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"tool_calls\"}],\"usage\":{\"prompt_tokens\":58,\"completion_tokens\":18}}\n\ndata: [DONE]\n\n"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request was refused before processing.\n\n- `invalid_input`: Content-Type is application/json, but the body is empty or not valid JSON.\n- `invalid_coding_input`: The body is not a coding request as documented: Content-Type text/plain, a field that is not listed, a message without content, stream not true, both or neither of max_tokens and max_completion_tokens, a value out of range, or tool calls and tool messages that do not match.\n- `invalid_request_id`: X-Request-Id is missing or not a version 4 UUID.\n- `reasoning_not_supported`: The model does not take this reasoning_effort.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "invalid_input": {
                    "summary": "invalid_input",
                    "value": {
                      "error": "invalid_input",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_input"
                    }
                  },
                  "invalid_coding_input": {
                    "summary": "invalid_coding_input",
                    "value": {
                      "error": "invalid_coding_input",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_coding_input"
                    }
                  },
                  "invalid_request_id": {
                    "summary": "invalid_request_id",
                    "value": {
                      "error": "invalid_request_id",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_request_id"
                    }
                  },
                  "reasoning_not_supported": {
                    "summary": "reasoning_not_supported",
                    "value": {
                      "error": "reasoning_not_supported",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/reasoning_not_supported"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The key is missing or not valid.\n\n- `api_key_required`: No Authorization: Bearer header with a komplai key.\n- `invalid_api_key`: The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.\n- `invalid_token`: The key was revoked or expired, or its access ended, while the request was admitted or processed.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "api_key_required": {
                    "summary": "api_key_required",
                    "value": {
                      "error": "api_key_required",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/api_key_required"
                    }
                  },
                  "invalid_api_key": {
                    "summary": "invalid_api_key",
                    "value": {
                      "error": "invalid_api_key",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key"
                    }
                  },
                  "invalid_token": {
                    "summary": "invalid_token",
                    "value": {
                      "error": "invalid_token",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_token"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key may not do this, or the organisation's access does not cover it.\n\n- `integration_not_permitted`: An organisation integration key cannot use this operation; it needs a personal key.\n- `insufficient_scope`: The key lacks the scope this operation needs (x-komplai-scopes): it was not created with it, its person is no longer assigned the app, or the organisation's processing approval no longer covers it.\n- `identity_retired`: The person behind a personal key has been removed from komplai.\n- `app_not_assigned`: The key's person lost the app or their membership while the request was admitted.\n- `coding_not_enabled`: Coding is not enabled for your organisation, or its Chat does not run in komplai's EU cloud.\n- `processing_not_enabled`: EU processing is not enabled for your organisation's Chat, or the organisation is on a pilot where komplai pays for Redact's EU processing only.\n- `processing_not_approved`: Your organisation's processing approval for Chat is missing or was withdrawn.\n- `processing_choice_not_allowed`: Your organisation does not allow EU cloud processing through the API.\n- `ai_credit_not_granted`: No monthly AI spending cap is set up for this month.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "integration_not_permitted": {
                    "summary": "integration_not_permitted",
                    "value": {
                      "error": "integration_not_permitted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/integration_not_permitted"
                    }
                  },
                  "insufficient_scope": {
                    "summary": "insufficient_scope",
                    "value": {
                      "error": "insufficient_scope",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope"
                    }
                  },
                  "identity_retired": {
                    "summary": "identity_retired",
                    "value": {
                      "error": "identity_retired",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/identity_retired"
                    }
                  },
                  "app_not_assigned": {
                    "summary": "app_not_assigned",
                    "value": {
                      "error": "app_not_assigned",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/app_not_assigned"
                    }
                  },
                  "coding_not_enabled": {
                    "summary": "coding_not_enabled",
                    "value": {
                      "error": "coding_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/coding_not_enabled"
                    }
                  },
                  "processing_not_enabled": {
                    "summary": "processing_not_enabled",
                    "value": {
                      "error": "processing_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_enabled"
                    }
                  },
                  "processing_not_approved": {
                    "summary": "processing_not_approved",
                    "value": {
                      "error": "processing_not_approved",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_not_approved"
                    }
                  },
                  "processing_choice_not_allowed": {
                    "summary": "processing_choice_not_allowed",
                    "value": {
                      "error": "processing_choice_not_allowed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/processing_choice_not_allowed"
                    }
                  },
                  "ai_credit_not_granted": {
                    "summary": "ai_credit_not_granted",
                    "value": {
                      "error": "ai_credit_not_granted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ai_credit_not_granted"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict with the current state.\n\n- `terms_changed`: X-Komplai-Contract is not the current contract.\n- `duplicate_request`: This X-Request-Id was used before with this key. Nothing was sent to the model.\n- `model_not_enabled`: model is not a coding model on your organisation's EU route.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "terms_changed": {
                    "summary": "terms_changed",
                    "value": {
                      "error": "terms_changed",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/terms_changed"
                    }
                  },
                  "duplicate_request": {
                    "summary": "duplicate_request",
                    "value": {
                      "error": "duplicate_request",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/duplicate_request"
                    }
                  },
                  "model_not_enabled": {
                    "summary": "model_not_enabled",
                    "value": {
                      "error": "model_not_enabled",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/model_not_enabled"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Too large.\n\n- `context_limit`: The body is over 2,000,000 characters as JSON, max_tokens is over the model's max_output_tokens, or the input and max_tokens together do not fit the model's context.\n- `Payload Too Large`: The body is over 2,000,000 bytes.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "context_limit": {
                    "summary": "context_limit",
                    "value": {
                      "error": "context_limit",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/context_limit"
                    }
                  },
                  "payload_too_large": {
                    "summary": "Payload Too Large",
                    "value": {
                      "error": "Payload Too Large",
                      "statusCode": 413,
                      "code": "FST_ERR_CTP_BODY_TOO_LARGE",
                      "message": "Request body is too large",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/payload-too-large"
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.\n\n- `Unsupported Media Type`: The body has no Content-Type, or one the API does not read, such as XML or form data. Send application/json.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "unsupported_media_type": {
                    "summary": "Unsupported Media Type",
                    "value": {
                      "error": "Unsupported Media Type",
                      "statusCode": 415,
                      "code": "FST_ERR_CTP_INVALID_MEDIA_TYPE",
                      "message": "Unsupported Media Type",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/unsupported-media-type"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Refused for now. Wait the seconds in Retry-After before you try again.\n\n- `rate_limited`: Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.\n- `ai_credit_exhausted`: The organisation's monthly AI spending cap or its own lower limit is reached.\n- `person_ai_credit_exhausted`: Your personal AI limit is reached (personal keys only).\n- `upstream_rate_limited`: The model provider refused the call for now. Try again later.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              },
              "Retry-After": {
                "description": "Seconds to wait before trying again (at most 60). Sent with rate_limited; upstream_rate_limited, ai_credit_exhausted and person_ai_credit_exhausted carry none.",
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 60
                },
                "example": 60
              },
              "x-ratelimit-limit": {
                "description": "Requests per minute allowed from your IP address (the per-IP network limit).",
                "schema": {
                  "type": "integer"
                },
                "example": 300
              },
              "x-ratelimit-remaining": {
                "description": "Requests left in the current minute from your IP address.",
                "schema": {
                  "type": "integer"
                },
                "example": 0
              },
              "x-ratelimit-reset": {
                "description": "Seconds until the per-IP window resets.",
                "schema": {
                  "type": "integer"
                },
                "example": 42
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "rate_limited": {
                    "summary": "rate_limited",
                    "value": {
                      "error": "rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/rate_limited"
                    }
                  },
                  "ai_credit_exhausted": {
                    "summary": "ai_credit_exhausted",
                    "value": {
                      "error": "ai_credit_exhausted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/ai_credit_exhausted"
                    }
                  },
                  "person_ai_credit_exhausted": {
                    "summary": "person_ai_credit_exhausted",
                    "value": {
                      "error": "person_ai_credit_exhausted",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/person_ai_credit_exhausted"
                    }
                  },
                  "upstream_rate_limited": {
                    "summary": "upstream_rate_limited",
                    "value": {
                      "error": "upstream_rate_limited",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/upstream_rate_limited"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "A processing service failed. No partial result is returned.\n\n- `coding_service_unavailable`: The model provider failed or could not be reached. The call may have reached the model: do not send it again automatically with a new X-Request-Id.\n- `invalid_coding_response`: The provider's stream failed validation before anything was passed on.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "coding_service_unavailable": {
                    "summary": "coding_service_unavailable",
                    "value": {
                      "error": "coding_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/coding_service_unavailable"
                    }
                  },
                  "invalid_coding_response": {
                    "summary": "invalid_coding_response",
                    "value": {
                      "error": "invalid_coding_response",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/invalid_coding_response"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Processing is not available right now.\n\n- `chat_cloud_unavailable`: Your organisation's EU cloud setup cannot be used right now.\n- `coding_not_configured`: The EU route has no coding terms or coding models on this server.\n- `coding_service_unavailable`: An unexpected failure before the provider was called.\n- `access_service_unavailable`: Keys and access could not be checked.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "chat_cloud_unavailable": {
                    "summary": "chat_cloud_unavailable",
                    "value": {
                      "error": "chat_cloud_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/chat_cloud_unavailable"
                    }
                  },
                  "coding_not_configured": {
                    "summary": "coding_not_configured",
                    "value": {
                      "error": "coding_not_configured",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/coding_not_configured"
                    }
                  },
                  "coding_service_unavailable": {
                    "summary": "coding_service_unavailable",
                    "value": {
                      "error": "coding_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/coding_service_unavailable"
                    }
                  },
                  "access_service_unavailable": {
                    "summary": "access_service_unavailable",
                    "value": {
                      "error": "access_service_unavailable",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable"
                    }
                  }
                }
              }
            }
          },
          "504": {
            "description": "The call timed out.\n\n- `timeout`: The answer did not finish within 600 seconds. The call reached the model: treat the outcome as unknown.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "required": [
                    "error",
                    "request_id",
                    "doc_url"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "statusCode": {
                      "type": "integer",
                      "description": "Only in a refusal Fastify writes itself: the HTTP status again."
                    },
                    "code": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: Fastify's own code, such as FST_ERR_CTP_BODY_TOO_LARGE."
                    },
                    "message": {
                      "type": "string",
                      "description": "Only in a refusal Fastify writes itself: a short English sentence."
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "This request's id, the same as the Request-Id header. Quote it when you ask komplai about the call."
                    },
                    "doc_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The page that explains this code and what to do about it."
                    }
                  }
                },
                "examples": {
                  "timeout": {
                    "summary": "timeout",
                    "value": {
                      "error": "timeout",
                      "request_id": "0f8e2c4a-6b1d-4e3f-9a7c-5d2b8e1f4a60",
                      "doc_url": "https://developers.komplai.dk/errors/timeout"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "x-komplai-error-codes": [
    {
      "code": "access_service_unavailable",
      "statuses": [
        503
      ],
      "meaning": "Keys and access could not be checked.",
      "action": "Try again shortly. If it keeps happening, contact komplai with the request_id.",
      "doc_url": "https://developers.komplai.dk/errors/access_service_unavailable",
      "operations": [
        "redactText",
        "detectPersonalData",
        "restoreRedactText",
        "submitRedactJob",
        "getRedactJob",
        "deleteRedactJob",
        "getRedactJobResult",
        "redactFile",
        "submitRedactFileJob",
        "readAccess",
        "listChatModels",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "readCodingAccount",
        "listCodingModels",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "agent_no_progress",
      "statuses": [
        502
      ],
      "meaning": "Redact Agentic stopped making progress: several rounds in a row read nothing new, found nothing and moved to no new step. No partial result is returned.",
      "action": "Send the text again, or run it with engine pipeline.",
      "doc_url": "https://developers.komplai.dk/errors/agent_no_progress",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": true
    },
    {
      "code": "agentic_disabled",
      "statuses": [
        503
      ],
      "meaning": "engine agentic is switched off on this server.",
      "action": "Leave engine out, which runs pipeline, until Redact Agentic is switched on again.",
      "doc_url": "https://developers.komplai.dk/errors/agentic_disabled",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "agentic_lookup_not_allowed",
      "statuses": [
        403
      ],
      "meaning": "A source in sources is not approved by the organisation's administrator.",
      "action": "Remove the source from sources, or ask your organisation's administrator to approve it.",
      "doc_url": "https://developers.komplai.dk/errors/agentic_lookup_not_allowed",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "agentic_lookup_unavailable",
      "statuses": [
        503
      ],
      "meaning": "A source in sources is not available right now.",
      "action": "Remove the source from sources, or try again later.",
      "doc_url": "https://developers.komplai.dk/errors/agentic_lookup_unavailable",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "agentic_preview_not_enabled",
      "statuses": [
        403
      ],
      "meaning": "The organisation's administrator has not turned on the Redact Agentic preview.",
      "action": "Ask your organisation's administrator to turn on the Redact Agentic preview, or leave engine out.",
      "doc_url": "https://developers.komplai.dk/errors/agentic_preview_not_enabled",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "ai_credit_exhausted",
      "statuses": [
        429
      ],
      "meaning": "The organisation's monthly AI spending cap for EU cloud is reached, or its own lower limit, or on a pilot the pilot's total allowance. Local processing keeps working.",
      "action": "Choose local processing for the call, or ask your organisation's administrator about the limit. Waiting does not help: a monthly cap starts again next month.",
      "doc_url": "https://developers.komplai.dk/errors/ai_credit_exhausted",
      "operations": [
        "redactText",
        "redactFile",
        "createChatCompletion",
        "createCodingChatCompletion"
      ],
      "job": true
    },
    {
      "code": "ai_credit_not_granted",
      "statuses": [
        403
      ],
      "meaning": "No monthly AI spending cap is set up for this month, so eu-cloud cannot run.",
      "action": "Choose local processing, or ask your organisation's administrator to have a monthly AI spending cap agreed for EU cloud.",
      "doc_url": "https://developers.komplai.dk/errors/ai_credit_not_granted",
      "operations": [
        "redactText",
        "redactFile",
        "createChatCompletion",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "api_key_required",
      "statuses": [
        401
      ],
      "meaning": "No Authorization: Bearer header with a komplai key.",
      "action": "Send the header Authorization: Bearer with a komplai key, one that starts with kp_live_ or kp_org_.",
      "doc_url": "https://developers.komplai.dk/errors/api_key_required",
      "operations": [
        "redactText",
        "detectPersonalData",
        "restoreRedactText",
        "submitRedactJob",
        "getRedactJob",
        "deleteRedactJob",
        "getRedactJobResult",
        "redactFile",
        "submitRedactFileJob",
        "readAccess",
        "listChatModels",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "readCodingAccount",
        "listCodingModels",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "app_not_assigned",
      "statuses": [
        403
      ],
      "meaning": "The key's person lost the app or their membership while the request was admitted.",
      "action": "Ask your organisation's administrator to assign you the app again.",
      "doc_url": "https://developers.komplai.dk/errors/app_not_assigned",
      "operations": [
        "redactText",
        "detectPersonalData",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "approved_redact_storage_policy_required",
      "statuses": [
        403
      ],
      "meaning": "Your organisation processes personal data but has no approved storage policy for text jobs.",
      "action": "Ask your organisation's administrator to approve a storage policy for text jobs, or send short texts to POST /v1/redact.",
      "doc_url": "https://developers.komplai.dk/errors/approved_redact_storage_policy_required",
      "operations": [
        "submitRedactJob",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "Bad Request",
      "statuses": [
        400
      ],
      "meaning": "Fastify's reason phrase: a path parameter does not have the documented form, for example a job id that is not a UUID.",
      "action": "Use the id exactly as the API returned it.",
      "doc_url": "https://developers.komplai.dk/errors/bad-request",
      "operations": [
        "getRedactJob",
        "deleteRedactJob",
        "getRedactJobResult"
      ],
      "job": false
    },
    {
      "code": "chat_cloud_unavailable",
      "statuses": [
        503
      ],
      "meaning": "The organisation's EU cloud setup cannot be used right now.",
      "action": "Try again later, or choose local processing. If it keeps happening, contact komplai with the request_id.",
      "doc_url": "https://developers.komplai.dk/errors/chat_cloud_unavailable",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob",
        "readAccess",
        "listChatModels",
        "createChatCompletion",
        "readCodingAccount",
        "listCodingModels",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "coding_not_configured",
      "statuses": [
        503
      ],
      "meaning": "komplai's EU route has no coding terms or coding models on this server.",
      "action": "Contact komplai with the request_id; there is nothing to change on your side.",
      "doc_url": "https://developers.komplai.dk/errors/coding_not_configured",
      "operations": [
        "readCodingAccount",
        "listCodingModels",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "coding_not_enabled",
      "statuses": [
        403
      ],
      "meaning": "Coding is not enabled for the organisation, its Chat does not run in komplai's EU cloud with an approved setup, or the key's person is not assigned Chat.",
      "action": "Ask your organisation's administrator for access to Chat. komplai turns coding on for the organisation; the administrator can contact komplai about it.",
      "doc_url": "https://developers.komplai.dk/errors/coding_not_enabled",
      "operations": [
        "readCodingAccount",
        "listCodingModels",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "coding_service_unavailable",
      "statuses": [
        502,
        503
      ],
      "meaning": "The coding model's provider failed or could not be reached, or an unexpected failure happened.",
      "action": "Try again later. If the stream had begun, the call may have reached the model: do not send it again automatically.",
      "doc_url": "https://developers.komplai.dk/errors/coding_service_unavailable",
      "operations": [
        "readCodingAccount",
        "listCodingModels",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "connection_failed",
      "statuses": [
        502
      ],
      "meaning": "The provider behind one of the organisation's own connections could not be reached or failed.",
      "action": "Try again later, or check the provider's status. The API never moves the call to other processing.",
      "doc_url": "https://developers.komplai.dk/errors/connection_failed",
      "operations": [
        "createChatCompletion"
      ],
      "job": false
    },
    {
      "code": "connection_model_unavailable",
      "statuses": [
        409
      ],
      "meaning": "The provider behind the connection does not know the model the connection names.",
      "action": "Ask your organisation's administrator to correct the connection's model on the hub's AI processing page.",
      "doc_url": "https://developers.komplai.dk/errors/connection_model_unavailable",
      "operations": [
        "createChatCompletion"
      ],
      "job": false
    },
    {
      "code": "connection_not_found",
      "statuses": [
        404
      ],
      "meaning": "No connection with this id is available to you through the API.",
      "action": "Check the connection's value on the hub's AI processing page, or ask your organisation's administrator for it.",
      "doc_url": "https://developers.komplai.dk/errors/connection_not_found",
      "operations": [
        "listChatModels",
        "createChatCompletion"
      ],
      "job": false
    },
    {
      "code": "connection_payment_required",
      "statuses": [
        502
      ],
      "meaning": "The provider behind the connection wants payment on the organisation's account.",
      "action": "Ask your organisation's administrator to settle the account with the provider.",
      "doc_url": "https://developers.komplai.dk/errors/connection_payment_required",
      "operations": [
        "createChatCompletion"
      ],
      "job": false
    },
    {
      "code": "connection_rate_limited",
      "statuses": [
        429
      ],
      "meaning": "The provider behind the connection refused the call for now.",
      "action": "Wait the seconds in Retry-After, then try again.",
      "doc_url": "https://developers.komplai.dk/errors/connection_rate_limited",
      "operations": [
        "createChatCompletion"
      ],
      "job": false
    },
    {
      "code": "connection_request_refused",
      "statuses": [
        502
      ],
      "meaning": "The provider behind the connection refused the request, for example a value it does not take.",
      "action": "Check the request against the provider's own limits, such as max_tokens or response_format.",
      "doc_url": "https://developers.komplai.dk/errors/connection_request_refused",
      "operations": [
        "createChatCompletion"
      ],
      "job": false
    },
    {
      "code": "connection_unauthorized",
      "statuses": [
        502
      ],
      "meaning": "The provider refused the connection's key.",
      "action": "Ask your organisation's administrator to renew the connection's key.",
      "doc_url": "https://developers.komplai.dk/errors/connection_unauthorized",
      "operations": [
        "createChatCompletion"
      ],
      "job": false
    },
    {
      "code": "connection_unavailable",
      "statuses": [
        503
      ],
      "meaning": "The connection cannot be used: its key cannot be read or its address is refused.",
      "action": "Ask your organisation's administrator to check the connection on the hub's AI processing page.",
      "doc_url": "https://developers.komplai.dk/errors/connection_unavailable",
      "operations": [
        "createChatCompletion"
      ],
      "job": false
    },
    {
      "code": "context_limit",
      "statuses": [
        413
      ],
      "meaning": "The input and the requested output do not fit the model: max_tokens is over the model's limit, the input with max_tokens is over its context, or, in Redact, a part of the text does not fit even after it was asked again in halves down to 1,500 characters.",
      "action": "Shorten the input or lower max_tokens. In Redact, split the text.",
      "doc_url": "https://developers.komplai.dk/errors/context_limit",
      "operations": [
        "redactText",
        "redactFile",
        "createChatCompletion",
        "createCodingChatCompletion"
      ],
      "job": true
    },
    {
      "code": "detection_service_unavailable",
      "statuses": [
        502,
        503
      ],
      "meaning": "The detection service could not be reached or failed.",
      "action": "Try again shortly. For a failed text job, submit the text again.",
      "doc_url": "https://developers.komplai.dk/errors/detection_service_unavailable",
      "operations": [
        "redactText",
        "detectPersonalData",
        "redactFile"
      ],
      "job": true
    },
    {
      "code": "detector_unavailable",
      "statuses": [
        503
      ],
      "meaning": "detectors includes ner, but NER is not set up.",
      "action": "Leave ner out of detectors, or try again later.",
      "doc_url": "https://developers.komplai.dk/errors/detector_unavailable",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": false
    },
    {
      "code": "duplicate_request",
      "statuses": [
        409
      ],
      "meaning": "This X-Request-Id was used before with this key, so nothing was sent to the model.",
      "action": "Use a new UUID for each new operation. If you were retrying an operation whose outcome you did not know, it had already reached komplai: do not send it again.",
      "doc_url": "https://developers.komplai.dk/errors/duplicate_request",
      "operations": [
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "empty_file",
      "statuses": [
        400
      ],
      "meaning": "file.data decodes to nothing.",
      "action": "Send the file's bytes, base64 encoded, in file.data.",
      "doc_url": "https://developers.komplai.dk/errors/empty_file",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "encrypted_file",
      "statuses": [
        422
      ],
      "meaning": "The PDF is password protected.",
      "action": "Remove the PDF's password and send it again.",
      "doc_url": "https://developers.komplai.dk/errors/encrypted_file",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "engine_retired",
      "statuses": [
        400
      ],
      "meaning": "engine review, the separate LLM engine, is retired. Send engine pipeline, or leave engine out, with detectors [\"regex\", \"llm\"]; hint says the same. Nothing was reserved or processed.",
      "action": "Send engine pipeline, or leave engine out, with detectors [\"regex\", \"llm\"].",
      "doc_url": "https://developers.komplai.dk/errors/engine_retired",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": true
    },
    {
      "code": "eu_cloud_refused",
      "statuses": [
        502
      ],
      "meaning": "komplai's EU cloud refused the call, so the model did not answer it.",
      "action": "Choose local processing or one of your organisation's connections, and contact komplai with the request_id. There is nothing to change on your side.",
      "doc_url": "https://developers.komplai.dk/errors/eu_cloud_refused",
      "operations": [
        "createChatCompletion"
      ],
      "job": false
    },
    {
      "code": "exceeds_ner_window",
      "statuses": [
        413
      ],
      "meaning": "text is over 16,000 UTF-16 code units, or the body is over 100,000 bytes. Use POST /v1/redact for longer texts.",
      "action": "Send longer texts to POST /v1/redact, or split the text.",
      "doc_url": "https://developers.komplai.dk/errors/exceeds_ner_window",
      "operations": [
        "detectPersonalData"
      ],
      "job": false
    },
    {
      "code": "file_extraction_unavailable",
      "statuses": [
        503
      ],
      "meaning": "The file reader could not start or stopped. This is not caused by the file.",
      "action": "Try again shortly. If it keeps happening, contact komplai with the request_id.",
      "doc_url": "https://developers.komplai.dk/errors/file_extraction_unavailable",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "file_too_complex",
      "statuses": [
        413
      ],
      "meaning": "Reading the file took more than 120 seconds or more resources than allowed (memory, ZIP entries, XML size or depth).",
      "action": "Split the file, or send its text to POST /v1/redact or as a text job.",
      "doc_url": "https://developers.komplai.dk/errors/file_too_complex",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "file_too_large",
      "statuses": [
        413
      ],
      "meaning": "The file is over 50 MiB, or the body is over its byte limit.",
      "action": "Split the file into parts under the limit.",
      "doc_url": "https://developers.komplai.dk/errors/file_too_large",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "file_type_mismatch",
      "statuses": [
        400
      ],
      "meaning": "The extension or contentType disagrees with the file's content.",
      "action": "Send the file's real extension and contentType, or leave contentType out.",
      "doc_url": "https://developers.komplai.dk/errors/file_type_mismatch",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "gateway_misconfigured",
      "statuses": [
        503
      ],
      "meaning": "The connection to the detection service or the language model is misconfigured.",
      "action": "Contact komplai with the request_id; there is nothing to change on your side.",
      "doc_url": "https://developers.komplai.dk/errors/gateway_misconfigured",
      "operations": [
        "redactText",
        "detectPersonalData",
        "redactFile"
      ],
      "job": false
    },
    {
      "code": "idempotency_conflict",
      "statuses": [
        409
      ],
      "meaning": "This Idempotency-Key was used with a different body. Use a new UUID for a new job.",
      "action": "Use a new UUID as Idempotency-Key for a new job.",
      "doc_url": "https://developers.komplai.dk/errors/idempotency_conflict",
      "operations": [
        "submitRedactJob",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "identity_retired",
      "statuses": [
        403
      ],
      "meaning": "The person behind a personal key has been removed from komplai.",
      "action": "Use a key of a person who is still in the organisation.",
      "doc_url": "https://developers.komplai.dk/errors/identity_retired",
      "operations": [
        "redactText",
        "detectPersonalData",
        "restoreRedactText",
        "submitRedactJob",
        "getRedactJob",
        "deleteRedactJob",
        "getRedactJobResult",
        "redactFile",
        "submitRedactFileJob",
        "readAccess",
        "listChatModels",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "readCodingAccount",
        "listCodingModels",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "incoherent_redaction_policy",
      "statuses": [
        503
      ],
      "meaning": "Your organisation's redaction and allow rules conflict for this text.",
      "action": "Ask your organisation's administrator to resolve the conflict between its redaction and allow rules.",
      "doc_url": "https://developers.komplai.dk/errors/incoherent_redaction_policy",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": false
    },
    {
      "code": "incomplete_detection",
      "statuses": [
        502
      ],
      "meaning": "A detection layer did not finish the whole text. No partial result is returned.",
      "action": "Send the text again. If it fails again, split it or leave out the layer that failed.",
      "doc_url": "https://developers.komplai.dk/errors/incomplete_detection",
      "operations": [
        "redactText",
        "detectPersonalData",
        "redactFile"
      ],
      "job": true
    },
    {
      "code": "insufficient_scope",
      "statuses": [
        403
      ],
      "meaning": "The key lacks a scope the operation needs (x-komplai-scopes): it was not issued or created with it, its person is no longer assigned the app, or the organisation's processing approval no longer covers it.",
      "action": "Use a key with the scope, or ask your organisation's administrator about the assignment.",
      "doc_url": "https://developers.komplai.dk/errors/insufficient_scope",
      "operations": [
        "redactText",
        "detectPersonalData",
        "restoreRedactText",
        "submitRedactJob",
        "getRedactJob",
        "deleteRedactJob",
        "getRedactJobResult",
        "redactFile",
        "submitRedactFileJob",
        "readAccess",
        "listChatModels",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "readCodingAccount",
        "listCodingModels",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "integration_not_permitted",
      "statuses": [
        403
      ],
      "meaning": "An organisation integration key cannot use this operation or this option.",
      "action": "Use a personal key for this operation, or leave the option out.",
      "doc_url": "https://developers.komplai.dk/errors/integration_not_permitted",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob",
        "listChatModels",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "readCodingAccount",
        "listCodingModels",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "Internal Server Error",
      "statuses": [
        500
      ],
      "meaning": "Fastify's reason phrase for a failure the API did not expect.",
      "action": "Try again. If it keeps happening, contact komplai with the request_id.",
      "doc_url": "https://developers.komplai.dk/errors/internal-server-error",
      "operations": [],
      "job": false
    },
    {
      "code": "invalid_api_key",
      "statuses": [
        401
      ],
      "meaning": "The key is unknown, expired, revoked or retired, the person behind a personal key is no longer a member or no longer assigned any app the key covers, or your organisation's API access for it is not set up or no longer active.",
      "action": "Check that you send the whole key. A personal key is created in the hub's API area; for a new organisation integration key, contact komplai.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_api_key",
      "operations": [
        "redactText",
        "detectPersonalData",
        "restoreRedactText",
        "submitRedactJob",
        "getRedactJob",
        "deleteRedactJob",
        "getRedactJobResult",
        "redactFile",
        "submitRedactFileJob",
        "readAccess",
        "listChatModels",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "readCodingAccount",
        "listCodingModels",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "invalid_body",
      "statuses": [
        400,
        415
      ],
      "meaning": "The body is not a JSON object with the documented fields: invalid JSON, Content-Type text/plain, an unknown field, a field of the wrong type, or a required field that is missing or blank.",
      "action": "Send a JSON object with the documented fields, and Content-Type application/json.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_body",
      "operations": [
        "redactText",
        "detectPersonalData",
        "restoreRedactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "invalid_coding_input",
      "statuses": [
        400
      ],
      "meaning": "The body is not a coding request as documented.",
      "action": "Correct the request against the field rules of POST /v1/coding/chat/completions.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_coding_input",
      "operations": [
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "invalid_coding_response",
      "statuses": [
        502
      ],
      "meaning": "The model provider's stream failed validation.",
      "action": "Try again later. If the stream had begun, do not send it again automatically.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_coding_response",
      "operations": [
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "invalid_detection",
      "statuses": [
        502
      ],
      "meaning": "A detection layer returned output that failed validation. For llm: an answer that still could not be used after its part of the text was asked again in halves down to 1,500 characters.",
      "action": "Send the text again, or leave out the layer that failed.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_detection",
      "operations": [
        "redactText",
        "detectPersonalData",
        "redactFile"
      ],
      "job": true
    },
    {
      "code": "invalid_detectors",
      "statuses": [
        400
      ],
      "meaning": "detectors is empty, has an unknown or repeated value, or more than three values.",
      "action": "Send one to three of regex, ner and llm, each once.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_detectors",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "invalid_file",
      "statuses": [
        400,
        422
      ],
      "meaning": "file is missing or malformed (filename missing, blank, longer than 255 characters or with control characters; data not a string; an unknown key), or the PDF or Word file itself is damaged or not a Word document.",
      "action": "Send a PDF or a Word .docx file that opens, with a filename.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_file",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "invalid_file_encoding",
      "statuses": [
        400
      ],
      "meaning": "file.data is empty or not base64.",
      "action": "Encode the file's bytes as base64 in file.data.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_file_encoding",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "invalid_idempotency_key",
      "statuses": [
        400
      ],
      "meaning": "Idempotency-Key is not a UUID, or it was sent more than once.",
      "action": "Send one Idempotency-Key header with a UUID, or none.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_idempotency_key",
      "operations": [
        "submitRedactJob",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "invalid_include",
      "statuses": [
        400
      ],
      "meaning": "include has an unknown or repeated value.",
      "action": "Send include values from mapping, findings, changes and skipped, each once.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_include",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "invalid_input",
      "statuses": [
        400
      ],
      "meaning": "The request is not what the operation documents, for example invalid JSON, a field that is not listed or a value out of range.",
      "action": "Correct the request against the operation's request schema.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_input",
      "operations": [
        "deleteRedactJob",
        "listChatModels",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "invalid_job",
      "statuses": [
        400
      ],
      "meaning": "job is not a UUID.",
      "action": "Send the job's id exactly as POST /v1/redact/jobs returned it.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_job",
      "operations": [
        "restoreRedactText"
      ],
      "job": false
    },
    {
      "code": "invalid_mapping",
      "statuses": [
        400
      ],
      "meaning": "mapping is not an object, or a value is empty, not a string, or contains NUL or a lone surrogate.",
      "action": "Send mapping exactly as POST /v1/redact returned it.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_mapping",
      "operations": [
        "restoreRedactText"
      ],
      "job": false
    },
    {
      "code": "invalid_ner_deployment",
      "statuses": [
        400
      ],
      "meaning": "nerDeployment is not eu-de or local.",
      "action": "Send eu-de or local, or leave nerDeployment out.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_ner_deployment",
      "operations": [
        "redactText",
        "detectPersonalData",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "invalid_organisation_policy",
      "statuses": [
        503
      ],
      "meaning": "Your organisation's redaction rules cannot be applied (more than 500 rules, or an invalid rule).",
      "action": "Ask your organisation's administrator to correct its redaction rules.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_organisation_policy",
      "operations": [
        "redactText",
        "restoreRedactText",
        "submitRedactJob",
        "getRedactJob",
        "deleteRedactJob",
        "getRedactJobResult",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "invalid_placeholder",
      "statuses": [
        400
      ],
      "meaning": "A mapping key is empty, longer than 128 UTF-16 code units, has surrounding spaces, contains NUL or a lone surrogate, or is `__proto__`, `constructor` or `prototype`.",
      "action": "Send mapping exactly as POST /v1/redact returned it.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_placeholder",
      "operations": [
        "restoreRedactText"
      ],
      "job": false
    },
    {
      "code": "invalid_processing",
      "statuses": [
        400
      ],
      "meaning": "processing is not local or eu-cloud.",
      "action": "Send local or eu-cloud, or leave processing out.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_processing",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "invalid_request_id",
      "statuses": [
        400
      ],
      "meaning": "X-Request-Id is missing or not a version 4 UUID.",
      "action": "Send a new version 4 UUID in X-Request-Id for each new operation.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_request_id",
      "operations": [
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "invalid_response",
      "statuses": [
        502
      ],
      "meaning": "The model's answer failed validation, so nothing was returned.",
      "action": "Try again. If it keeps happening, choose another model or contact komplai with the request_id.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_response",
      "operations": [
        "redactText",
        "redactFile",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts"
      ],
      "job": false
    },
    {
      "code": "invalid_restore_source",
      "statuses": [
        400
      ],
      "meaning": "Send exactly one of mapping and job.",
      "action": "Send exactly one of mapping and job.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_restore_source",
      "operations": [
        "restoreRedactText"
      ],
      "job": false
    },
    {
      "code": "invalid_sources",
      "statuses": [
        400
      ],
      "meaning": "sources has an unknown or repeated value.",
      "action": "Send sources from the list in GET /v1/access, each once.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_sources",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "invalid_text_encoding",
      "statuses": [
        400
      ],
      "meaning": "text contains NUL or a lone UTF-16 surrogate.",
      "action": "Remove NUL characters and lone UTF-16 surrogates from text.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_text_encoding",
      "operations": [
        "redactText",
        "restoreRedactText",
        "submitRedactJob"
      ],
      "job": false
    },
    {
      "code": "invalid_token",
      "statuses": [
        401
      ],
      "meaning": "The key was revoked or expired, or its access ended, while the request was admitted or processed.",
      "action": "Check that the key is still valid, then send the request again with a valid key.",
      "doc_url": "https://developers.komplai.dk/errors/invalid_token",
      "operations": [
        "redactText",
        "detectPersonalData",
        "restoreRedactText",
        "submitRedactJob",
        "getRedactJob",
        "deleteRedactJob",
        "getRedactJobResult",
        "redactFile",
        "submitRedactFileJob",
        "readAccess",
        "listChatModels",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "readCodingAccount",
        "listCodingModels",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "job_not_finished",
      "statuses": [
        409
      ],
      "meaning": "The job is still queued or running; state says which. Try again after Retry-After seconds.",
      "action": "Wait the seconds in Retry-After, then try again.",
      "doc_url": "https://developers.komplai.dk/errors/job_not_finished",
      "operations": [
        "restoreRedactText"
      ],
      "job": false
    },
    {
      "code": "llm_model_not_applicable",
      "statuses": [
        400
      ],
      "meaning": "model was sent, but llm is not in detectors, or engine is not pipeline.",
      "action": "Add llm to detectors, or leave model out.",
      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_applicable",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "llm_model_not_validated",
      "statuses": [
        400,
        409
      ],
      "meaning": "The language model in model is not validated for Redact at the processing you chose. GET /v1/access lists the models you may name.",
      "action": "Name a model that GET /v1/access lists as available, or leave model out once the default is validated.",
      "doc_url": "https://developers.komplai.dk/errors/llm_model_not_validated",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": true
    },
    {
      "code": "local_model_unavailable",
      "statuses": [
        503
      ],
      "meaning": "The local language model in model is validated for Redact, but local infrastructure does not offer it right now. The API never picks another model.",
      "action": "Try again later, or name another available model from GET /v1/access.",
      "doc_url": "https://developers.komplai.dk/errors/local_model_unavailable",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": true
    },
    {
      "code": "mapping_not_available",
      "statuses": [
        409
      ],
      "meaning": "The job kept neither mapping nor changes; hint says how to submit it again.",
      "action": "Submit the text again with include [\"mapping\"] or [\"changes\"].",
      "doc_url": "https://developers.komplai.dk/errors/mapping_not_available",
      "operations": [
        "restoreRedactText"
      ],
      "job": false
    },
    {
      "code": "model_busy",
      "statuses": [
        503
      ],
      "meaning": "The language model on local infrastructure cannot take this call right now: its shared queue is full, or no node can run the model (for example while it restarts). Wait Retry-After seconds; a text job waits by itself.",
      "action": "Wait the seconds in Retry-After, or send the text as a text job, which waits for its turn.",
      "doc_url": "https://developers.komplai.dk/errors/model_busy",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": false
    },
    {
      "code": "model_not_enabled",
      "statuses": [
        404,
        409
      ],
      "meaning": "The model is not one you may use at this processing, or it is not offered there right now.",
      "action": "Choose a model from the operation's model list (GET /v1/models, GET /v1/access or GET /v1/coding/models), or leave the model out for the default.",
      "doc_url": "https://developers.komplai.dk/errors/model_not_enabled",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "model_queue_expired",
      "statuses": [
        504
      ],
      "meaning": "The language model's shared queue on local infrastructure did not reach this call before its deadline. Try again later, or send it as a text job, which waits for its turn.",
      "action": "Try again later, or send the text as a text job.",
      "doc_url": "https://developers.komplai.dk/errors/model_queue_expired",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": true
    },
    {
      "code": "model_stalled",
      "statuses": [
        504
      ],
      "meaning": "The language model stopped answering: its connection was silent, or it ran past the time its answer needs at the model's measured speed. No partial result is returned.",
      "action": "Send the text again, or send it as a text job.",
      "doc_url": "https://developers.komplai.dk/errors/model_stalled",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": true
    },
    {
      "code": "ner_deployment_without_ner",
      "statuses": [
        400
      ],
      "meaning": "nerDeployment was sent, but ner is not in detectors (or mode is regex).",
      "action": "Add ner to detectors, or leave nerDeployment out.",
      "doc_url": "https://developers.komplai.dk/errors/ner_deployment_without_ner",
      "operations": [
        "redactText",
        "detectPersonalData",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "ner_location_unavailable",
      "statuses": [
        503
      ],
      "meaning": "The NER place cannot be used now: komplai has not switched it on for the API, or it did not answer. nerDeployment names the place. NER never moves to another place; GET /v1/access shows which places are available.",
      "action": "Choose a place that GET /v1/access lists as available, or leave nerDeployment out.",
      "doc_url": "https://developers.komplai.dk/errors/ner_location_unavailable",
      "operations": [
        "redactText",
        "detectPersonalData",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": true
    },
    {
      "code": "ner_model_preview_required",
      "statuses": [
        403
      ],
      "meaning": "The NER model in nerModel is a preview. It needs your organisation's Redact preview, which an organisation integration key does not have.",
      "action": "Choose a stable NER model from GET /v1/access, or leave nerModel out.",
      "doc_url": "https://developers.komplai.dk/errors/ner_model_preview_required",
      "operations": [
        "redactText",
        "detectPersonalData",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": true
    },
    {
      "code": "ner_model_unavailable",
      "statuses": [
        400
      ],
      "meaning": "The NER model in nerModel is retired or does not run at the chosen place. A model other than the default also needs detectors.",
      "action": "Choose a model that GET /v1/access lists for that place, or leave nerModel out.",
      "doc_url": "https://developers.komplai.dk/errors/ner_model_unavailable",
      "operations": [
        "redactText",
        "detectPersonalData",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": true
    },
    {
      "code": "ner_model_unconfigured",
      "statuses": [
        503
      ],
      "meaning": "The NER model in nerModel is not set up at the default place on this server.",
      "action": "Choose another model from GET /v1/access, or leave nerModel out.",
      "doc_url": "https://developers.komplai.dk/errors/ner_model_unconfigured",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": true
    },
    {
      "code": "ner_model_unknown",
      "statuses": [
        400
      ],
      "meaning": "No NER model has the id in nerModel. GET /v1/access lists the models you may choose.",
      "action": "Use a model id from GET /v1/access.",
      "doc_url": "https://developers.komplai.dk/errors/ner_model_unknown",
      "operations": [
        "redactText",
        "detectPersonalData",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": true
    },
    {
      "code": "ner_model_without_ner",
      "statuses": [
        400
      ],
      "meaning": "nerModel was sent, but ner is not in detectors (or mode is regex).",
      "action": "Add ner to detectors, or leave nerModel out.",
      "doc_url": "https://developers.komplai.dk/errors/ner_model_without_ner",
      "operations": [
        "redactText",
        "detectPersonalData",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "no_text_found",
      "statuses": [
        422
      ],
      "meaning": "The file contains no text at all.",
      "action": "Make the file's text readable first, for example with OCR; the API reads only a text layer.",
      "doc_url": "https://developers.komplai.dk/errors/no_text_found",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "Not Found",
      "statuses": [
        404
      ],
      "meaning": "Fastify's reason phrase: no operation has this method and path.",
      "action": "Check the method and path against the operations in this document.",
      "doc_url": "https://developers.komplai.dk/errors/not-found",
      "operations": [],
      "job": false
    },
    {
      "code": "not_found",
      "statuses": [
        404
      ],
      "meaning": "No job with this id for this key. A job is visible only to the key that created it, and it disappears when it expires.",
      "action": "Check the job's id and the key: a job is visible only to the key that created it, until it expires.",
      "doc_url": "https://developers.komplai.dk/errors/not_found",
      "operations": [
        "restoreRedactText",
        "getRedactJob",
        "deleteRedactJob",
        "getRedactJobResult"
      ],
      "job": false
    },
    {
      "code": "Payload Too Large",
      "statuses": [
        413
      ],
      "meaning": "Fastify's reason phrase: the body is over the operation's byte limit.",
      "action": "Send a smaller body; each operation states its limit.",
      "doc_url": "https://developers.komplai.dk/errors/payload-too-large",
      "operations": [
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "person_ai_credit_exhausted",
      "statuses": [
        429
      ],
      "meaning": "Your personal AI limit is reached (personal keys only).",
      "action": "Choose local processing, or ask your organisation's administrator about your personal limit.",
      "doc_url": "https://developers.komplai.dk/errors/person_ai_credit_exhausted",
      "operations": [
        "redactText",
        "redactFile",
        "createChatCompletion",
        "createCodingChatCompletion"
      ],
      "job": true
    },
    {
      "code": "processing_access_changed",
      "statuses": [
        403,
        409
      ],
      "meaning": "The organisation's processing setup changed while the request was handled. Send it again.",
      "action": "Send the request again.",
      "doc_url": "https://developers.komplai.dk/errors/processing_access_changed",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "processing_access_withdrawn",
      "statuses": [
        403
      ],
      "meaning": "Access to the job was withdrawn after it was created: the agreement, the processing setup, the organisation's redaction rules or the storage policy for text jobs changed. The job is invalidated and its text and result are deleted. This is final: stop reading the job and do not reuse its Idempotency-Key; submit the text again as a new job when access allows it.",
      "action": "Stop reading the job. Submit the text again as a new job, with a new Idempotency-Key, when access allows it.",
      "doc_url": "https://developers.komplai.dk/errors/processing_access_withdrawn",
      "operations": [
        "restoreRedactText",
        "submitRedactJob",
        "getRedactJob",
        "deleteRedactJob",
        "getRedactJobResult",
        "submitRedactFileJob"
      ],
      "job": true
    },
    {
      "code": "processing_capacity_busy",
      "statuses": [
        429
      ],
      "meaning": "All organisation-rule workers in this API process are busy. There is no queue: try again after Retry-After.",
      "action": "Wait the seconds in Retry-After, then try again.",
      "doc_url": "https://developers.komplai.dk/errors/processing_capacity_busy",
      "operations": [
        "redactText",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "processing_choice_not_allowed",
      "statuses": [
        403
      ],
      "meaning": "The organisation does not allow this processing (local infrastructure or EU cloud) through the API, or the connection is not made available through the API.",
      "action": "Choose processing your organisation allows, or ask its administrator.",
      "doc_url": "https://developers.komplai.dk/errors/processing_choice_not_allowed",
      "operations": [
        "redactText",
        "detectPersonalData",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob",
        "listChatModels",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "createCodingChatCompletion"
      ],
      "job": true
    },
    {
      "code": "processing_complexity_exceeded",
      "statuses": [
        413
      ],
      "meaning": "The text needs more work than its length allows: more candidate matches or matched characters than the per-character bounds in /v1/access, coreference matching work beyond its per-character bound (for example one name nested inside itself thousands of times), or more time for the organisation rules than their limit, which grows with the text and the number of rules. The bounds grow with the text, so splitting it rarely helps. Nothing partial is returned.",
      "action": "Check the text for long repetitive patterns, or ask your organisation's administrator to simplify its rules. Splitting the text rarely helps.",
      "doc_url": "https://developers.komplai.dk/errors/processing_complexity_exceeded",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": true
    },
    {
      "code": "processing_not_approved",
      "statuses": [
        403
      ],
      "meaning": "The organisation's processing approval for the app is missing or was withdrawn.",
      "action": "Ask your organisation's administrator; processing is approved in the organisation's agreement with komplai.",
      "doc_url": "https://developers.komplai.dk/errors/processing_not_approved",
      "operations": [
        "redactText",
        "detectPersonalData",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "processing_not_enabled",
      "statuses": [
        403
      ],
      "meaning": "The processing is not enabled for the app in the organisation: EU processing is not approved and enabled, or the agreement keeps processing on local infrastructure.",
      "action": "Choose local processing, or ask your organisation's administrator.",
      "doc_url": "https://developers.komplai.dk/errors/processing_not_enabled",
      "operations": [
        "redactText",
        "detectPersonalData",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob",
        "listChatModels",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "processing_outcome_uncertain",
      "statuses": [],
      "meaning": "A text job submitted before 20 October 2026 was interrupted during work by the language model, so komplai cannot tell whether that step finished. A job now resumes from its checkpoint instead.",
      "action": "Submit the text again as a new job.",
      "doc_url": "https://developers.komplai.dk/errors/processing_outcome_uncertain",
      "operations": [],
      "job": true
    },
    {
      "code": "processing_policy_changed",
      "statuses": [
        409
      ],
      "meaning": "Your organisation's redaction rules changed while the request ran. Send it again.",
      "action": "Send the request again.",
      "doc_url": "https://developers.komplai.dk/errors/processing_policy_changed",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": false
    },
    {
      "code": "processing_timeout",
      "statuses": [
        504
      ],
      "meaning": "The call took more than 300 seconds, or the client disconnected. Send long texts as text jobs.",
      "action": "Send long texts as a text job (POST /v1/redact/jobs).",
      "doc_url": "https://developers.komplai.dk/errors/processing_timeout",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": false
    },
    {
      "code": "rate_limited",
      "statuses": [
        429
      ],
      "meaning": "Too many requests: the per-minute limit of the person behind a personal key (shared with that person's other keys and use of the hub) or of the integration behind an organisation integration key (shared by its keys), the organisation's (120), the API's (300) or the per-IP network limit. Wait Retry-After seconds.",
      "action": "Wait the seconds in Retry-After, then try again. Spread calls out; GET /v1/access shows your limits.",
      "doc_url": "https://developers.komplai.dk/errors/rate_limited",
      "operations": [
        "redactText",
        "detectPersonalData",
        "restoreRedactText",
        "submitRedactJob",
        "getRedactJob",
        "deleteRedactJob",
        "getRedactJobResult",
        "redactFile",
        "submitRedactFileJob",
        "readAccess",
        "listChatModels",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "readCodingAccount",
        "listCodingModels",
        "createCodingChatCompletion"
      ],
      "job": true
    },
    {
      "code": "reasoning_effort_not_supported",
      "statuses": [
        409
      ],
      "meaning": "The reasoning effort komplai set for this EU language model in Redact is not one the model accepts on your organisation's EU route. Nothing was sent to the model, and the API does not send another effort.",
      "action": "Name another model in the meantime, and tell komplai the request_id.",
      "doc_url": "https://developers.komplai.dk/errors/reasoning_effort_not_supported",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": true
    },
    {
      "code": "reasoning_not_enabled",
      "statuses": [
        409
      ],
      "meaning": "The model does not take this reasoning_effort, or one of the organisation's own connections was sent one other than none.",
      "action": "Send a value from the model's reasoning_efforts in GET /v1/models, or leave reasoning_effort out.",
      "doc_url": "https://developers.komplai.dk/errors/reasoning_not_enabled",
      "operations": [
        "createChatCompletion"
      ],
      "job": false
    },
    {
      "code": "reasoning_not_supported",
      "statuses": [
        400
      ],
      "meaning": "The coding model does not take this reasoning_effort.",
      "action": "Send a value from the model's reasoning_efforts in GET /v1/coding/models, or leave reasoning_effort out.",
      "doc_url": "https://developers.komplai.dk/errors/reasoning_not_supported",
      "operations": [
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "redact_capabilities_unavailable",
      "statuses": [
        503
      ],
      "meaning": "Redact's processing details could not be read.",
      "action": "Try again shortly.",
      "doc_url": "https://developers.komplai.dk/errors/redact_capabilities_unavailable",
      "operations": [
        "readAccess"
      ],
      "job": false
    },
    {
      "code": "redact_file_not_enabled",
      "statuses": [
        503
      ],
      "meaning": "File input is switched off on this server.",
      "action": "Send the file's text to POST /v1/redact or as a text job instead.",
      "doc_url": "https://developers.komplai.dk/errors/redact_file_not_enabled",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "redact_job_attempts_exhausted",
      "statuses": [
        410
      ],
      "meaning": "The text job was interrupted three times, for example by restarts, and was stopped. No partial result is kept.",
      "action": "Submit the text again as a new job.",
      "doc_url": "https://developers.komplai.dk/errors/redact_job_attempts_exhausted",
      "operations": [
        "getRedactJobResult"
      ],
      "job": true
    },
    {
      "code": "redact_job_cancelled",
      "statuses": [
        410
      ],
      "meaning": "The job was deleted or cancelled.",
      "action": "Submit the text again if you still need the result.",
      "doc_url": "https://developers.komplai.dk/errors/redact_job_cancelled",
      "operations": [
        "restoreRedactText",
        "getRedactJobResult"
      ],
      "job": true
    },
    {
      "code": "redact_job_failed",
      "statuses": [
        410
      ],
      "meaning": "The job failed for a reason that is not passed on, for example a reached monthly AI spending cap or a failing EU cloud model.",
      "action": "Submit the text again as a new job. If it fails again, contact komplai with the request_id.",
      "doc_url": "https://developers.komplai.dk/errors/redact_job_failed",
      "operations": [
        "restoreRedactText",
        "getRedactJobResult"
      ],
      "job": true
    },
    {
      "code": "redact_job_integrity_failed",
      "statuses": [
        503
      ],
      "meaning": "The job's encrypted data could not be read.",
      "action": "Submit the text again as a new job, and tell komplai the request_id.",
      "doc_url": "https://developers.komplai.dk/errors/redact_job_integrity_failed",
      "operations": [
        "restoreRedactText",
        "submitRedactJob",
        "getRedactJobResult",
        "submitRedactFileJob"
      ],
      "job": true
    },
    {
      "code": "redact_job_progress_failed",
      "statuses": [],
      "meaning": "The worker could not record the job's progress, so it stopped the job instead of letting it run unrecorded. No partial result is kept. Submit it again.",
      "action": "Submit the text again as a new job.",
      "doc_url": "https://developers.komplai.dk/errors/redact_job_progress_failed",
      "operations": [],
      "job": true
    },
    {
      "code": "redact_job_service_unavailable",
      "statuses": [
        503
      ],
      "meaning": "The job store failed.",
      "action": "Try again shortly.",
      "doc_url": "https://developers.komplai.dk/errors/redact_job_service_unavailable",
      "operations": [
        "restoreRedactText",
        "submitRedactJob",
        "getRedactJob",
        "deleteRedactJob",
        "getRedactJobResult",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "redact_job_stalled",
      "statuses": [],
      "meaning": "The job made no progress for 65 minutes, longer than any single step can take, and was stopped. Waiting for the language model on local infrastructure is not progress. No partial result is kept.",
      "action": "Submit the text again as a new job, or later when local infrastructure is less busy.",
      "doc_url": "https://developers.komplai.dk/errors/redact_job_stalled",
      "operations": [],
      "job": true
    },
    {
      "code": "redact_job_unavailable",
      "statuses": [
        410
      ],
      "meaning": "The job ended without a result and without a stored reason.",
      "action": "Submit the text again as a new job.",
      "doc_url": "https://developers.komplai.dk/errors/redact_job_unavailable",
      "operations": [
        "restoreRedactText"
      ],
      "job": false
    },
    {
      "code": "redact_jobs_not_enabled",
      "statuses": [
        503
      ],
      "meaning": "Text jobs are not accepted right now; GET /v1/access shows text_contract.jobs_accepting_submissions.",
      "action": "Send short texts to POST /v1/redact, and try text jobs again later.",
      "doc_url": "https://developers.komplai.dk/errors/redact_jobs_not_enabled",
      "operations": [
        "submitRedactJob",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "redact_restore_unavailable",
      "statuses": [
        503
      ],
      "meaning": "Unexpected failure while restoring.",
      "action": "Try again shortly.",
      "doc_url": "https://developers.komplai.dk/errors/redact_restore_unavailable",
      "operations": [
        "restoreRedactText"
      ],
      "job": false
    },
    {
      "code": "redact_runtime_unavailable",
      "statuses": [
        503
      ],
      "meaning": "A selected layer (NER or the language model) is not set up or not available right now.",
      "action": "Leave out the layer that is not available, or try again later.",
      "doc_url": "https://developers.komplai.dk/errors/redact_runtime_unavailable",
      "operations": [
        "redactText",
        "detectPersonalData",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": true
    },
    {
      "code": "restore_too_large",
      "statuses": [
        413
      ],
      "meaning": "The body, the mapping (more than 100,000 entries, more than 2,000,000 UTF-16 code units or more than 500 fixed tokens) or the restored text (over 8,000,000 UTF-16 code units) is too large.",
      "action": "Restore the text in parts, each with only the labels it contains.",
      "doc_url": "https://developers.komplai.dk/errors/restore_too_large",
      "operations": [
        "restoreRedactText"
      ],
      "job": false
    },
    {
      "code": "result_too_large",
      "statuses": [
        413
      ],
      "meaning": "The result would be over 32 MiB. Ask for fewer include reports or split the text.",
      "action": "Ask for fewer include reports, or split the text.",
      "doc_url": "https://developers.komplai.dk/errors/result_too_large",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": true
    },
    {
      "code": "scanned_pages_not_supported",
      "statuses": [
        422
      ],
      "meaning": "A page has no text layer, for example a scan. No OCR runs. pages lists the pages; allowPagesWithoutText true processes the rest.",
      "action": "Send allowPagesWithoutText true to process the other pages, or make the scanned pages readable first.",
      "doc_url": "https://developers.komplai.dk/errors/scanned_pages_not_supported",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "semantic_runtime_not_configured",
      "statuses": [
        503
      ],
      "meaning": "No local language model is set up for llm.",
      "action": "Leave llm out of detectors, or choose processing eu-cloud if your organisation has it.",
      "doc_url": "https://developers.komplai.dk/errors/semantic_runtime_not_configured",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": false
    },
    {
      "code": "service_unavailable",
      "statuses": [
        502,
        503
      ],
      "meaning": "The model failed or could not be reached, or local infrastructure is not set up for this call right now.",
      "action": "Try again later. If the call may have reached the model, do not send it again automatically.",
      "doc_url": "https://developers.komplai.dk/errors/service_unavailable",
      "operations": [
        "redactText",
        "redactFile",
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts"
      ],
      "job": false
    },
    {
      "code": "terms_changed",
      "statuses": [
        409
      ],
      "meaning": "X-Komplai-Contract is not the current contract: the provider, its terms, the data mode, the agreement or the coding models and their prices changed.",
      "action": "Read GET /v1/coding/account again, show the new terms to the person, and send the new contract.",
      "doc_url": "https://developers.komplai.dk/errors/terms_changed",
      "operations": [
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "text_too_large",
      "statuses": [
        413
      ],
      "meaning": "text is over the operation's limit in UTF-16 code units, or the body is over its byte limit.",
      "action": "Send the text as a text job, or split it under the operation's limit.",
      "doc_url": "https://developers.komplai.dk/errors/text_too_large",
      "operations": [
        "redactText",
        "restoreRedactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "timeout",
      "statuses": [
        504
      ],
      "meaning": "The model did not answer in time. The call reached the model, so its outcome is unknown.",
      "action": "Try again with a shorter input or a lower max_tokens; do not send it again automatically.",
      "doc_url": "https://developers.komplai.dk/errors/timeout",
      "operations": [
        "createChatCompletion",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "too_many_pages",
      "statuses": [
        413
      ],
      "meaning": "The PDF has more than 2,000 pages.",
      "action": "Split the PDF into parts of at most 2,000 pages.",
      "doc_url": "https://developers.komplai.dk/errors/too_many_pages",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "unsafe_placeholder",
      "statuses": [
        502
      ],
      "meaning": "A fixed replacement token from your organisation's rules occurs in the text or collides with a label.",
      "action": "Remove the fixed replacement token from the text, or ask your organisation's administrator to change it.",
      "doc_url": "https://developers.komplai.dk/errors/unsafe_placeholder",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": false
    },
    {
      "code": "Unsupported Media Type",
      "statuses": [
        415
      ],
      "meaning": "Fastify's reason phrase: the body has no Content-Type, or one the API does not read, such as XML or form data.",
      "action": "Send Content-Type: application/json.",
      "doc_url": "https://developers.komplai.dk/errors/unsupported-media-type",
      "operations": [
        "createChatCompletion",
        "evaluateText",
        "evaluateTexts",
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "unsupported_engine_detectors",
      "statuses": [
        400
      ],
      "meaning": "The detectors do not fit engine agentic: it needs regex and llm, with optional ner.",
      "action": "For engine agentic, send detectors regex and llm, with ner if you want it.",
      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_detectors",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "unsupported_engine_mode",
      "statuses": [
        400
      ],
      "meaning": "mode disagrees with detectors. Leave mode out when you send detectors.",
      "action": "Leave mode out when you send detectors.",
      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_mode",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "unsupported_engine_sources",
      "statuses": [
        400
      ],
      "meaning": "sources was sent without engine agentic.",
      "action": "Leave sources out, or send engine agentic.",
      "doc_url": "https://developers.komplai.dk/errors/unsupported_engine_sources",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "unsupported_file_type",
      "statuses": [
        400
      ],
      "meaning": "The file is neither a PDF nor a Word .docx, by its content, its extension or its contentType.",
      "action": "Send a PDF or a Word .docx file.",
      "doc_url": "https://developers.komplai.dk/errors/unsupported_file_type",
      "operations": [
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "unsupported_pilot_mode",
      "statuses": [
        400
      ],
      "meaning": "mode is not regex or ner, or semantic is true. No language model runs on this operation.",
      "action": "Send mode regex or ner, and leave semantic out. The language model runs on POST /v1/redact.",
      "doc_url": "https://developers.komplai.dk/errors/unsupported_pilot_mode",
      "operations": [
        "detectPersonalData"
      ],
      "job": false
    },
    {
      "code": "unsupported_processing",
      "statuses": [
        400
      ],
      "meaning": "processing eu-cloud needs llm in detectors (and engine pipeline). Add llm to detectors.",
      "action": "Add llm to detectors, or leave processing out.",
      "doc_url": "https://developers.komplai.dk/errors/unsupported_processing",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "unsupported_redact_engine",
      "statuses": [
        400
      ],
      "meaning": "engine is not one of the documented values.",
      "action": "Send engine pipeline, or leave engine out.",
      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_engine",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "unsupported_redact_mode",
      "statuses": [
        400
      ],
      "meaning": "mode is not regex, ner or semantic.",
      "action": "Send detectors instead of mode.",
      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_mode",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "unsupported_redact_policy",
      "statuses": [
        400
      ],
      "meaning": "policy is not all-detected.",
      "action": "Leave policy out, or send all-detected.",
      "doc_url": "https://developers.komplai.dk/errors/unsupported_redact_policy",
      "operations": [
        "redactText",
        "submitRedactJob",
        "redactFile",
        "submitRedactFileJob"
      ],
      "job": false
    },
    {
      "code": "upstream_rate_limited",
      "statuses": [
        429
      ],
      "meaning": "The coding model's provider refused the call for now.",
      "action": "Try again later.",
      "doc_url": "https://developers.komplai.dk/errors/upstream_rate_limited",
      "operations": [
        "createCodingChatCompletion"
      ],
      "job": false
    },
    {
      "code": "use_job",
      "statuses": [
        413
      ],
      "meaning": "The run cannot finish within the 300 seconds a synchronous call may take. estimatedSeconds is komplai's estimate from its measured speeds, with an empty queue; holdSeconds is the limit. Nothing was processed or charged. Send the same body as a text job (POST /v1/redact/jobs), or this call with Prefer: respond-async.",
      "action": "Send the same body as a text job (POST /v1/redact/jobs), or this call with the header Prefer: respond-async.",
      "doc_url": "https://developers.komplai.dk/errors/use_job",
      "operations": [
        "redactText",
        "redactFile"
      ],
      "job": false
    }
  ]
}
