{"openapi":"3.1.0","info":{"title":"Headband API","version":"1.0.0","summary":"One API across Meilisearch, Typesense, Elasticsearch and Visor.","description":"The public /v1 API. Every path also answers under /api/v1. Authenticate with `Authorization: Bearer <key>`. Every response carries `X-Request-Id`, and every error body carries the same `requestId`. Versioning and deprecation policy: https://headband.dev/docs/api-reference#versioning","contact":{"name":"Headband","url":"https://headband.dev/contact"},"termsOfService":"https://headband.dev/terms"},"servers":[{"url":"https://headband.dev","description":"Production"}],"externalDocs":{"description":"Headband documentation","url":"https://headband.dev/docs"},"tags":[{"name":"Search","description":"Search, multi-search, hybrid and AI search."},{"name":"Indexes","description":"Create, read, configure and delete indexes."},{"name":"Documents","description":"Add, read and delete documents."},{"name":"Tasks","description":"Asynchronous write tasks."},{"name":"Bulk","description":"Large batched writes."},{"name":"Analytics","description":"Search analytics and click events."},{"name":"Experiments","description":"A/B tests between engines or settings."},{"name":"Connectors","description":"Connector runs."},{"name":"Branches","description":"Branches of a project's indexes."},{"name":"Schema","description":"Schema state and aliases for the CLI's migration flow."},{"name":"Keys","description":"API keys (CLI-minted keys only)."},{"name":"Projects","description":"Projects (CLI-minted keys only)."},{"name":"Instances","description":"Dedicated engine instances."},{"name":"Sync","description":"Database-to-index sync. Early access, per org."},{"name":"System","description":"Health, warm-up, llms.txt and this document."}],"paths":{"/v1/ai/search":{"post":{"operationId":"aiSearch","summary":"Search, formatted for RAG","description":"Searches one index and returns each hit as clean text for an LLM context: string fields with HTML stripped as `content`, the other fields as `metadata`, and the document id as `source`. Counts toward the monthly searches, but not the plan's searches a second. No paging: `limit` is passed to the engine as sent.","tags":["Search"],"security":[{"adminKey":[]},{"searchKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin","search"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["index"],"properties":{"index":{"type":"string","description":"The index uid."},"q":{"type":"string"},"limit":{"type":"integer","default":20},"filter":{"type":"string"},"experimentKey":{"type":"string","description":"Recorded with the search's analytics."},"variant":{"type":"string","description":"Recorded with the search's analytics."},"sessionId":{"type":"string","description":"Recorded with the search's analytics."}}}}}},"responses":{"200":{"description":"RAG-ready results.","content":{"application/json":{"schema":{"type":"object","required":["results","query","processingTimeMs","totalResults"],"properties":{"results":{"type":"array","items":{"type":"object","required":["content","metadata","source"],"properties":{"content":{"type":"string","description":"The hit's string fields, HTML stripped."},"metadata":{"type":"object","additionalProperties":true},"source":{"type":"object","properties":{"index":{"type":"string"},"documentId":{"oneOf":[{"type":"string"},{"type":"number"}]}}},"relevanceScore":{"type":"number"}}}},"query":{"type":"string"},"processingTimeMs":{"type":"number"},"totalResults":{"type":"integer"}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad request. Codes include `invalid_json`, `index_required`, `invalid_index_uid`, `invalid_branch_name`, `invalid_search_filter`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (with `requiredPermission`), `branch_not_allowed` (a branch key naming another branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"The index is not in the request's scope (`index_not_found`), or the Headband-Branch names no live branch (`branch_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The uid names an index on more than one branch in scope (`index_ambiguous`). Pick one with Headband-Branch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"description":"Rate limit or the plan's monthly search cap. Codes: `usage_limit_exceeded` (monthly cap, with `plan`, `current`, `limit`, `resetsAt`, `upgradeUrl`; `Retry-After` counts to the reset). The per-key and per-IP request limits answer with no code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"description":"The engine or an auth lookup is unavailable for now. Codes include `auth_lookup_failed`, `engine_not_configured`, `instance_waking`, `index_migrating` (with `Retry-After`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}},"/v1/branches":{"get":{"operationId":"listBranches","summary":"List branches","description":"Live branches. A branch key lists its own project's. An org key lists one project's with `?project=` (or the project Headband-Branch names in its `<project id>/<branch>` form), else every live branch of the org. Admin keys only.","tags":["Branches"],"security":[{"adminKey":[]}],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"project","in":"query","required":false,"description":"A project id. An org key names the project here (or with Headband-Branch's `<project id>/<branch>` form) to address a branch by name.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The branches.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"branches":{"type":"array","items":{"type":"object","required":["id","name","projectId","primary","protected","expiresAt","state","parentId","createdAt"],"properties":{"id":{"type":"string","description":"The branch id (`br_` and 12 characters)."},"name":{"type":"string"},"projectId":{"type":"string"},"primary":{"type":"boolean","description":"True for the project's production branch."},"protected":{"type":"boolean","description":"Always true for the primary branch."},"expiresAt":{"type":["string","null"],"format":"date-time","description":"Null for never."},"state":{"type":"string","description":"Such as `ready` or `deleting`."},"parentId":{"type":["string","null"],"description":"Null for a project's first branch."},"createdAt":{"type":"string","format":"date-time"}}}}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_branch_name` (a malformed Headband-Branch), `invalid_request` (`project` and Headband-Branch name different projects).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Codes: `permission_denied` (a search key), `branch_not_allowed` (a branch key naming another project or branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `project_not_found`, `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"post":{"operationId":"createBranch","summary":"Create a branch","description":"Creates a branch in a project. With `empty: true` it is empty: no indexes, no instances. Without it, the branch is a fork of the parent (default: the project's primary branch): every index of the parent, with its data and settings, on new serverless engines that share the parent's storage copy-on-write. A fork holds the parent's data as of its last flush to bucket storage, and writes on either side never reach the other. Meilisearch and Elasticsearch indexes fork; anything else (Typesense, Visor, your own engine, an engine with no copy in bucket storage yet) makes the whole create 409 `fork_not_supported`, naming the indexes, and nothing is made. A forked Elasticsearch engine parks after 30 idle minutes and boots again on the next request. Forking is refused with 409 `fork_not_available` until it is switched on, and every create is 409 `branches_not_open` while branches other than main are not open. A branch key creates in its own project; an org key names the project with `projectId` or Headband-Branch's `<project id>/<branch>` form. Needs `branches.manage`, which only keys minted by `headband login` (the CLI device flow) hold; any other key gets 403 `branches_manage_required`.","tags":["Branches"],"security":[{"adminKey":[]}],"x-headband-permissions":["branches.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Refused with 409 `branches_not_open` until non-primary branches are switched on.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"The new branch's name. Names are 1 to 63 characters: lower-case letters, digits, `.`, `_`, `/` and `-`, starting and ending with a letter or digit, and not shaped like a branch id."},"empty":{"type":"boolean","description":"True for an empty branch. Absent or false forks the parent."},"parent":{"type":["string","null"],"description":"The parent branch's id or name."},"expiresAt":{"type":["string","null"],"format":"date-time","description":"A future ISO 8601 time with an offset. The branch is deleted after it."},"projectId":{"type":"string","description":"The project. Needed by an org key unless Headband-Branch names one."}}}}}},"responses":{"201":{"description":"The branch was created. A fork's answer adds `instances` (its engines), `forkedIndexes` and `forkMs`.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"type":"object","required":["id","name","projectId","primary","protected","expiresAt","state","parentId","createdAt"],"properties":{"id":{"type":"string","description":"The branch id (`br_` and 12 characters)."},"name":{"type":"string"},"projectId":{"type":"string"},"primary":{"type":"boolean","description":"True for the project's production branch."},"protected":{"type":"boolean","description":"Always true for the primary branch."},"expiresAt":{"type":["string","null"],"format":"date-time","description":"Null for never."},"state":{"type":"string","description":"Such as `ready` or `deleting`."},"parentId":{"type":["string","null"],"description":"Null for a project's first branch."},"createdAt":{"type":"string","format":"date-time"}}},{"type":"object","properties":{"instances":{"type":"array","items":{"type":"object","required":["id","engine","endpoint"],"properties":{"id":{"type":"string","description":"The engine instance id."},"engine":{"type":"string","description":"`meilisearch`, `elasticsearch`, `typesense` or `visor`."},"endpoint":{"type":"string","description":"The engine's native endpoint."},"fromInstanceId":{"type":"string","description":"On a fork's create answer only: the parent engine it was forked from."}}}},"forkedIndexes":{"type":"integer"},"forkMs":{"type":"integer","description":"How long the fork took."}}}]}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_json`, `invalid_branch_name`, `invalid_request`, `invalid_expires_at`, `branch_required` (an org key named no project).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Codes: `plan_branch_limit`, `plan_project_read_only`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Codes: `branches_manage_required`, `branch_not_allowed`, `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `project_not_found`, `branch_not_found` (the parent).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `branches_not_open`, `fork_not_available`, `fork_not_supported` (with `unforkable`: each index's `uid`, `engine` and `reason`), `branch_exists`, `branch_deleting`, `branch_not_ready` (the parent).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Codes: `fork_busy` (storage maintenance on the parent; `Retry-After` set), `fork_unavailable`, `no_capacity`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}}}},"/v1/branches/{branch}":{"parameters":[{"name":"branch","in":"path","required":true,"description":"A branch id (`br_...`) or name. An org key naming a branch by name also gives its project with `?project=` or Headband-Branch.","schema":{"type":"string"}}],"get":{"operationId":"getBranch","summary":"Get a branch","description":"One branch the key reaches, with `instances`: its live engines and their native endpoints. A branch being deleted is still returned, with `state: deleting`; a fork still being made has `state: creating`. Admin keys only.","tags":["Branches"],"security":[{"adminKey":[]}],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"project","in":"query","required":false,"description":"A project id. An org key names the project here (or with Headband-Branch's `<project id>/<branch>` form) to address a branch by name.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The branch.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"type":"object","required":["id","name","projectId","primary","protected","expiresAt","state","parentId","createdAt"],"properties":{"id":{"type":"string","description":"The branch id (`br_` and 12 characters)."},"name":{"type":"string"},"projectId":{"type":"string"},"primary":{"type":"boolean","description":"True for the project's production branch."},"protected":{"type":"boolean","description":"Always true for the primary branch."},"expiresAt":{"type":["string","null"],"format":"date-time","description":"Null for never."},"state":{"type":"string","description":"Such as `ready` or `deleting`."},"parentId":{"type":["string","null"],"description":"Null for a project's first branch."},"createdAt":{"type":"string","format":"date-time"}}},{"type":"object","properties":{"instances":{"type":"array","items":{"type":"object","required":["id","engine","endpoint"],"properties":{"id":{"type":"string","description":"The engine instance id."},"engine":{"type":"string","description":"`meilisearch`, `elasticsearch`, `typesense` or `visor`."},"endpoint":{"type":"string","description":"The engine's native endpoint."},"fromInstanceId":{"type":"string","description":"On a fork's create answer only: the parent engine it was forked from."}}}}}}]}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_branch_name`, `branch_required`, `invalid_request`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Codes: `permission_denied` (a search key), `branch_not_allowed`, `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `branch_not_found`, `project_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"patch":{"operationId":"updateBranch","summary":"Protect, unprotect or expire a branch","description":"Sets `protected` and `expiresAt` (null clears the expiry). A protected branch cannot expire, and the primary branch is always protected and never expires. Needs `branches.manage`, which only keys minted by `headband login` (the CLI device flow) hold; any other key gets 403 `branches_manage_required`.","tags":["Branches"],"security":[{"adminKey":[]}],"x-headband-permissions":["branches.manage"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"project","in":"query","required":false,"description":"A project id. An org key names the project here (or with Headband-Branch's `<project id>/<branch>` form) to address a branch by name.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"protected":{"type":"boolean"},"expiresAt":{"type":["string","null"],"format":"date-time","description":"A future ISO 8601 time with an offset, or null to clear. Omit to leave as is."}}}}}},"responses":{"200":{"description":"The updated branch.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","required":["id","name","projectId","primary","protected","expiresAt","state","parentId","createdAt"],"properties":{"id":{"type":"string","description":"The branch id (`br_` and 12 characters)."},"name":{"type":"string"},"projectId":{"type":"string"},"primary":{"type":"boolean","description":"True for the project's production branch."},"protected":{"type":"boolean","description":"Always true for the primary branch."},"expiresAt":{"type":["string","null"],"format":"date-time","description":"Null for never."},"state":{"type":"string","description":"Such as `ready` or `deleting`."},"parentId":{"type":["string","null"],"description":"Null for a project's first branch."},"createdAt":{"type":"string","format":"date-time"}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_json`, `invalid_request`, `invalid_expires_at`, `invalid_branch_name`, `branch_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Codes: `branches_manage_required`, `branch_not_allowed`, `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `branch_not_found`, `project_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `branch_is_primary`, `branch_protected`, `branch_deleting`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"delete":{"operationId":"deleteBranch","summary":"Delete a branch","description":"Deletes the branch with its indexes, engine instances and keys. Refused for the primary branch, a protected branch, a branch with live child branches, and a branch with synced indexes. A delete that failed part way leaves the branch in `state: deleting`; calling again finishes it. Needs `branches.manage`, which only keys minted by `headband login` (the CLI device flow) hold; any other key gets 403 `branches_manage_required`.","tags":["Branches"],"security":[{"adminKey":[]}],"x-headband-permissions":["branches.manage"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"project","in":"query","required":false,"description":"A project id. An org key names the project here (or with Headband-Branch's `<project id>/<branch>` form) to address a branch by name.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The branch is deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","required":["deleted","id"],"properties":{"deleted":{"type":"boolean","const":true},"id":{"type":"string"}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_branch_name`, `branch_required`, `invalid_request`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Codes: `branches_manage_required`, `branch_not_allowed`, `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `branch_not_found`, `project_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `branch_is_primary`, `branch_protected`, `branch_has_children`, `branch_has_synced_indexes`, or an engine instance's own refusal.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/bulk":{"post":{"operationId":"bulkIndex","summary":"Bulk upsert documents","description":"Upserts documents into one index, split into engine batches of `batchSize`. Answers 202 with one task per batch once every batch is handed to the engine. If the engine rejects some documents (Elasticsearch, Typesense), answers 422 `partial_failure` with up to 100 per-document errors; the other documents were written. Free plan orgs are checked against the stored-data cap before anything is written. Runs for up to 300 seconds.","tags":["Bulk"],"security":[{"adminKey":[]}],"x-headband-permissions":["documents.add"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"batchSize","in":"query","required":false,"description":"Documents per engine batch.","schema":{"type":"integer","minimum":100,"maximum":50000,"default":10000}},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["index","documents"],"properties":{"index":{"type":"string","description":"The logical uid of the index to write to."},"primaryKey":{"type":"string","description":"The primary key field, passed to the engine. Omit to let the engine infer it."},"documents":{"type":"array","minItems":1,"items":{"type":"object","additionalProperties":true},"description":"The documents to upsert. The whole body counts against the 4 MB request limit."}}}}}},"responses":{"202":{"description":"Every batch was accepted by the engine.","content":{"application/json":{"schema":{"type":"object","required":["tasks","totalDocuments","totalBatches","batchSize"],"properties":{"tasks":{"type":"array","items":{"type":"object","properties":{"taskUid":{"type":"integer","description":"The engine's task id for this batch."},"indexUid":{"type":"string","description":"The logical index uid."},"status":{"type":"string"},"batchNumber":{"type":"integer","description":"1-based."},"documentsInBatch":{"type":"integer"}}}},"totalDocuments":{"type":"integer"},"totalBatches":{"type":"integer"},"batchSize":{"type":"integer"}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad input. Codes: `invalid_request` (batchSize out of range, empty documents), `invalid_json`, `index_required`, `invalid_branch_name`, `invalid_index_uid`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"The project is read-only after a plan downgrade: `plan_project_read_only`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"The key may not write here. Codes: `permission_denied` (a search key, or no `documents.add`), `branch_not_allowed`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `index_not_found`, `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `index_ambiguous` (name one branch with Headband-Branch), `index_managed_by_sync`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"Some documents failed: `partial_failure`, with `failed`, `errors` (up to 100) and `retryable: true` plus `Retry-After: 1` when every failure is retryable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Rate limit exceeded (`Retry-After` set), or the Free plan's stored-data cap would be passed: `usage_limit_exceeded`, with `plan`, `current`, `limit` and `upgradeUrl`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/ServiceUnavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}},"/v1/bulk/stream":{"post":{"operationId":"bulkIndexStream","summary":"Bulk upsert documents with streamed progress","description":"The same upsert as POST /v1/bulk, but answers 200 with a Server-Sent Events stream and sends one event per batch as the engine accepts it. Errors found before the stream opens (auth, validation, the stored-data cap, index lookup) answer as normal JSON errors. After that, failures ride on the events: per-document errors (at most 100 across the whole stream) appear in `batch_complete` and `complete`, and an engine failure ends the stream with an `error` event. Batches sent before an error stay written. Runs for up to 300 seconds.","tags":["Bulk"],"security":[{"adminKey":[]}],"x-headband-permissions":["documents.add"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"batchSize","in":"query","required":false,"description":"Documents per engine batch.","schema":{"type":"integer","minimum":100,"maximum":50000,"default":10000}},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["index","documents"],"properties":{"index":{"type":"string","description":"The logical uid of the index to write to."},"primaryKey":{"type":"string","description":"The primary key field, passed to the engine. Omit to let the engine infer it."},"documents":{"type":"array","minItems":1,"items":{"type":"object","additionalProperties":true},"description":"The documents to upsert. The whole body counts against the 4 MB request limit."}}}}}},"responses":{"200":{"description":"A `text/event-stream`. Each event is one `data: <json>` line followed by a blank line. The JSON's `event` field is `batch_complete` (one per batch), then `complete`; or `error`, which ends the stream.","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","title":"batch_complete","properties":{"event":{"const":"batch_complete"},"batchNumber":{"type":"integer","description":"1-based."},"taskUid":{"type":"integer"},"indexUid":{"type":"string","description":"The index uid the engine reported for the task."},"status":{"type":"string"},"documentsInBatch":{"type":"integer"},"failed":{"type":"integer"},"errors":{"type":"array","items":{"type":"object","description":"One document the engine rejected.","properties":{"id":{"type":["string","null"],"description":"The document id when known."},"status":{"type":"integer","description":"The engine's per-document HTTP status."},"type":{"type":"string","description":"A short machine-readable error type."},"reason":{"type":"string","description":"The engine's reason for a 4xx (URLs and addresses redacted, at most 300 characters); a generic message for 429 and 5xx."},"retryable":{"type":"boolean","description":"True for engine status 429 or 5xx."}}}}}},{"type":"object","title":"complete","description":"When any document failed, also carries `error`, `code: partial_failure` and `retryable: true` when every failure is retryable.","properties":{"event":{"const":"complete"},"totalDocuments":{"type":"integer"},"totalBatches":{"type":"integer"},"batchSize":{"type":"integer"},"failed":{"type":"integer"},"errors":{"type":"array","items":{"type":"object","description":"One document the engine rejected.","properties":{"id":{"type":["string","null"],"description":"The document id when known."},"status":{"type":"integer","description":"The engine's per-document HTTP status."},"type":{"type":"string","description":"A short machine-readable error type."},"reason":{"type":"string","description":"The engine's reason for a 4xx (URLs and addresses redacted, at most 300 characters); a generic message for 429 and 5xx."},"retryable":{"type":"boolean","description":"True for engine status 429 or 5xx."}}},"maxItems":100},"error":{"type":"string"},"code":{"type":"string","const":"partial_failure"},"retryable":{"type":"boolean"}}},{"type":"object","title":"error","properties":{"event":{"const":"error"},"error":{"type":"string"},"code":{"type":"string"}}}]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad input. Codes: `invalid_request` (batchSize out of range, empty documents), `invalid_json`, `index_required`, `invalid_branch_name`, `invalid_index_uid`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"The project is read-only after a plan downgrade: `plan_project_read_only`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"The key may not write here. Codes: `permission_denied` (a search key, or no `documents.add`), `branch_not_allowed`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `index_not_found`, `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `index_ambiguous` (name one branch with Headband-Branch), `index_managed_by_sync`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"description":"Rate limit exceeded (`Retry-After` set), or the Free plan's stored-data cap would be passed: `usage_limit_exceeded`, with `plan`, `current`, `limit` and `upgradeUrl`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/ServiceUnavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}},"/v1/connectors":{"get":{"operationId":"listConnectors","summary":"Integration catalog","description":"The public catalog of framework recipes, sync connectors and experiment patterns, each with a status (`live`, `recipe` or `roadmap`). No key needed. Credential and environment variable names are left out; `requiresConfiguration` says whether a connector needs any.","tags":["Connectors"],"security":[],"responses":{"200":{"description":"The catalog.","content":{"application/json":{"schema":{"type":"object","required":["data","generatedAt","note"],"properties":{"data":{"type":"object","properties":{"frameworks":{"type":"array","items":{"type":"object","additionalProperties":true}},"connectors":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["live","recipe","roadmap"]},"requiresConfiguration":{"type":"boolean"}},"additionalProperties":true}},"experiments":{"type":"array","items":{"type":"object","additionalProperties":true}}}},"generatedAt":{"type":"string","format":"date-time"},"note":{"type":"string"}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]}},"/v1/connectors/runs":{"get":{"operationId":"listConnectorRuns","summary":"List connector runs","description":"The project's connector runs, newest first. The project is `projectId`, else the key's own project. An org-scoped key must name one (400 `project_id_required`).","tags":["Connectors"],"security":[{"adminKey":[]}],"x-headband-permissions":["connectors.write"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"projectId","in":"query","required":false,"description":"The project. Required for an org-scoped key.","schema":{"type":"string"}},{"name":"connector","in":"query","required":false,"description":"Only runs of this connector slug.","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"Only runs with this status.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Most runs to return. Out-of-range values are clamped.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"The runs.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"runs":{"type":"array","items":{"type":"object","description":"A connector run record.","properties":{"id":{"type":"string"},"orgId":{"type":"string"},"projectId":{"type":"string"},"connectorSlug":{"type":"string"},"indexUid":{"type":["string","null"]},"status":{"type":"string","enum":["started","succeeded","failed","skipped","dead_letter"]},"mode":{"type":"string","enum":["self_hosted","managed"]},"source":{"type":["string","null"]},"command":{"type":["string","null"]},"externalRunId":{"type":["string","null"]},"count":{"type":["integer","null"]},"retryCount":{"type":"integer"},"error":{"type":["string","null"]},"details":{"type":["object","null"],"additionalProperties":true},"startedAt":{"type":"string","format":"date-time"},"finishedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Code: `project_id_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Code: `project_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"post":{"operationId":"recordConnectorRun","summary":"Record a connector run","description":"Records one connector run, as `headband sync worker` and the connector scripts do. With `externalRunId`, a run with the same project, connector and external id is updated in place (200); otherwise a new run is created (201). `finishedAt` defaults to now unless the status is `started`. `details` is stored with secret-looking values redacted and is cut at about 8 KB. The project is `projectId`, else the key's own project. An org-scoped key must name one (400 `project_id_required`).","tags":["Connectors"],"security":[{"adminKey":[]}],"x-headband-permissions":["connectors.write"],"x-headband-key-types":["admin"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["connectorSlug","status"],"properties":{"connectorSlug":{"type":"string","description":"A connector recipe slug from GET /v1/connectors, or `orm-sync`."},"status":{"type":"string","enum":["started","succeeded","failed","skipped","dead_letter"]},"mode":{"type":"string","enum":["self_hosted","managed"],"default":"self_hosted"},"projectId":{"type":"string","description":"Required for an org-scoped key."},"indexUid":{"type":["string","null"]},"source":{"type":["string","null"],"maxLength":512},"command":{"type":["string","null"],"maxLength":512},"externalRunId":{"type":["string","null"],"maxLength":512,"description":"Your own run id. Makes the call an upsert."},"count":{"type":["number","null"],"minimum":0,"description":"Rounded down."},"retryCount":{"type":["number","null"],"minimum":0,"default":0},"error":{"type":["string","null"],"maxLength":2000},"details":{"type":["object","null"],"additionalProperties":true},"startedAt":{"type":"string","format":"date-time","description":"Defaults to now."},"finishedAt":{"type":["string","null"],"format":"date-time"}},"description":"Strings are trimmed and cut to their maximum length rather than refused."}}}},"responses":{"200":{"description":"An existing run (same `externalRunId`) was updated.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"run":{"type":"object","description":"A connector run record.","properties":{"id":{"type":"string"},"orgId":{"type":"string"},"projectId":{"type":"string"},"connectorSlug":{"type":"string"},"indexUid":{"type":["string","null"]},"status":{"type":"string","enum":["started","succeeded","failed","skipped","dead_letter"]},"mode":{"type":"string","enum":["self_hosted","managed"]},"source":{"type":["string","null"]},"command":{"type":["string","null"]},"externalRunId":{"type":["string","null"]},"count":{"type":["integer","null"]},"retryCount":{"type":"integer"},"error":{"type":["string","null"]},"details":{"type":["object","null"],"additionalProperties":true},"startedAt":{"type":"string","format":"date-time"},"finishedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"201":{"description":"A new run was recorded.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"run":{"type":"object","description":"A connector run record.","properties":{"id":{"type":"string"},"orgId":{"type":"string"},"projectId":{"type":"string"},"connectorSlug":{"type":"string"},"indexUid":{"type":["string","null"]},"status":{"type":"string","enum":["started","succeeded","failed","skipped","dead_letter"]},"mode":{"type":"string","enum":["self_hosted","managed"]},"source":{"type":["string","null"]},"command":{"type":["string","null"]},"externalRunId":{"type":["string","null"]},"count":{"type":["integer","null"]},"retryCount":{"type":"integer"},"error":{"type":["string","null"]},"details":{"type":["object","null"],"additionalProperties":true},"startedAt":{"type":"string","format":"date-time"},"finishedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_json`, `invalid_connector_run`, `project_id_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Code: `project_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]}},"/v1/documents":{"get":{"operationId":"listDocuments","summary":"Browse documents","description":"Raw documents of an index, by offset or by cursor. Offset paging stops at 1000 documents on Visor and 10,000 on Elasticsearch. With `cursor` (empty on the first request) the walk covers the whole index on any engine: each page answers `nextCursor`, null on the last. Admin keys holding `documents.read` only.","tags":["Documents"],"security":[{"adminKey":[]}],"x-headband-permissions":["documents.read"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"index","in":"query","required":true,"description":"The uid of the index.","schema":{"type":"string","maxLength":64,"pattern":"^[A-Za-z0-9_-]+$"}},{"name":"limit","in":"query","required":false,"description":"Documents per page.","schema":{"type":"integer","default":20}},{"name":"offset","in":"query","required":false,"description":"Documents to skip. Not with `cursor`.","schema":{"type":"integer","default":0}},{"name":"fields","in":"query","required":false,"description":"Comma-separated fields to return, such as `id,title`.","schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"description":"Walk by cursor: empty for the first page, then the previous page's `nextCursor`. Opaque.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"A page of documents.","content":{"application/json":{"schema":{"type":"object","required":["results","limit","total"],"properties":{"results":{"type":"array","items":{"type":"object","additionalProperties":true}},"offset":{"type":"integer","description":"Offset paging only."},"limit":{"type":"integer","description":"The page size the engine used."},"total":{"type":"integer","description":"Documents in the index, as the engine counts them."},"nextCursor":{"type":["string","null"],"description":"Cursor paging only: the next page's cursor, null on the last page."}},"additionalProperties":true}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `index_required`, `invalid_cursor` (both `cursor` and `offset`), `invalid_index_uid`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Codes: `permission_denied` (search keys, or no `documents.read`, with `requiredPermission`), `all_indexes_key_required`, `branch_not_allowed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"`index_not_found`, or `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"`index_ambiguous`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"post":{"operationId":"addDocuments","summary":"Add or replace documents","description":"Upserts a JSON array of documents into the index and answers 202 with the task. The request body is capped at 4 MB. Counts against the plan's writes a second and, on the Free plan, its storage cap. Elasticsearch and Typesense write synchronously and report per-document failures as a 422. An index Sync manages is refused.","tags":["Documents"],"security":[{"adminKey":[]}],"x-headband-permissions":["documents.add"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"index","in":"query","required":true,"description":"The uid of the index.","schema":{"type":"string","maxLength":64,"pattern":"^[A-Za-z0-9_-]+$"}},{"name":"primaryKey","in":"query","required":false,"description":"The primary key field, when the engine cannot infer it.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"description":"The documents.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true}}}}},"responses":{"202":{"description":"Write accepted.","content":{"application/json":{"schema":{"type":"object","required":["taskUid","indexUid","status"],"properties":{"taskUid":{"type":"integer","description":"The engine's task id. Poll GET /v1/tasks/{taskId} with `?index=`."},"indexUid":{"type":"string"},"status":{"type":"string","description":"The task status, such as `enqueued` or `succeeded`."},"enqueuedAt":{"type":"string","format":"date-time"}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `index_required`, `invalid_json`, `invalid_request` (the body is not an array), `invalid_index_uid`, `invalid_branch_name`, and the engine's own validation codes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"The project is over the plan's project limit and read-only (`plan_project_read_only`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`index_not_found`, or `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `index_ambiguous`, `index_managed_by_sync`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"Some documents failed (`partial_failure`). The body adds `failed` (a count), `errors` (up to 100 of `{ id, status, type, reason, retryable }`) and `retryable: true` when every failure may succeed on a retry, with `Retry-After: 1`. Elasticsearch and Typesense report per-document failures this way.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"Codes: `rate_limited` (the key's or the plan's writes a second, with `Retry-After`), `usage_limit_exceeded` (the Free plan's storage cap, with `plan`, `current`, `limit`, `upgradeUrl`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"description":"Codes include `index_migrating` (with `Retry-After`), `instance_unavailable`, `auth_lookup_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}}},"delete":{"operationId":"deleteDocuments","summary":"Delete documents","description":"Deletes documents by id, or by filter, and answers 202 with the task. `ids` wins when both are sent. Counts against the plan's writes a second. An index Sync manages is refused.","tags":["Documents"],"security":[{"adminKey":[]}],"x-headband-permissions":["documents.delete"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"index","in":"query","required":true,"description":"The uid of the index.","schema":{"type":"string","maxLength":64,"pattern":"^[A-Za-z0-9_-]+$"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Either `ids` or `filter`.","properties":{"ids":{"type":"array","items":{"type":["string","number"]},"description":"Document ids."},"filter":{"description":"A filter expression, or a list of them (one nested level for an OR group).","oneOf":[{"type":"string","minLength":1},{"type":"array","items":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]}}]}}}}}},"responses":{"202":{"description":"Delete accepted.","content":{"application/json":{"schema":{"type":"object","required":["taskUid","indexUid","status"],"properties":{"taskUid":{"type":"integer","description":"The engine's task id. Poll GET /v1/tasks/{taskId} with `?index=`."},"indexUid":{"type":"string"},"status":{"type":"string","description":"The task status, such as `enqueued` or `succeeded`."}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `index_required`, `invalid_json`, `invalid_request` (neither `ids` nor a valid `filter`), `invalid_index_uid`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"The project is over the plan's project limit and read-only (`plan_project_read_only`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`index_not_found`, or `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `index_ambiguous`, `index_managed_by_sync`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"Some documents failed (`partial_failure`). The body adds `failed` (a count), `errors` (up to 100 of `{ id, status, type, reason, retryable }`) and `retryable: true` when every failure may succeed on a retry, with `Retry-After: 1`. Elasticsearch and Typesense report per-document failures this way.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"description":"`rate_limited`: the key's or the plan's writes a second. Wait for `Retry-After` seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"description":"Codes include `index_migrating` (with `Retry-After`), `instance_unavailable`, `auth_lookup_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}}}},"/v1/documents/{docId}":{"get":{"operationId":"getDocument","summary":"Get a document","description":"One raw document by id, as the engine stores it. Admin keys holding `documents.read` only.","tags":["Documents"],"security":[{"adminKey":[]}],"x-headband-permissions":["documents.read"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"docId","in":"path","required":true,"description":"The document id: 1 to 511 letters, numbers, hyphens and underscores.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,511}$"}},{"name":"index","in":"query","required":true,"description":"The uid of the index.","schema":{"type":"string","maxLength":64,"pattern":"^[A-Za-z0-9_-]+$"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The document.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `index_required`, `invalid_document_id`, `invalid_index_uid`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Codes: `permission_denied` (search keys, or no `documents.read`, with `requiredPermission`), `all_indexes_key_required`, `branch_not_allowed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"The document or the index (`index_not_found`) does not exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"`index_ambiguous`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/experiments/assign":{"post":{"operationId":"assignExperimentVariant","summary":"Assign an experiment variant","description":"Assigns a subject to a variant of an experiment, deterministically: the same subject gets the same answer. Reads the project's latest config for `experimentKey`. Org-wide keys must name the project with `projectId` or an `indexUid` in it. `assigned` is false when the experiment is not running, the subject is outside the rollout, or no variant has weight.","tags":["Experiments"],"security":[{"adminKey":[]},{"searchKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin","search"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["experimentKey","subjectId"],"properties":{"experimentKey":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$"},"subjectId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$","description":"A stable id for the user or visitor."},"sessionId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$","description":"Defaults to `subjectId`."},"projectId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$"},"indexUid":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$"},"index":{"type":"string","description":"Alias of `indexUid`."}}}}}},"responses":{"200":{"description":"The assignment.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"assigned":{"type":"boolean"},"reason":{"type":"string","enum":["not_runnable","outside_rollout","no_weight"]},"experimentKey":{"type":"string"},"variant":{"type":"string"},"indexUid":{"type":"string","description":"The index the variant searches."},"sessionId":{"type":"string"},"config":{"type":"object","properties":{"status":{"type":"string","enum":["draft","active","paused","archived"]},"trafficPercent":{"type":"integer"},"killSwitch":{"type":"boolean"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad request. Codes: `invalid_json`, `invalid_experiment_key`, `invalid_subject_id`, `invalid_session_id`, `invalid_project_id`, `invalid_index_uid`, `project_required`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (with `requiredPermission`), `branch_not_allowed` (a branch key naming another branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Not found. Codes: `experiment_config_not_found`, `project_not_found`, `index_not_found`, `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The uid names an index on more than one branch in scope (`index_ambiguous`). Pick one with Headband-Branch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/experiments/config":{"get":{"operationId":"getExperimentConfig","summary":"Read experiment configs","description":"Returns the latest config of each experiment in a project, sorted by `experimentKey`. Project keys read their own project; org-wide keys must pass `projectId` or `indexUid`. Reads at most the 200 newest config rows.","tags":["Experiments"],"security":[{"adminKey":[]},{"searchKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin","search"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"},{"name":"projectId","in":"query","required":false,"description":"The project. Ignored for project keys.","schema":{"type":"string"}},{"name":"indexUid","in":"query","required":false,"description":"An index in the project, when `projectId` is not given. `index` is accepted too.","schema":{"type":"string"}},{"name":"experimentKey","in":"query","required":false,"description":"Only this experiment.","schema":{"type":"string"}}],"responses":{"200":{"description":"The experiment configs.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"experiments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"experimentKey":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["draft","active","paused","archived"]},"trafficPercent":{"type":"integer","minimum":0,"maximum":100},"killSwitch":{"type":"boolean"},"primaryMetric":{"type":"string"},"guardrailMetric":{"type":["string","null"]},"assignmentSalt":{"type":"string"},"variants":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"indexUid":{"type":"string"},"weight":{"type":"integer"}}}},"updatedAt":{"type":"string","format":"date-time"}}}}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad request. Codes: `project_required`, `invalid_index_uid`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (with `requiredPermission`), `branch_not_allowed` (a branch key naming another branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Not found. Codes: `project_not_found`, `index_not_found`, `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The uid names an index on more than one branch in scope (`index_ambiguous`). Pick one with Headband-Branch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/experiments/events":{"post":{"operationId":"sendExperimentEvent","summary":"Record an experiment event","description":"Records one experiment event (a view, click, conversion or custom event) for a variant, on the project of the named index.","tags":["Experiments"],"security":[{"adminKey":[]},{"searchKey":[]}],"x-headband-permissions":["analytics.write"],"x-headband-key-types":["admin","search"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventType","experimentKey","variant","indexUid"],"properties":{"eventType":{"type":"string","enum":["experiment_view","search_result_click","conversion","custom"]},"experimentKey":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$"},"variant":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$"},"indexUid":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$"},"index":{"type":"string","description":"Alias of `indexUid`."},"sessionId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$"},"query":{"type":"string","maxLength":500},"filters":{"description":"The search's filters, stored with the event."},"conversionValue":{"type":"number","minimum":0,"description":"Rounded to a whole number."},"metadata":{"type":"object","additionalProperties":true,"description":"At most 4096 bytes as JSON; larger is a 413 `metadata_too_large`."}}}}}},"responses":{"202":{"description":"The event was recorded.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"accepted":{"type":"boolean","const":true},"eventType":{"type":"string"},"experimentKey":{"type":"string"},"variant":{"type":"string"},"indexUid":{"type":"string"}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad request. Codes: `invalid_json`, `invalid_experiment_event`, `project_required`, `invalid_index_uid`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (with `requiredPermission`), `branch_not_allowed` (a branch key naming another branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"The index is not in the request's scope (`index_not_found`), or the Headband-Branch names no live branch (`branch_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The uid names an index on more than one branch in scope (`index_ambiguous`). Pick one with Headband-Branch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/health":{"get":{"operationId":"getHealth","summary":"Health check","description":"Probes the database and, when one is configured, the platform search engine, each with a 2 second timeout. Answers 503 with the same body when any probe is down. Customer engines are not probed.","tags":["System"],"security":[],"responses":{"200":{"description":"Every probe is ok.","content":{"application/json":{"schema":{"type":"object","required":["status","checks"],"properties":{"status":{"type":"string","enum":["available","degraded"]},"checks":{"type":"object","required":["database"],"properties":{"database":{"type":"string","enum":["ok","down"]},"searchEngine":{"type":"string","enum":["ok","down"],"description":"Omitted when no platform engine is configured."}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"A probe is down.","content":{"application/json":{"schema":{"type":"object","required":["status","checks"],"properties":{"status":{"type":"string","enum":["available","degraded"]},"checks":{"type":"object","required":["database"],"properties":{"database":{"type":"string","enum":["ok","down"]},"searchEngine":{"type":"string","enum":["ok","down"],"description":"Omitted when no platform engine is configured."}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]}},"/v1/hybrid-search":{"post":{"operationId":"hybridSearch","summary":"Keyword and vector search","description":"Hybrid search on one index through each engine's native hybrid API. The caller computes `vector`; Headband does not embed. Without a vector, or when the index has no embedder or vector field, the engine runs keyword only and the answer says `fallback: true`. Visor always runs keyword only. Its own rate-limit bucket: 300 requests a minute per key. Counts toward the monthly searches, but not the plan's searches a second.","tags":["Search"],"security":[{"adminKey":[]},{"searchKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin","search"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["index"],"properties":{"index":{"type":"string","minLength":1,"description":"The index uid."},"q":{"type":"string"},"vector":{"type":"array","items":{"type":"number"}},"limit":{"type":"integer"},"filter":{"type":"string"},"semanticRatio":{"type":"number","minimum":0,"maximum":1}}}}}},"responses":{"200":{"description":"Hits with per-hit scores.","content":{"application/json":{"schema":{"type":"object","required":["hits","query","processingTimeMs"],"properties":{"hits":{"type":"array","items":{"type":"object","properties":{"_scoring":{"type":"object","description":"Engine-local scores. Not comparable across engines.","properties":{"keyword":{"type":"number"},"semantic":{"type":"number"},"combined":{"type":"number"}}}},"additionalProperties":true}},"query":{"type":"string"},"processingTimeMs":{"type":"number"},"estimatedTotalHits":{"type":"integer"},"totalHits":{"type":"integer"},"totalHitsExact":{"type":"boolean"},"fallback":{"type":"boolean","description":"True when the engine ran keyword only."}},"additionalProperties":true}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad request. Codes include `invalid_json`, `index_required`, `invalid_request` (a bad `vector` or `semanticRatio`), `invalid_index_uid`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (with `requiredPermission`), `branch_not_allowed` (a branch key naming another branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"The index is not in the request's scope (`index_not_found`), or the Headband-Branch names no live branch (`branch_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The uid names an index on more than one branch in scope (`index_ambiguous`). Pick one with Headband-Branch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"description":"Rate limit or the plan's monthly search cap. Codes: `usage_limit_exceeded` (monthly cap, with `plan`, `current`, `limit`, `resetsAt`, `upgradeUrl`; `Retry-After` counts to the reset). The per-key and per-IP request limits answer with no code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"description":"The engine or an auth lookup is unavailable for now. Codes include `auth_lookup_failed`, `engine_not_configured`, `instance_waking`, `index_migrating` (with `Retry-After`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}},"/v1/indexes":{"get":{"operationId":"listIndexes","summary":"List indexes","description":"Lists the indexes in the request's branch scope that the key reaches. A branch key sees its own branch; an org key sees the primary branches, or the branch Headband-Branch names. When the scope spans projects each entry carries `projectId`. A project key of its primary branch also lists the project's default engine and registers any index found there that Headband did not know yet. A key limited to chosen engines sees only indexes on those engines.","tags":["Indexes"],"security":[{"adminKey":[]},{"searchKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin","search"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The indexes.","content":{"application/json":{"schema":{"type":"object","required":["results"],"properties":{"results":{"type":"array","items":{"type":"object","required":["uid","engine"],"properties":{"uid":{"type":"string","description":"The index uid."},"primaryKey":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"engine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"]},"instanceId":{"type":"string","description":"The dedicated engine the index lives on. Only for instance-hosted indexes."},"engineIndexUid":{"type":"string","description":"The index's name on the instance endpoint, for the engine's own client. Only for instance-hosted indexes."},"projectId":{"type":["string","null"],"description":"Only when the scope spans projects (an org key)."}},"additionalProperties":true}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `invalid_branch_name` (Headband-Branch).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"post":{"operationId":"createIndex","summary":"Create an index","description":"Creates an index and answers 202 with the engine's task. Needs a project-scoped admin key, or an org-scoped CLI key holding `schema.manage`. The branch follows the key: a branch key's own branch; for an org key the Headband-Branch header and `projectId`, else the Default project's `main`. The engine defaults to the project's engine. With `instanceId` the index goes on that dedicated engine of the org, and takes that engine's type unless `engine` names one (which must match). Without it, platform defaults may place the index on the org's default instance for the engine. Your own engine (BYOE) and the shared engines serve only a project's primary branch.","tags":["Indexes"],"security":[{"adminKey":[]}],"x-headband-permissions":["indexes.create"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["uid"],"properties":{"uid":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_-]+$"},"engine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"],"description":"Defaults to the instance's engine, else the project's engine."},"primaryKey":{"type":"string"},"instanceId":{"type":["string","null"],"minLength":1,"description":"A dedicated engine of the org. An org without dedicated engines gets 404 `instance_not_found` for any value."},"projectId":{"type":["string","null"],"description":"Org keys: the project the index goes in. Defaults to the Default project."}}}}}},"responses":{"202":{"description":"Creation accepted.","content":{"application/json":{"schema":{"type":"object","required":["taskUid","indexUid","engine","status","projectId","branch"],"properties":{"taskUid":{"type":"integer"},"indexUid":{"type":"string"},"engine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"]},"status":{"type":"string"},"projectId":{"type":"string","description":"The project the index landed in."},"branch":{"type":"string","description":"The name of the branch the index landed on."},"instanceId":{"type":"string","description":"Only when the index was placed on a dedicated engine."},"engineIndexUid":{"type":"string","description":"The index's name on the instance endpoint. Only with `instanceId`."}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_json`, `invalid_index_uid`, `invalid_request` (a non-string `projectId`), `invalid_engine`, `invalid_instance_id`, `instance_engine_mismatch`, `shared_engines_closed`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"A plan limit. Codes include `plan_project_limit` (making the Default project) and `plan_engine_limit`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Codes: `permission_denied`, `project_scope_required` (an org key without `schema.manage`), `org_wide_key_required`, `all_indexes_key_required`, `branch_not_allowed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `project_not_found`, `branch_not_found`, `instance_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes include `index_already_exists` (on this branch), `index_uid_taken` (another project's primary branch has the uid), `branch_deleting`, `byoe_not_branchable`, `instance_not_in_branch`, `shared_engines_closed`, `instance_deleting`, `instance_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"description":"Codes include `instance_unavailable` (with `Retry-After`), `engine_not_configured`, `no_capacity`, `auth_lookup_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}}}},"/v1/indexes/{uid}":{"get":{"operationId":"getIndex","summary":"Get an index","description":"The index's details from its engine, with its logical uid and engine. An instance-hosted index also carries `instanceId` and `engineIndexUid`.","tags":["Indexes"],"security":[{"adminKey":[]},{"searchKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin","search"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"uid","in":"path","required":true,"description":"The index uid: 1 to 64 letters, numbers, hyphens and underscores.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The index.","content":{"application/json":{"schema":{"type":"object","required":["uid","engine"],"properties":{"uid":{"type":"string","description":"The index uid."},"primaryKey":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"engine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"]},"instanceId":{"type":"string","description":"The dedicated engine the index lives on. Only for instance-hosted indexes."},"engineIndexUid":{"type":"string","description":"The index's name on the instance endpoint, for the engine's own client. Only for instance-hosted indexes."}},"additionalProperties":true}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `invalid_branch_name` (Headband-Branch), `invalid_index_uid`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`index_not_found`, or `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"`index_ambiguous`: several branches in scope hold the uid. Name one with Headband-Branch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"delete":{"operationId":"deleteIndex","summary":"Delete an index","description":"Deletes the index on its engine and its Headband record, and answers 202 with the task. After a reindex the alias target's record goes too. A project held read-only by the plan may still delete. An index Sync manages is refused.","tags":["Indexes"],"security":[{"adminKey":[]}],"x-headband-permissions":["indexes.delete"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"uid","in":"path","required":true,"description":"The index uid: 1 to 64 letters, numbers, hyphens and underscores.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"202":{"description":"Deletion accepted.","content":{"application/json":{"schema":{"type":"object","required":["taskUid","indexUid","status"],"properties":{"taskUid":{"type":"integer","description":"The engine's task id. Poll GET /v1/tasks/{taskId} with `?index=`."},"indexUid":{"type":"string"},"status":{"type":"string","description":"The task status, such as `enqueued` or `succeeded`."},"type":{"type":"string"},"enqueuedAt":{"type":"string","format":"date-time"}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `invalid_branch_name` (Headband-Branch), `invalid_index_uid`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`index_not_found`, or `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `index_ambiguous`, `index_managed_by_sync`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"description":"Codes include `index_migrating` (the index is moving engines, with `Retry-After`), `instance_unavailable`, `auth_lookup_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}}}},"/v1/indexes/{uid}/click-feedback":{"parameters":[{"name":"uid","in":"path","required":true,"description":"The index uid.","schema":{"type":"string"}}],"get":{"operationId":"getClickFeedbackSettings","summary":"Read click feedback settings","description":"Whether click feedback ranks this index, its strength, and the settings kept for each engine the index has used. Admin keys only. Only for organizations with click feedback.","tags":["Analytics"],"security":[{"adminKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin"],"x-headband-availability":"Organizations with click feedback enabled.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The settings.","content":{"application/json":{"schema":{"type":"object","required":["index","engine","enabled","strength","window","support","engines"],"properties":{"index":{"type":"string"},"engine":{"type":"string","description":"The index's current engine."},"enabled":{"type":"boolean","description":"Whether click feedback ranks this index on its current engine."},"strength":{"type":"number","minimum":0,"maximum":1,"description":"Default 0.5."},"window":{"type":"integer","description":"How many top results are re-ranked (50)."},"support":{"type":["object","null"],"properties":{"ranking":{"type":"boolean"},"note":{"type":"string"}}},"engines":{"type":"object","description":"Every engine this index has settings for.","additionalProperties":{"type":"object","properties":{"enabled":{"type":"boolean"},"strength":{"type":"number"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad request. Codes: `invalid_index_uid`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (search keys, with `requiredKeyType`), `branch_not_allowed`, `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Click feedback is not enabled for this organization (`click_feedback_not_enabled`), the index is not in scope (`index_not_found`), or the branch is unknown (`branch_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The uid names an index on more than one branch in scope (`index_ambiguous`). Pick one with Headband-Branch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Click feedback is not set up on this deployment yet (`click_feedback_unavailable`), or the key lookup failed (`auth_lookup_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}}},"patch":{"operationId":"updateClickFeedbackSettings","summary":"Update click feedback settings","description":"Turns click feedback ranking on or off and sets its strength. Without `engine` it sets the index's current engine. Answers with the settings after the change.","tags":["Analytics"],"security":[{"adminKey":[]}],"x-headband-permissions":["settings.update"],"x-headband-key-types":["admin"],"x-headband-availability":"Organizations with click feedback enabled.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Send `enabled`, `strength` or both. Unknown fields are refused.","properties":{"enabled":{"type":"boolean"},"strength":{"type":"number","minimum":0,"maximum":1},"engine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"The settings after the change.","content":{"application/json":{"schema":{"type":"object","required":["index","engine","enabled","strength","window","support","engines"],"properties":{"index":{"type":"string"},"engine":{"type":"string","description":"The index's current engine."},"enabled":{"type":"boolean","description":"Whether click feedback ranks this index on its current engine."},"strength":{"type":"number","minimum":0,"maximum":1,"description":"Default 0.5."},"window":{"type":"integer","description":"How many top results are re-ranked (50)."},"support":{"type":["object","null"],"properties":{"ranking":{"type":"boolean"},"note":{"type":"string"}}},"engines":{"type":"object","description":"Every engine this index has settings for.","additionalProperties":{"type":"object","properties":{"enabled":{"type":"boolean"},"strength":{"type":"number"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad request. Codes: `invalid_json`, `invalid_body`, `invalid_enabled`, `invalid_strength`, `invalid_engine`, `invalid_index_uid`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (with `requiredPermission`), `branch_not_allowed` (a branch key naming another branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Click feedback is not enabled for this organization (`click_feedback_not_enabled`), the index is not in scope (`index_not_found`), or the branch is unknown (`branch_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The uid names an index on more than one branch in scope (`index_ambiguous`). Pick one with Headband-Branch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Click feedback is not set up on this deployment yet (`click_feedback_unavailable`), or the key lookup failed (`auth_lookup_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}}},"delete":{"operationId":"deleteClickFeedbackData","summary":"Delete click feedback data","description":"Deletes every click, search record and setting this index has. Ranking by clicks is off afterwards.","tags":["Analytics"],"security":[{"adminKey":[]}],"x-headband-permissions":["settings.update"],"x-headband-key-types":["admin"],"x-headband-availability":"Organizations with click feedback enabled.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The data was deleted.","content":{"application/json":{"schema":{"type":"object","required":["index","deleted"],"properties":{"index":{"type":"string"},"deleted":{"type":"boolean","const":true}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad request. Codes: `invalid_index_uid`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (with `requiredPermission`), `branch_not_allowed` (a branch key naming another branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Click feedback is not enabled for this organization (`click_feedback_not_enabled`), the index is not in scope (`index_not_found`), or the branch is unknown (`branch_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The uid names an index on more than one branch in scope (`index_ambiguous`). Pick one with Headband-Branch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Click feedback is not set up on this deployment yet (`click_feedback_unavailable`), or the key lookup failed (`auth_lookup_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}}}},"/v1/indexes/{uid}/engine":{"get":{"operationId":"getIndexEngine","summary":"Get an index's engine and latest switch","description":"The index's engine, its dedicated engine if any, and its latest engine switch (or null). Poll it while a switch runs: `switch.status` goes `enqueued`, `processing`, then `succeeded` or `failed`.","tags":["Indexes"],"security":[{"adminKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"uid","in":"path","required":true,"description":"The index uid: 1 to 64 letters, numbers, hyphens and underscores.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The engine and latest switch.","content":{"application/json":{"schema":{"type":"object","required":["indexUid","engine","instanceId","switch"],"properties":{"indexUid":{"type":"string"},"engine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"]},"instanceId":{"type":["string","null"]},"switch":{"oneOf":[{"type":"object","required":["switchId","type","indexUid","status","phase","fromEngine","toEngine"],"properties":{"switchId":{"type":"string","description":"`esw_...`."},"type":{"type":"string","const":"engineSwitch"},"indexUid":{"type":"string"},"status":{"type":"string","enum":["enqueued","processing","succeeded","failed"]},"phase":{"type":"string","enum":["queued","copying","draining","catching_up","cleaning","succeeded","failed"]},"fromEngine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"]},"toEngine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"]},"instanceId":{"type":["string","null"],"description":"The dedicated engine the index moves to."},"writesHeld":{"type":"boolean","description":"True while writes wait for the final catch-up."},"documents":{"type":"object","properties":{"source":{"type":"integer"},"copied":{"type":"integer"},"verified":{"type":["integer","null"]}}},"settings":{"type":"object","description":"How the index's settings move to the new engine.","properties":{"carried":{"type":"array","items":{"type":"string"},"description":"Settings the new engine applies."},"drops":{"type":"array","description":"Settings the new engine will not apply.","items":{"type":"object","properties":{"setting":{"type":"string"},"reason":{"type":"string"},"kept":{"type":"boolean","description":"Stored on the new index all the same."}}}},"added":{"type":"array","description":"Settings the switch adds that the source did not have.","items":{"type":"object","properties":{"setting":{"type":"string"},"value":{},"reason":{"type":"string"}}}}}},"notes":{"type":"array","items":{"type":"string"}},"retrying":{"type":["string","null"],"description":"The last error while a step retries."},"error":{"type":["object","null"],"properties":{"code":{"type":["string","null"]},"message":{"type":"string"}}},"enqueuedAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"flippedAt":{"type":["string","null"],"format":"date-time"},"finishedAt":{"type":["string","null"],"format":"date-time"}}},{"type":"null"}]}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `invalid_branch_name` (Headband-Branch).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Codes: `permission_denied` (search keys, with `requiredKeyType: admin`), `org_wide_key_required`, `all_indexes_key_required`, `branch_not_allowed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"`index_not_found`, or `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"post":{"operationId":"switchIndexEngine","summary":"Move an index to another engine","description":"Copies the index into a new index on `engine`, placed as a new index of that engine on the same branch would be (or on `instanceId`), holds writes for the final catch-up, verifies counts and ids, then flips the index to it. The uid, keys and URLs stay. Answers 202 with the switch; poll GET. A setting the new engine cannot apply is a 409 `settings_not_portable` until the body sends `acceptDrops: true`. `dryRun: true` answers the plan with 200 and changes nothing. Indexes on your own engine (BYOE), indexes Sync manages and keys limited to chosen engines are refused.","tags":["Indexes"],"security":[{"adminKey":[]}],"x-headband-permissions":["indexes.create","indexes.delete"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"uid","in":"path","required":true,"description":"The index uid: 1 to 64 letters, numbers, hyphens and underscores.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["engine"],"properties":{"engine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"],"description":"The engine to move to."},"instanceId":{"type":["string","null"],"minLength":1,"description":"A dedicated engine of the branch that runs `engine`."},"acceptDrops":{"type":"boolean","default":false,"description":"Switch even though some settings will not carry over."},"declareKey":{"type":"boolean","default":false,"description":"Declare the primary key while the switch reads the index, when the source cannot be read without it (Visor)."},"dryRun":{"type":"boolean","default":false,"description":"Answer the plan and change nothing."}}}}}},"responses":{"200":{"description":"The dry-run plan.","content":{"application/json":{"schema":{"type":"object","required":["dryRun","indexUid","fromEngine","toEngine"],"properties":{"dryRun":{"type":"boolean","const":true},"indexUid":{"type":"string"},"fromEngine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"]},"toEngine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"]},"documents":{"type":"integer","description":"Documents in the source index."},"target":{"type":"object","properties":{"instanceId":{"type":["string","null"]},"willCreate":{"type":"boolean","description":"The branch's default instance of the engine would be created."}}},"settings":{"type":"object","description":"How the index's settings move to the new engine.","properties":{"carried":{"type":"array","items":{"type":"string"},"description":"Settings the new engine applies."},"drops":{"type":"array","description":"Settings the new engine will not apply.","items":{"type":"object","properties":{"setting":{"type":"string"},"reason":{"type":"string"},"kept":{"type":"boolean","description":"Stored on the new index all the same."}}}},"added":{"type":"array","description":"Settings the switch adds that the source did not have.","items":{"type":"object","properties":{"setting":{"type":"string"},"value":{},"reason":{"type":"string"}}}}}},"notes":{"type":"array","items":{"type":"string"}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"202":{"description":"The switch, enqueued.","content":{"application/json":{"schema":{"type":"object","required":["switchId","type","indexUid","status","phase","fromEngine","toEngine"],"properties":{"switchId":{"type":"string","description":"`esw_...`."},"type":{"type":"string","const":"engineSwitch"},"indexUid":{"type":"string"},"status":{"type":"string","enum":["enqueued","processing","succeeded","failed"]},"phase":{"type":"string","enum":["queued","copying","draining","catching_up","cleaning","succeeded","failed"]},"fromEngine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"]},"toEngine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"]},"instanceId":{"type":["string","null"],"description":"The dedicated engine the index moves to."},"writesHeld":{"type":"boolean","description":"True while writes wait for the final catch-up."},"documents":{"type":"object","properties":{"source":{"type":"integer"},"copied":{"type":"integer"},"verified":{"type":["integer","null"]}}},"settings":{"type":"object","description":"How the index's settings move to the new engine.","properties":{"carried":{"type":"array","items":{"type":"string"},"description":"Settings the new engine applies."},"drops":{"type":"array","description":"Settings the new engine will not apply.","items":{"type":"object","properties":{"setting":{"type":"string"},"reason":{"type":"string"},"kept":{"type":"boolean","description":"Stored on the new index all the same."}}}},"added":{"type":"array","description":"Settings the switch adds that the source did not have.","items":{"type":"object","properties":{"setting":{"type":"string"},"value":{},"reason":{"type":"string"}}}}}},"notes":{"type":"array","items":{"type":"string"}},"retrying":{"type":["string","null"],"description":"The last error while a step retries."},"error":{"type":["object","null"],"properties":{"code":{"type":["string","null"]},"message":{"type":"string"}}},"enqueuedAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"flippedAt":{"type":["string","null"],"format":"date-time"},"finishedAt":{"type":["string","null"],"format":"date-time"}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_json`, `invalid_engine`, `invalid_instance_id`, `invalid_request` (a non-boolean flag), `byoe_index`, `same_engine`, `instance_engine_mismatch`, `no_default_instance`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"A plan limit, such as `plan_engine_limit`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Codes: `permission_denied`, `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`, `branch_not_allowed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `index_not_found`, `instance_not_found`, `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `settings_not_portable` (with `settings` and `notes`), `engine_switch_in_progress` (with `switchId`), `migration_in_progress`, `alias_target_mismatch`, `instance_not_in_branch`, `target_name_taken`, `index_managed_by_sync`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"The target engine is unavailable or not configured, or `auth_lookup_failed`. Some carry `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}}}},"/v1/indexes/{uid}/settings":{"get":{"operationId":"getIndexSettings","summary":"Get index settings","description":"The index's settings as its engine reports them, in Headband's settings vocabulary. Admin keys only.","tags":["Indexes"],"security":[{"adminKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"uid","in":"path","required":true,"description":"The index uid: 1 to 64 letters, numbers, hyphens and underscores.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The settings.","content":{"application/json":{"schema":{"type":"object","description":"Headband's settings vocabulary (Meilisearch's). Engines apply different subsets: see docs/engine-capability-matrix.md.","properties":{"searchableAttributes":{"type":"array","items":{"type":"string"}},"filterableAttributes":{"type":"array","items":{"type":"string"}},"sortableAttributes":{"type":"array","items":{"type":"string"}},"rankingRules":{"type":"array","items":{"type":"string"}},"synonyms":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"stopWords":{"type":"array","items":{"type":"string"}},"displayedAttributes":{"type":"array","items":{"type":"string"}},"distinctAttribute":{"type":["string","null"]},"typoTolerance":{"type":"object","properties":{"enabled":{"type":"boolean"},"minWordSizeForTypos":{"type":"object","properties":{"oneTypo":{"type":"integer"},"twoTypos":{"type":"integer"}}},"disableOnWords":{"type":"array","items":{"type":"string"}},"disableOnAttributes":{"type":"array","items":{"type":"string"}}}},"faceting":{"type":"object","properties":{"maxValuesPerFacet":{"type":"integer"},"sortFacetValuesBy":{"type":"object","additionalProperties":{"type":"string"}}}},"pagination":{"type":"object","properties":{"maxTotalHits":{"type":"integer"}}},"separatorTokens":{"type":"array","items":{"type":"string"}},"nonSeparatorTokens":{"type":"array","items":{"type":"string"}},"dictionary":{"type":"array","items":{"type":"string"}},"proximityPrecision":{"type":"string","enum":["byWord","byAttribute"]},"searchCutoffMs":{"type":["integer","null"]},"embedders":{"type":["object","null"],"additionalProperties":true}},"additionalProperties":true}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `invalid_branch_name` (Headband-Branch), `invalid_index_uid`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`index_not_found`, or `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"`index_ambiguous`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"patch":{"operationId":"updateIndexSettings","summary":"Update index settings","description":"Sends the settings to the index's engine and answers 202 with the task. The body is passed to the engine adapter, which decides what applies. Meilisearch applies every setting. Typesense accepts `synonyms`, `searchableAttributes`, `filterableAttributes` and `sortableAttributes` and refuses the rest with 400. Elasticsearch refuses settings it cannot honor with 400 `unsupported_setting`. Visor refuses `faceting`, `distinctAttribute`, `embedders` and some others. An index Sync manages is refused.","tags":["Indexes"],"security":[{"adminKey":[]}],"x-headband-permissions":["settings.update"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"uid","in":"path","required":true,"description":"The index uid: 1 to 64 letters, numbers, hyphens and underscores.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"description":"The settings to change. Fields left out are unchanged.","content":{"application/json":{"schema":{"type":"object","description":"Headband's settings vocabulary (Meilisearch's). Engines apply different subsets: see docs/engine-capability-matrix.md.","properties":{"searchableAttributes":{"type":"array","items":{"type":"string"}},"filterableAttributes":{"type":"array","items":{"type":"string"}},"sortableAttributes":{"type":"array","items":{"type":"string"}},"rankingRules":{"type":"array","items":{"type":"string"}},"synonyms":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"stopWords":{"type":"array","items":{"type":"string"}},"displayedAttributes":{"type":"array","items":{"type":"string"}},"distinctAttribute":{"type":["string","null"]},"typoTolerance":{"type":"object","properties":{"enabled":{"type":"boolean"},"minWordSizeForTypos":{"type":"object","properties":{"oneTypo":{"type":"integer"},"twoTypos":{"type":"integer"}}},"disableOnWords":{"type":"array","items":{"type":"string"}},"disableOnAttributes":{"type":"array","items":{"type":"string"}}}},"faceting":{"type":"object","properties":{"maxValuesPerFacet":{"type":"integer"},"sortFacetValuesBy":{"type":"object","additionalProperties":{"type":"string"}}}},"pagination":{"type":"object","properties":{"maxTotalHits":{"type":"integer"}}},"separatorTokens":{"type":"array","items":{"type":"string"}},"nonSeparatorTokens":{"type":"array","items":{"type":"string"}},"dictionary":{"type":"array","items":{"type":"string"}},"proximityPrecision":{"type":"string","enum":["byWord","byAttribute"]},"searchCutoffMs":{"type":["integer","null"]},"embedders":{"type":["object","null"],"additionalProperties":true}},"additionalProperties":true}}}},"responses":{"202":{"description":"Update accepted.","content":{"application/json":{"schema":{"type":"object","required":["taskUid","indexUid","status"],"properties":{"taskUid":{"type":"integer","description":"The engine's task id. Poll GET /v1/tasks/{taskId} with `?index=`."},"indexUid":{"type":"string"},"status":{"type":"string","description":"The task status, such as `enqueued` or `succeeded`."},"type":{"type":"string"},"enqueuedAt":{"type":"string","format":"date-time"}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `invalid_json`, `invalid_index_uid`, `unsupported_setting`, `invalid_branch_name`, and the engine's own validation codes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"The project is over the plan's project limit and read-only (`plan_project_read_only`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`index_not_found`, or `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes include `index_ambiguous`, `index_managed_by_sync`, `settings_need_documents` (Typesense: a sortable field no document has typed yet).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"description":"Codes include `index_migrating` (with `Retry-After`), `instance_unavailable`, `auth_lookup_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}}}},"/v1/indexes/{uid}/stats":{"get":{"operationId":"getIndexStats","summary":"Get index stats","description":"The index's document count, indexing state and field distribution. Admin keys only.","tags":["Indexes"],"security":[{"adminKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"uid","in":"path","required":true,"description":"The index uid: 1 to 64 letters, numbers, hyphens and underscores.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The stats.","content":{"application/json":{"schema":{"type":"object","required":["numberOfDocuments","isIndexing","fieldDistribution"],"properties":{"numberOfDocuments":{"type":"integer"},"isIndexing":{"type":"boolean"},"fieldDistribution":{"type":"object","additionalProperties":{"type":"integer"}}},"additionalProperties":true}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `invalid_branch_name` (Headband-Branch), `invalid_index_uid`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"`index_not_found`, or `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"`index_ambiguous`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/instances":{"get":{"operationId":"listInstances","summary":"List engine instances","description":"Lists the org's dedicated engines, with their project and branch names and wake times over the last 24 hours. Needs an org-scoped admin key with `instances.manage` (in the admin defaults). A key limited to chosen engines sees only those.","responses":{"200":{"description":"The engines.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"allOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"engine":{"type":"string","enum":["meilisearch","elasticsearch","typesense","visor"]},"engineVersion":{"type":"string"},"lane":{"type":"string","enum":["always_on","serverless"]},"memoryMb":{"type":"integer"},"region":{"type":"string"},"state":{"type":"string","description":"The lifecycle state its host reports (running, parked, ...), `unavailable` while its host cannot serve it, or `starting` while a start runs past the engine's usual readiness wait (a large index loading). Requests answer 503 instance_waking with Retry-After until it is up."},"unavailableReason":{"type":["string","null"],"enum":["host_down","host_unreachable",null],"description":"Why `state` is `unavailable`. Null otherwise."},"starting":{"type":["object","null"],"properties":{"since":{"type":"string","format":"date-time","description":"When the start began."},"usualS":{"type":["integer","null"],"description":"How long a start of this engine with this much data usually takes, in seconds. Null when unknown."}},"required":["since","usualS"],"description":"Set while `state` is `starting`. Null otherwise."},"endpoint":{"type":"string"},"idleTimeoutS":{"type":"integer"},"projectId":{"type":["string","null"]},"branchId":{"type":["string","null"]},"isDefault":{"type":"boolean"},"pendingChanges":{"type":"boolean","description":"A change has not reached the engine host yet."},"changeRejected":{"type":"boolean","description":"The host refused the latest change and runs the previous settings."},"lastBackupAt":{"type":["string","null"],"format":"date-time"},"bucketHome":{"type":"boolean","description":"The engine keeps every acknowledged write in object storage itself and takes no backups."},"restartable":{"type":"boolean"},"parkedByPlan":{"type":"boolean","description":"Held parked because the plan no longer includes it. It wakes only after an upgrade."},"lastWakeAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","engine","engineVersion","lane","memoryMb","region","state","unavailableReason","starting","endpoint","idleTimeoutS","projectId","branchId","isDefault","pendingChanges","changeRejected","lastBackupAt","bucketHome","restartable","parkedByPlan","lastWakeAt","createdAt","updatedAt"]},{"type":"object","properties":{"projectName":{"type":["string","null"]},"branchName":{"type":["string","null"]},"branchPrimary":{"type":"boolean"},"wakeP50Ms":{"type":["number","null"],"description":"Median wake time over the last 24 hours."},"wakeP95Ms":{"type":["number","null"],"description":"95th percentile wake time over the last 24 hours."}}}]}},"defaultPlacement":{"type":"boolean","description":"Whether new indexes go to a dedicated engine by default."}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage engines. Codes: `permission_denied` (no `instances.manage`, or not an admin key), `org_key_required` (a branch key), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"The org has no dedicated engines (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Instances"],"security":[{"adminKey":[]}],"x-headband-permissions":["instances.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Dedicated engine instances are enabled per org. An org without them gets 404 `not_found` on every /v1/instances path, whatever the key.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]},"post":{"operationId":"createInstance","summary":"Create an engine instance","description":"Creates a dedicated engine on a branch: the branch named by Headband-Branch, else `projectId`'s primary branch, else the Default project's main. With no `lane` on a plan without always-warm engines, an engine that can run serverless starts serverless. Needs an org-scoped admin key with `instances.manage` (in the admin defaults). A key limited to chosen engines sees only those. Writes share a rate-limit bucket of 30 per key.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"engine":{"type":"string","enum":["meilisearch","elasticsearch","typesense","visor"]},"name":{"type":"string","minLength":1,"maxLength":64,"description":"Trimmed. No control characters. Default `<engine>-<region>`."},"lane":{"type":"string","enum":["always_on","serverless"],"description":"Default: the engine's default lane. Always on needs a paid plan, except where it is the engine's only lane."},"memoryMb":{"type":"integer","enum":[512,1024,2048,4096,8192,16384,32768],"description":"One of the engine's sizes. Elasticsearch starts at 1024. A Free plan runs only the smallest size. Default: the smallest."},"region":{"type":"string","pattern":"^[a-z0-9]{2,16}$","description":"Default: the platform's region."},"idleTimeoutS":{"type":"integer","minimum":60,"maximum":86400,"description":"Seconds of no traffic before a serverless engine parks.","default":300},"projectId":{"type":["string","null"],"pattern":"^[A-Za-z0-9_-]{1,64}$"},"isDefault":{"type":"boolean","description":"Make it the org's default engine."}},"required":["engine"]}}}},"responses":{"201":{"description":"The new engine.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"engine":{"type":"string","enum":["meilisearch","elasticsearch","typesense","visor"]},"engineVersion":{"type":"string"},"lane":{"type":"string","enum":["always_on","serverless"]},"memoryMb":{"type":"integer"},"region":{"type":"string"},"state":{"type":"string","description":"The lifecycle state its host reports (running, parked, ...), `unavailable` while its host cannot serve it, or `starting` while a start runs past the engine's usual readiness wait (a large index loading). Requests answer 503 instance_waking with Retry-After until it is up."},"unavailableReason":{"type":["string","null"],"enum":["host_down","host_unreachable",null],"description":"Why `state` is `unavailable`. Null otherwise."},"starting":{"type":["object","null"],"properties":{"since":{"type":"string","format":"date-time","description":"When the start began."},"usualS":{"type":["integer","null"],"description":"How long a start of this engine with this much data usually takes, in seconds. Null when unknown."}},"required":["since","usualS"],"description":"Set while `state` is `starting`. Null otherwise."},"endpoint":{"type":"string"},"idleTimeoutS":{"type":"integer"},"projectId":{"type":["string","null"]},"branchId":{"type":["string","null"]},"isDefault":{"type":"boolean"},"pendingChanges":{"type":"boolean","description":"A change has not reached the engine host yet."},"changeRejected":{"type":"boolean","description":"The host refused the latest change and runs the previous settings."},"lastBackupAt":{"type":["string","null"],"format":"date-time"},"bucketHome":{"type":"boolean","description":"The engine keeps every acknowledged write in object storage itself and takes no backups."},"restartable":{"type":"boolean"},"parkedByPlan":{"type":"boolean","description":"Held parked because the plan no longer includes it. It wakes only after an upgrade."},"lastWakeAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","engine","engineVersion","lane","memoryMb","region","state","unavailableReason","starting","endpoint","idleTimeoutS","projectId","branchId","isDefault","pendingChanges","changeRejected","lastBackupAt","bucketHome","restartable","parkedByPlan","lastWakeAt","createdAt","updatedAt"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Invalid request. Codes: `invalid_json`, `invalid_body`, `invalid_engine`, `invalid_lane`, `lane_not_supported`, `invalid_memory`, `invalid_region`, `unknown_region`, `invalid_idle_timeout`, `invalid_name`, `invalid_project`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"The plan does not allow this. Codes: `plan_required`, `plan_engine_limit`, `plan_project_limit`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"The key may not create engines. Codes: `permission_denied` (no `instances.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`, `branch_not_allowed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (the org has no dedicated engines), `project_not_found`, `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `branch_deleting`, `default_instance_exists`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Codes: `engine_not_available`, `no_capacity`, `auth_lookup_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"tags":["Instances"],"security":[{"adminKey":[]}],"x-headband-permissions":["instances.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Dedicated engine instances are enabled per org. An org without them gets 404 `not_found` on every /v1/instances path, whatever the key."}},"/v1/instances/{instanceId}":{"get":{"operationId":"getInstance","summary":"Get an engine instance","description":"One dedicated engine. Needs an org-scoped admin key with `instances.manage` (in the admin defaults). A key limited to chosen engines sees only those.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"instanceId","in":"path","required":true,"description":"The engine instance's id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The engine.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"engine":{"type":"string","enum":["meilisearch","elasticsearch","typesense","visor"]},"engineVersion":{"type":"string"},"lane":{"type":"string","enum":["always_on","serverless"]},"memoryMb":{"type":"integer"},"region":{"type":"string"},"state":{"type":"string","description":"The lifecycle state its host reports (running, parked, ...), `unavailable` while its host cannot serve it, or `starting` while a start runs past the engine's usual readiness wait (a large index loading). Requests answer 503 instance_waking with Retry-After until it is up."},"unavailableReason":{"type":["string","null"],"enum":["host_down","host_unreachable",null],"description":"Why `state` is `unavailable`. Null otherwise."},"starting":{"type":["object","null"],"properties":{"since":{"type":"string","format":"date-time","description":"When the start began."},"usualS":{"type":["integer","null"],"description":"How long a start of this engine with this much data usually takes, in seconds. Null when unknown."}},"required":["since","usualS"],"description":"Set while `state` is `starting`. Null otherwise."},"endpoint":{"type":"string"},"idleTimeoutS":{"type":"integer"},"projectId":{"type":["string","null"]},"branchId":{"type":["string","null"]},"isDefault":{"type":"boolean"},"pendingChanges":{"type":"boolean","description":"A change has not reached the engine host yet."},"changeRejected":{"type":"boolean","description":"The host refused the latest change and runs the previous settings."},"lastBackupAt":{"type":["string","null"],"format":"date-time"},"bucketHome":{"type":"boolean","description":"The engine keeps every acknowledged write in object storage itself and takes no backups."},"restartable":{"type":"boolean"},"parkedByPlan":{"type":"boolean","description":"Held parked because the plan no longer includes it. It wakes only after an upgrade."},"lastWakeAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","engine","engineVersion","lane","memoryMb","region","state","unavailableReason","starting","endpoint","idleTimeoutS","projectId","branchId","isDefault","pendingChanges","changeRejected","lastBackupAt","bucketHome","restartable","parkedByPlan","lastWakeAt","createdAt","updatedAt"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage engines. Codes: `permission_denied` (no `instances.manage`, or not an admin key), `org_key_required` (a branch key), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (the org has no dedicated engines), `instance_not_found` (no such engine in this org, or not on the key's engine list).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Instances"],"security":[{"adminKey":[]}],"x-headband-permissions":["instances.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Dedicated engine instances are enabled per org. An org without them gets 404 `not_found` on every /v1/instances path, whatever the key."},"patch":{"operationId":"updateInstance","summary":"Update an engine instance","description":"Changes an engine's name, lane, size or idle timeout. Engine and region cannot change. A new size or a move to always on is checked against the plan. Needs an org-scoped admin key with `instances.manage` (in the admin defaults). A key limited to chosen engines sees only those.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"instanceId","in":"path","required":true,"description":"The engine instance's id.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":64,"description":"Trimmed. No control characters."},"lane":{"type":"string","enum":["always_on","serverless"]},"memoryMb":{"type":"integer","enum":[512,1024,2048,4096,8192,16384,32768],"description":"One of the engine's sizes. Elasticsearch starts at 1024. A Free plan runs only the smallest size."},"idleTimeoutS":{"type":"integer","minimum":60,"maximum":86400,"description":"Seconds of no traffic before a serverless engine parks."}},"minProperties":1,"description":"At least one field. `engine` or `region` is refused (`immutable_field`)."}}}},"responses":{"200":{"description":"The engine.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"engine":{"type":"string","enum":["meilisearch","elasticsearch","typesense","visor"]},"engineVersion":{"type":"string"},"lane":{"type":"string","enum":["always_on","serverless"]},"memoryMb":{"type":"integer"},"region":{"type":"string"},"state":{"type":"string","description":"The lifecycle state its host reports (running, parked, ...), `unavailable` while its host cannot serve it, or `starting` while a start runs past the engine's usual readiness wait (a large index loading). Requests answer 503 instance_waking with Retry-After until it is up."},"unavailableReason":{"type":["string","null"],"enum":["host_down","host_unreachable",null],"description":"Why `state` is `unavailable`. Null otherwise."},"starting":{"type":["object","null"],"properties":{"since":{"type":"string","format":"date-time","description":"When the start began."},"usualS":{"type":["integer","null"],"description":"How long a start of this engine with this much data usually takes, in seconds. Null when unknown."}},"required":["since","usualS"],"description":"Set while `state` is `starting`. Null otherwise."},"endpoint":{"type":"string"},"idleTimeoutS":{"type":"integer"},"projectId":{"type":["string","null"]},"branchId":{"type":["string","null"]},"isDefault":{"type":"boolean"},"pendingChanges":{"type":"boolean","description":"A change has not reached the engine host yet."},"changeRejected":{"type":"boolean","description":"The host refused the latest change and runs the previous settings."},"lastBackupAt":{"type":["string","null"],"format":"date-time"},"bucketHome":{"type":"boolean","description":"The engine keeps every acknowledged write in object storage itself and takes no backups."},"restartable":{"type":"boolean"},"parkedByPlan":{"type":"boolean","description":"Held parked because the plan no longer includes it. It wakes only after an upgrade."},"lastWakeAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","engine","engineVersion","lane","memoryMb","region","state","unavailableReason","starting","endpoint","idleTimeoutS","projectId","branchId","isDefault","pendingChanges","changeRejected","lastBackupAt","bucketHome","restartable","parkedByPlan","lastWakeAt","createdAt","updatedAt"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Invalid request. Codes: `invalid_json`, `invalid_body`, `immutable_field`, `empty_update`, `invalid_name`, `invalid_lane`, `invalid_memory`, `invalid_idle_timeout`, `lane_not_supported`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Codes: `plan_required`, `plan_downgrade_parked`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"The key may not manage engines. Codes: `permission_denied` (no `instances.manage`, or not an admin key), `org_key_required` (a branch key), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (the org has no dedicated engines), `instance_not_found` (no such engine in this org, or not on the key's engine list).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `instance_deleting`, `vm_lane_unavailable`, `disk_exceeds_plan`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"The engine host refused the new settings (`spec_rejected`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Codes: `no_capacity`, `instance_unavailable` (with `Retry-After`), `auth_lookup_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"tags":["Instances"],"security":[{"adminKey":[]}],"x-headband-permissions":["instances.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Dedicated engine instances are enabled per org. An org without them gets 404 `not_found` on every /v1/instances path, whatever the key."},"delete":{"operationId":"deleteInstance","summary":"Delete an engine instance","description":"Deletes an engine that no index or synced index uses. `deleted` is false while the host has not finished; the delete then completes in the background. Needs an org-scoped admin key with `instances.manage` (in the admin defaults). A key limited to chosen engines sees only those.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"instanceId","in":"path","required":true,"description":"The engine instance's id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The engine as it is now.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"engine":{"type":"string","enum":["meilisearch","elasticsearch","typesense","visor"]},"engineVersion":{"type":"string"},"lane":{"type":"string","enum":["always_on","serverless"]},"memoryMb":{"type":"integer"},"region":{"type":"string"},"state":{"type":"string","description":"The lifecycle state its host reports (running, parked, ...), `unavailable` while its host cannot serve it, or `starting` while a start runs past the engine's usual readiness wait (a large index loading). Requests answer 503 instance_waking with Retry-After until it is up."},"unavailableReason":{"type":["string","null"],"enum":["host_down","host_unreachable",null],"description":"Why `state` is `unavailable`. Null otherwise."},"starting":{"type":["object","null"],"properties":{"since":{"type":"string","format":"date-time","description":"When the start began."},"usualS":{"type":["integer","null"],"description":"How long a start of this engine with this much data usually takes, in seconds. Null when unknown."}},"required":["since","usualS"],"description":"Set while `state` is `starting`. Null otherwise."},"endpoint":{"type":"string"},"idleTimeoutS":{"type":"integer"},"projectId":{"type":["string","null"]},"branchId":{"type":["string","null"]},"isDefault":{"type":"boolean"},"pendingChanges":{"type":"boolean","description":"A change has not reached the engine host yet."},"changeRejected":{"type":"boolean","description":"The host refused the latest change and runs the previous settings."},"lastBackupAt":{"type":["string","null"],"format":"date-time"},"bucketHome":{"type":"boolean","description":"The engine keeps every acknowledged write in object storage itself and takes no backups."},"restartable":{"type":"boolean"},"parkedByPlan":{"type":"boolean","description":"Held parked because the plan no longer includes it. It wakes only after an upgrade."},"lastWakeAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","engine","engineVersion","lane","memoryMb","region","state","unavailableReason","starting","endpoint","idleTimeoutS","projectId","branchId","isDefault","pendingChanges","changeRejected","lastBackupAt","bucketHome","restartable","parkedByPlan","lastWakeAt","createdAt","updatedAt"]},{"type":"object","properties":{"deleted":{"type":"boolean","description":"True once the delete has finished."}},"required":["deleted"]}]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage engines. Codes: `permission_denied` (no `instances.manage`, or not an admin key), `org_key_required` (a branch key), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (the org has no dedicated engines), `instance_not_found` (no such engine in this org, or not on the key's engine list).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `instance_has_synced_indexes`, `instance_in_use` (names some of its indexes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Instances"],"security":[{"adminKey":[]}],"x-headband-permissions":["instances.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Dedicated engine instances are enabled per org. An org without them gets 404 `not_found` on every /v1/instances path, whatever the key."}},"/v1/instances/{instanceId}/park":{"post":{"operationId":"parkInstance","summary":"Park an engine instance","description":"Parks a serverless engine now. Always-on engines cannot be parked. Needs an org-scoped admin key with `instances.manage` (in the admin defaults). A key limited to chosen engines sees only those.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"instanceId","in":"path","required":true,"description":"The engine instance's id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The engine.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"engine":{"type":"string","enum":["meilisearch","elasticsearch","typesense","visor"]},"engineVersion":{"type":"string"},"lane":{"type":"string","enum":["always_on","serverless"]},"memoryMb":{"type":"integer"},"region":{"type":"string"},"state":{"type":"string","description":"The lifecycle state its host reports (running, parked, ...), `unavailable` while its host cannot serve it, or `starting` while a start runs past the engine's usual readiness wait (a large index loading). Requests answer 503 instance_waking with Retry-After until it is up."},"unavailableReason":{"type":["string","null"],"enum":["host_down","host_unreachable",null],"description":"Why `state` is `unavailable`. Null otherwise."},"starting":{"type":["object","null"],"properties":{"since":{"type":"string","format":"date-time","description":"When the start began."},"usualS":{"type":["integer","null"],"description":"How long a start of this engine with this much data usually takes, in seconds. Null when unknown."}},"required":["since","usualS"],"description":"Set while `state` is `starting`. Null otherwise."},"endpoint":{"type":"string"},"idleTimeoutS":{"type":"integer"},"projectId":{"type":["string","null"]},"branchId":{"type":["string","null"]},"isDefault":{"type":"boolean"},"pendingChanges":{"type":"boolean","description":"A change has not reached the engine host yet."},"changeRejected":{"type":"boolean","description":"The host refused the latest change and runs the previous settings."},"lastBackupAt":{"type":["string","null"],"format":"date-time"},"bucketHome":{"type":"boolean","description":"The engine keeps every acknowledged write in object storage itself and takes no backups."},"restartable":{"type":"boolean"},"parkedByPlan":{"type":"boolean","description":"Held parked because the plan no longer includes it. It wakes only after an upgrade."},"lastWakeAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","engine","engineVersion","lane","memoryMb","region","state","unavailableReason","starting","endpoint","idleTimeoutS","projectId","branchId","isDefault","pendingChanges","changeRejected","lastBackupAt","bucketHome","restartable","parkedByPlan","lastWakeAt","createdAt","updatedAt"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage engines. Codes: `permission_denied` (no `instances.manage`, or not an admin key), `org_key_required` (a branch key), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (the org has no dedicated engines), `instance_not_found` (no such engine in this org, or not on the key's engine list).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `always_on`, `instance_deleting`, `instance_busy`, or another code the host gave.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"description":"The engine host rejected the request (`box_rejected`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"503":{"description":"Codes: `instance_unavailable` (the engine host is not reachable; carries `Retry-After`), `auth_lookup_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"tags":["Instances"],"security":[{"adminKey":[]}],"x-headband-permissions":["instances.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Dedicated engine instances are enabled per org. An org without them gets 404 `not_found` on every /v1/instances path, whatever the key."}},"/v1/instances/{instanceId}/wake":{"post":{"operationId":"wakeInstance","summary":"Wake an engine instance","description":"Wakes an engine and reports how. An engine held parked by the plan does not wake. Needs an org-scoped admin key with `instances.manage` (in the admin defaults). A key limited to chosen engines sees only those.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"instanceId","in":"path","required":true,"description":"The engine instance's id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The engine and the wake.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"instance":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"engine":{"type":"string","enum":["meilisearch","elasticsearch","typesense","visor"]},"engineVersion":{"type":"string"},"lane":{"type":"string","enum":["always_on","serverless"]},"memoryMb":{"type":"integer"},"region":{"type":"string"},"state":{"type":"string","description":"The lifecycle state its host reports (running, parked, ...), `unavailable` while its host cannot serve it, or `starting` while a start runs past the engine's usual readiness wait (a large index loading). Requests answer 503 instance_waking with Retry-After until it is up."},"unavailableReason":{"type":["string","null"],"enum":["host_down","host_unreachable",null],"description":"Why `state` is `unavailable`. Null otherwise."},"starting":{"type":["object","null"],"properties":{"since":{"type":"string","format":"date-time","description":"When the start began."},"usualS":{"type":["integer","null"],"description":"How long a start of this engine with this much data usually takes, in seconds. Null when unknown."}},"required":["since","usualS"],"description":"Set while `state` is `starting`. Null otherwise."},"endpoint":{"type":"string"},"idleTimeoutS":{"type":"integer"},"projectId":{"type":["string","null"]},"branchId":{"type":["string","null"]},"isDefault":{"type":"boolean"},"pendingChanges":{"type":"boolean","description":"A change has not reached the engine host yet."},"changeRejected":{"type":"boolean","description":"The host refused the latest change and runs the previous settings."},"lastBackupAt":{"type":["string","null"],"format":"date-time"},"bucketHome":{"type":"boolean","description":"The engine keeps every acknowledged write in object storage itself and takes no backups."},"restartable":{"type":"boolean"},"parkedByPlan":{"type":"boolean","description":"Held parked because the plan no longer includes it. It wakes only after an upgrade."},"lastWakeAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","engine","engineVersion","lane","memoryMb","region","state","unavailableReason","starting","endpoint","idleTimeoutS","projectId","branchId","isDefault","pendingChanges","changeRejected","lastBackupAt","bucketHome","restartable","parkedByPlan","lastWakeAt","createdAt","updatedAt"]},"wake":{"type":"object","properties":{"via":{"type":"string"},"ms":{"type":"number"},"state":{"type":"string"}},"required":["via","ms","state"]}},"required":["instance","wake"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"The plan no longer includes this engine (`plan_downgrade_parked`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"The key may not manage engines. Codes: `permission_denied` (no `instances.manage`, or not an admin key), `org_key_required` (a branch key), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (the org has no dedicated engines), `instance_not_found` (no such engine in this org, or not on the key's engine list).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `instance_deleting`, `instance_busy`, or another code the host gave.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"description":"The engine host rejected the request (`box_rejected`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"503":{"description":"Codes: `instance_waking` (still waking, `Retry-After: 5`), `instance_unavailable` (with `Retry-After`), `auth_lookup_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"tags":["Instances"],"security":[{"adminKey":[]}],"x-headband-permissions":["instances.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Dedicated engine instances are enabled per org. An org without them gets 404 `not_found` on every /v1/instances path, whatever the key."}},"/v1/instances/{instanceId}/wakes":{"get":{"operationId":"listInstanceWakes","summary":"List an engine's wakes","description":"The engine's recent wakes, newest first. Needs an org-scoped admin key with `instances.manage` (in the admin defaults). A key limited to chosen engines sees only those.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"instanceId","in":"path","required":true,"description":"The engine instance's id.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"How many. Above 200 is cut to 200; anything not a positive integer means 50.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"The wakes.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"requestedAt":{"type":"string","format":"date-time"},"readyAt":{"type":["string","null"],"format":"date-time"},"firstByteAt":{"type":["string","null"],"format":"date-time"},"via":{"type":"string"},"outcome":{"type":"string"},"readyMs":{"type":["number","null"]},"phases":{"type":"object","additionalProperties":{"type":"number"}}},"required":["requestedAt","readyAt","firstByteAt","via","outcome","readyMs","phases"]}}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage engines. Codes: `permission_denied` (no `instances.manage`, or not an admin key), `org_key_required` (a branch key), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (the org has no dedicated engines), `instance_not_found` (no such engine in this org, or not on the key's engine list).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Instances"],"security":[{"adminKey":[]}],"x-headband-permissions":["instances.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Dedicated engine instances are enabled per org. An org without them gets 404 `not_found` on every /v1/instances path, whatever the key."}},"/v1/keys":{"get":{"operationId":"listKeys","summary":"List API keys","description":"Lists every API key of the key's org, newest first. Never returns a key's secret or hash. Only keys minted by the CLI device flow (`headband login`) hold this permission. It is in no default set, and POST /v1/keys can never grant it.","tags":["Keys"],"responses":{"200":{"description":"The org's keys.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"keys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["admin","search"]},"keyPrefix":{"type":"string","description":"The key's display prefix. Never the key itself."},"projectId":{"type":["string","null"],"description":"Null for an org key."},"instanceIds":{"type":["array","null"],"items":{"type":"string"},"description":"The dedicated engines the key is limited to. Null: not limited."},"indexUids":{"type":["array","null"],"items":{"type":"string"},"description":"The index uids a search key is limited to. Null: not limited."},"branch":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","description":"The branch id."},"name":{"type":"string","description":"The branch name."}},"required":["id","name"]},{"type":"null"}],"description":"Null for an org key."},"environment":{"type":"string","description":"Read from the key prefix."},"createdAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time"},"previousSecret":{"oneOf":[{"type":"object","properties":{"expiresAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":["string","null"],"format":"date-time"}},"required":["expiresAt","lastUsedAt"]},{"type":"null"}],"description":"After a rotation, the old secret while its grace period runs: when it stops working, and when it was last used. Null otherwise."}},"required":["id","name","type","keyPrefix","projectId","instanceIds","indexUids","branch","environment","createdAt","lastUsedAt","expiresAt","previousSecret"]}}},"required":["keys"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (no `keys.manage`), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required` (a key limited to chosen indexes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"security":[{"adminKey":[]}],"x-headband-permissions":["keys.manage"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]},"post":{"operationId":"createKey","summary":"Create an API key","description":"Creates an admin or search key and returns its raw value once, in `apiKey`. Only keys minted by the CLI device flow (`headband login`) hold this permission. It is in no default set, and POST /v1/keys can never grant it. The new key gets its type's default permissions, cut down to what the calling key holds; a named `permissions` list must sit inside that set, or the request is refused (never silently narrowed). Where it lands: a branch key's own branch; else the branch named by `branch` or the Headband-Branch header; else the named project's primary branch. An org key that names no project and no branch mints an org admin key; a search key always needs a branch (`branch_required`). `indexUids` is for search keys only. `expiresAt` must be in the future and at most one year away. Writes share a rate-limit bucket of 100 per key.","tags":["Keys"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Trimmed."},"type":{"type":"string","enum":["admin","search"]},"projectId":{"type":["string","null"],"minLength":1,"description":"The project whose primary branch the key lands on. An empty string is refused."},"branch":{"type":["string","null"],"minLength":1,"description":"A branch id or name. Takes the Headband-Branch header's place."},"permissions":{"type":["array","null"],"minItems":1,"items":{"type":"string"},"description":"Permission names, a subset of what the calling key could grant for this type. Management permissions are never grantable."},"expiresAt":{"type":["string","null"],"format":"date-time"},"instanceIds":{"type":["array","null"],"minItems":1,"maxItems":50,"items":{"type":"string"},"description":"Live dedicated engines of this org (and of the key's branch, for a branch key) to limit the key to. Not for a project key without a branch."},"indexUids":{"type":["array","null"],"minItems":1,"maxItems":100,"items":{"type":"string"},"description":"Search keys only: index uids to limit the key to. Deduplicated and sorted; not looked up, so an index created later is reached once it exists."}},"required":["name","type"]}}}},"responses":{"201":{"description":"The key. `apiKey` is the raw secret, shown only in this response.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"key":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["admin","search"]},"keyPrefix":{"type":"string"},"projectId":{"type":["string","null"]},"instanceIds":{"type":["array","null"],"items":{"type":"string"}},"indexUids":{"type":["array","null"],"items":{"type":"string"}},"branch":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","description":"The branch id."},"name":{"type":"string","description":"The branch name."}},"required":["id","name"]},{"type":"null"}]},"environment":{"type":"string"},"permissions":{"type":"array","items":{"type":"string"},"description":"Present only when the request named a permission list."},"expiresAt":{"type":["string","null"],"format":"date-time"}},"required":["id","name","type","keyPrefix","projectId","instanceIds","indexUids","branch","environment","expiresAt"]},"apiKey":{"type":"string","description":"The raw key. Store it now."}},"required":["key","apiKey"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Invalid request. Codes: `invalid_json`, `invalid_name`, `invalid_type`, `invalid_permissions`, `invalid_index_uids`, `invalid_instance_ids`, `invalid_expires_at`, `invalid_project_id`, `invalid_branch_name`, `branch_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Plan limit (`plan_project_limit`): landing the key would create a Default project the plan has no room for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"The key may not do this. Codes: `permission_denied` (no `keys.manage`, holds none of the type's permissions, or names permissions it cannot grant, listed in `deniedPermissions`), `branch_not_allowed` (a branch key naming another project or branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `project_not_found`, `branch_not_found`, `instance_not_found` (any `instanceIds` entry that is not a live engine of this org or branch).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The branch changed under the request. Codes: `branch_deleting`, `branch_changed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"security":[{"adminKey":[]}],"x-headband-permissions":["keys.manage"],"x-headband-key-types":["admin"]}},"/v1/keys/{keyId}":{"patch":{"operationId":"updateKeyEngines","summary":"Change which engines a key reaches","description":"Sets the key's engine list: a list limits it to those dedicated engines, null removes the limit. A branch key with no list reaches every live engine of its branch; an org key with no list reaches every engine of its org. The rules are a key create's: every listed engine is live, of this org, and for a branch key on its branch. Only keys minted by the CLI device flow (`headband login`) hold this permission. It is in no default set, and POST /v1/keys can never grant it. A narrowed key loses its reach at once: caches are cleared and engine hosts are told before this answers. The old secret of a rotation still in its grace period gets the same list. A branch key may change only keys of its own branch; any other key answers 404. Shares the keys rate-limit bucket of 100 per key.","tags":["Keys"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"keyId","in":"path","required":true,"description":"The key's id.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"instanceIds":{"type":["array","null"],"minItems":1,"maxItems":50,"items":{"type":"string"},"description":"Live dedicated engines of this org (and of the key's branch, for a branch key) to limit the key to. Null: no list."}},"required":["instanceIds"]}}}},"responses":{"200":{"description":"The key with its new list.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"key":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["admin","search"]},"keyPrefix":{"type":"string"},"projectId":{"type":["string","null"]},"instanceIds":{"type":["array","null"],"items":{"type":"string"}},"indexUids":{"type":["array","null"],"items":{"type":"string"}},"branch":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","description":"The branch id."},"name":{"type":"string","description":"The branch name."}},"required":["id","name"]},{"type":"null"}]},"environment":{"type":"string","description":"Read from the key prefix."},"createdAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time"}},"required":["id","name","type","keyPrefix","projectId","instanceIds","indexUids","branch","environment","createdAt","lastUsedAt","expiresAt"]}},"required":["key"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Invalid request. Codes: `invalid_json`, `invalid_instance_ids` (absent, or not null or a list of 1 to 50 ids).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (no `keys.manage`), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required` (a key limited to chosen indexes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `key_not_found` (no such key in this org or reach), `instance_not_found` (any `instanceIds` entry that is not a live engine of this org or of the key's branch).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"security":[{"adminKey":[]}],"x-headband-permissions":["keys.manage"],"x-headband-key-types":["admin"]},"delete":{"operationId":"deleteKey","summary":"Delete an API key","description":"Deletes one key of the org, and the old secret of a rotation still in its grace period. It stops working at once (caches are cleared and engine hosts are told). Only keys minted by the CLI device flow (`headband login`) hold this permission. It is in no default set, and POST /v1/keys can never grant it.","tags":["Keys"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"keyId","in":"path","required":true,"description":"The key's id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"const":true}},"required":["deleted"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (no `keys.manage`), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required` (a key limited to chosen indexes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No such key in this org (`key_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"security":[{"adminKey":[]}],"x-headband-permissions":["keys.manage"],"x-headband-key-types":["admin"]}},"/v1/keys/{keyId}/rotate":{"post":{"operationId":"rotateKey","summary":"Rotate an API key's secret","description":"Gives the key a new secret, returned once in `apiKey`. The old secret keeps working for the grace period: 24 hours by default, 0 ends it now, at most 7 days (604800 seconds). Only keys minted by the CLI device flow (`headband login`) hold this permission. It is in no default set, and POST /v1/keys can never grant it. A caller may rotate only a key it could have made itself: within its reach, holding no permission the caller lacks. Any other key answers the same 404 as another org's key. Deleting the key (DELETE /v1/keys/{keyId}) deletes its old secret too. Shares the keys rate-limit bucket of 100 per key.","tags":["Keys"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"keyId","in":"path","required":true,"description":"The key's id.","schema":{"type":"string"}}],"requestBody":{"required":false,"description":"Optional. An empty body uses the defaults.","content":{"application/json":{"schema":{"type":"object","properties":{"gracePeriodSeconds":{"type":"integer","minimum":0,"maximum":604800,"default":86400,"description":"How long the old secret keeps working, in seconds."}}}}}},"responses":{"200":{"description":"The rotated key. `apiKey` is the new raw secret, shown only in this response.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"key":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["admin","search"]},"keyPrefix":{"type":"string","description":"The new display prefix."},"projectId":{"type":["string","null"]},"instanceIds":{"type":["array","null"],"items":{"type":"string"}},"indexUids":{"type":["array","null"],"items":{"type":"string"}},"branchId":{"type":["string","null"]},"environment":{"type":"string","description":"Read from the key prefix."},"expiresAt":{"type":["string","null"],"format":"date-time"}},"required":["id","name","type","keyPrefix","projectId","instanceIds","indexUids","branchId","environment","expiresAt"]},"apiKey":{"type":"string","description":"The new raw key. Store it now."},"previousSecretExpiresAt":{"type":["string","null"],"format":"date-time","description":"When the old secret stops working. Null when the grace period was 0."}},"required":["key","apiKey","previousSecretExpiresAt"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad body. Codes: `invalid_json`, `invalid_grace_period` (not a whole number from 0 to 604800).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (no `keys.manage`), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required` (a key limited to chosen indexes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No such key in this org, or one the caller may not rotate (`key_not_found`). Also `branch_not_found` when the key's branch is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The key cannot be rotated now. Codes: `key_expired`, `branch_deleting`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"security":[{"adminKey":[]}],"x-headband-permissions":["keys.manage"],"x-headband-key-types":["admin"]}},"/v1/llms-txt":{"get":{"operationId":"getLlmsTxt","summary":"Generate llms.txt","description":"Builds an llms.txt document listing the indexes in the request's branch scope with their document counts. An index whose count cannot be read is listed with 0. Keys limited to chosen indexes or instances are refused.","tags":["Search"],"security":[{"adminKey":[]},{"searchKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin","search"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The llms.txt document.","content":{"text/plain":{"schema":{"type":"string"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"A malformed Headband-Branch (`invalid_branch_name`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (with `requiredPermission`), `branch_not_allowed` (a branch key naming another branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"The Headband-Branch names no live branch (`branch_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/multi-search":{"post":{"operationId":"multiSearch","summary":"Search several indexes at once","description":"Runs several searches in one request, each on its own index and engine, in parallel. The number of queries is capped by the plan. Each query takes the same parameters as /v1/search, with `indexUid` naming the index. A query that fails does not fail the request: its result carries `error` and `code` instead of hits. One Headband-Branch applies to every query. Each query counts as one search.","tags":["Search"],"security":[{"adminKey":[]},{"searchKey":[]},{"tenantToken":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin","search","tenant"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["queries"],"properties":{"queries":{"type":"array","minItems":1,"description":"At most the plan's multi-search query limit.","items":{"type":"object","required":["indexUid"],"properties":{"indexUid":{"type":"string","description":"The index uid."},"q":{"type":"string","description":"The query. Empty or missing searches everything."},"filter":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Filter expression. Under a tenant token the token's filter for this index is AND-ed with it; a filter that cannot be combined safely is a 400 `invalid_filter`."},"sort":{"type":"array","items":{"type":"string"}},"facets":{"type":"array","items":{"type":"string"}},"limit":{"type":"integer","minimum":0,"default":20,"description":"Hits to return. Above the engine's most per request (Typesense 250, Elasticsearch 10,000, Visor 1000, Meilisearch its `pagination.maxTotalHits`) it is lowered, and the answer carries `limitCapped: true`."},"offset":{"type":"integer","minimum":0},"page":{"type":"integer","minimum":1,"description":"Page mode: send `page` and/or `hitsPerPage`. Works on every engine."},"hitsPerPage":{"type":"integer","minimum":0,"default":20},"attributesToHighlight":{"type":"array","items":{"type":"string"}},"attributesToRetrieve":{"type":"array","items":{"type":"string"}},"attributesToCrop":{"type":"array","items":{"type":"string"}},"cropLength":{"type":"integer"},"showMatchesPosition":{"type":"boolean"},"showRankingScore":{"type":"boolean"},"matchingStrategy":{"type":"string","enum":["all","last","frequency"]},"hybrid":{"type":"object","properties":{"semanticRatio":{"type":"number"},"embedder":{"type":"string"}}},"vector":{"type":"array","items":{"type":"number"}},"mode":{"type":"string","enum":["instant","retrieval"],"description":"Visor only: `instant` (the default) or `retrieval` (BM25F). Other engines ignore it."}},"additionalProperties":true}}}}}}},"responses":{"200":{"description":"One result per query, in order.","content":{"application/json":{"schema":{"type":"object","required":["results"],"properties":{"results":{"type":"array","items":{"type":"object","required":["indexUid"],"properties":{"indexUid":{"type":"string"},"hits":{"type":"array","items":{"type":"object","additionalProperties":true}},"query":{"type":"string"},"processingTimeMs":{"type":"number"},"estimatedTotalHits":{"type":"integer"},"totalHits":{"type":"integer"},"totalHitsExact":{"type":"boolean","description":"False when the total is a lower bound (Elasticsearch past 10,000, Visor past 1000)."},"maxTotalHits":{"type":"integer","description":"Present when the total is past what paging can reach: the hits paging stops at."},"limitCapped":{"type":"boolean","description":"True when `limit` or `hitsPerPage` was lowered to the engine's most per request; that field then holds the value used."},"limit":{"type":"integer"},"page":{"type":"integer","description":"Page mode only."},"hitsPerPage":{"type":"integer","description":"Page mode only."},"totalPages":{"type":"integer","description":"Page mode only. Counts only pages that can be fetched."},"facetDistribution":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"number"}}},"facetStats":{"type":"object","additionalProperties":{"type":"object","properties":{"min":{"type":"number"},"max":{"type":"number"}}}},"queryId":{"type":"string","description":"Only for organizations with click feedback. Send it back on /v1/relevance/events to attribute clicks to this search."},"_engine":{"type":"string","description":"The engine that served this query."},"error":{"type":"string","description":"Set when this query failed."},"code":{"type":"string","description":"Set when this query failed, as /v1/search would answer (`index_not_found`, `index_ambiguous`, ...)."}},"additionalProperties":true}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad request. Codes: `invalid_json`, `invalid_request` (no queries), `too_many_search_queries` (over the plan's limit), `index_required` (a query without `indexUid`), `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (with `requiredPermission`), `branch_not_allowed` (a branch key naming another branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"The Headband-Branch names no live branch (`branch_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"description":"Rate limit or the plan's monthly search cap. Codes: `rate_limited` (the plan's searches a second), `usage_limit_exceeded` (monthly cap, with `plan`, `current`, `limit`, `resetsAt`, `upgradeUrl`; `Retry-After` counts to the reset). The per-key and per-IP request limits answer with no code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"The key lookup failed (`auth_lookup_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}}}},"/v1/openapi.json":{"get":{"operationId":"getOpenApiSpec","summary":"This OpenAPI document","description":"The OpenAPI 3.1 description of the /v1 API. Public, and cacheable for an hour (`Cache-Control: public, max-age=3600` with an `ETag`).","tags":["System"],"security":[],"responses":{"200":{"description":"The OpenAPI document.","content":{"application/json":{"schema":{"type":"object"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"304":{"description":"Not modified (the `If-None-Match` ETag still matches).","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}},"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]}},"/v1/projects":{"get":{"operationId":"listProjects","summary":"List projects","description":"Lists the org's projects, newest first. Only keys minted by the CLI device flow (`headband login`) hold this permission. It is in no default set, and POST /v1/keys can never grant it.","tags":["Projects"],"responses":{"200":{"description":"The org's projects.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"projects":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"engine":{"type":"string","description":"The project's default engine."},"prefix":{"type":"string","description":"The engine-side index prefix, `hbp_<id>_`."},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","engine","prefix","createdAt"]}}},"required":["projects"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (no `projects.manage`), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required` (a key limited to chosen indexes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"security":[{"adminKey":[]}],"x-headband-permissions":["projects.manage"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]},"post":{"operationId":"createProject","summary":"Create a project","description":"Creates a project and its `main` branch. Only keys minted by the CLI device flow (`headband login`) hold this permission. It is in no default set, and POST /v1/keys can never grant it. `engineConfig` (bring your own engine) is refused here: connect your own engine in the dashboard. The plan's project limit is checked first and answers 429 `usage_limit_exceeded`.","tags":["Projects"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Trimmed."},"engine":{"type":"string","enum":["meilisearch","typesense","elasticsearch","visor"],"default":"meilisearch"}},"required":["name"]}}}},"responses":{"201":{"description":"The project.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"engine":{"type":"string","description":"The project's default engine."},"prefix":{"type":"string","description":"The engine-side index prefix, `hbp_<id>_`."},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","engine","prefix","createdAt"]}},"required":["project"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Invalid request. Codes: `invalid_json`, `invalid_name`, `invalid_engine`, `engine_config_not_supported`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (no `projects.manage`), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required` (a key limited to chosen indexes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"description":"Rate limited, or the plan's project limit is reached (`usage_limit_exceeded`, with `plan`, `current`, `limit` and `upgradeUrl`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Codes: `engine_not_available` (the engine is not offered yet), `auth_lookup_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"security":[{"adminKey":[]}],"x-headband-permissions":["projects.manage"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]}},"/v1/projects/{projectId}":{"delete":{"operationId":"deleteProject","summary":"Delete a project","description":"Deletes a project with its branches and keys. Only keys minted by the CLI device flow (`headband login`) hold this permission. It is in no default set, and POST /v1/keys can never grant it. Refused with 409 `project_not_empty` while the project has any index or live dedicated engine, `force=true` included. `force=true` also deletes what the project's shared engine still holds that no index row knows.","tags":["Projects"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"projectId","in":"path","required":true,"description":"The project's id.","schema":{"type":"string"}},{"name":"force","in":"query","required":false,"description":"`true` also deletes leftover engine-side indexes on the project's shared engine.","schema":{"type":"string","enum":["true"]}}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"const":true},"id":{"type":"string"}},"required":["deleted","id"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (no `projects.manage`), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required` (a key limited to chosen indexes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"No such project in this org (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The project is not empty. Codes: `project_not_empty`, `index_migrating`, `instance_indexes_present` (the last two with `force=true`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"security":[{"adminKey":[]}],"x-headband-permissions":["projects.manage"],"x-headband-key-types":["admin"]}},"/v1/relevance/events":{"post":{"operationId":"sendRelevanceEvents","summary":"Send click and conversion events","description":"Records `search_result_click` (or `click`) and `conversion` events for click feedback. Up to 100 events naming up to 10 indexes per request; each event is checked on its own, and refused ones are listed in `rejected`. Pass the search's `queryId` to attribute an event. Search keys are also limited to 120 requests a minute per client address, and their events from crawler user agents are rejected as `bot`. Only for organizations with click feedback.","tags":["Analytics"],"security":[{"adminKey":[]},{"searchKey":[]}],"x-headband-permissions":["analytics.write"],"x-headband-key-types":["admin","search"],"x-headband-availability":"Organizations with click feedback enabled.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["events"],"properties":{"events":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","required":["type","index","docId"],"properties":{"type":{"type":"string","enum":["search_result_click","click","conversion"]},"index":{"type":"string","minLength":1,"maxLength":256,"description":"The index uid."},"docId":{"oneOf":[{"type":"string","minLength":1},{"type":"number"}]},"queryId":{"type":"string","description":"The `queryId` of the search the event follows."},"position":{"type":"integer","minimum":1,"maximum":1000},"sessionId":{"type":"string","minLength":1,"maxLength":128},"value":{"type":"number","minimum":0}}}}}}}}},"responses":{"202":{"description":"Accepted events, and the ones refused with why.","content":{"application/json":{"schema":{"type":"object","required":["accepted","rejected"],"properties":{"accepted":{"type":"integer"},"rejected":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","description":"Position in the request's `events` array."},"reason":{"type":"string","enum":["invalid_event","invalid_type","invalid_index","invalid_doc_id","invalid_query_id","invalid_position","invalid_session_id","invalid_value","index_not_found","bot"]}}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad request. Codes: `invalid_json`, `invalid_events`, `too_many_events`, `too_many_indexes`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (with `requiredPermission`), `branch_not_allowed` (a branch key naming another branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Click feedback is not enabled for this organization (`click_feedback_not_enabled`), or the branch is unknown (`branch_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Click feedback is not set up on this deployment yet (`click_feedback_unavailable`), or the key lookup failed (`auth_lookup_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}}}},"/v1/schema/alias":{"patch":{"operationId":"setSchemaAlias","summary":"Flip an index's alias","description":"Points a logical index uid at a physical engine index, the last step of a reindex (`headband schema apply`). A non-null target must match the index's schema state (status `migrating` or `applied`) and be an index on the same engine; the flip marks that state `applied`. `physicalUid: null` clears the alias, routing back to the logical uid. After a flip, the physical index the uid served before is dropped in the background once nothing else serves it, so flipping back to it later has nothing to serve. Synced indexes are refused. Needs `schema.manage`, which only keys minted by `headband login` (the CLI device flow) hold.","tags":["Schema"],"security":[{"adminKey":[]}],"x-headband-permissions":["schema.manage"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["indexUid","physicalUid"],"properties":{"indexUid":{"type":"string","description":"The logical index uid."},"physicalUid":{"type":["string","null"],"description":"The physical index to serve the uid from (a valid index uid), or null to clear."}}}}}},"responses":{"200":{"description":"The alias is set.","content":{"application/json":{"schema":{"type":"object","required":["indexUid","physicalUid"],"properties":{"indexUid":{"type":"string"},"physicalUid":{"type":["string","null"]}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_json`, `invalid_body`, `invalid_physical_uid`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Codes: `permission_denied` (no `schema.manage`), `branch_not_allowed`, `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `index_not_found`, `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `alias_target_mismatch`, `index_managed_by_sync`, `index_ambiguous`, `index_name_taken` (a synced index's build name), `index_changed` (retry).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/schema/state":{"get":{"operationId":"getSchemaState","summary":"Read schema state","description":"The applied schema state of indexes in the request's branch scope: the org's primary branches without Headband-Branch, else the branches it names. With `indexUid`, one state object (or 404); without, an array of every state in scope. Needs `schema.manage`, which only keys minted by `headband login` (the CLI device flow) hold.","tags":["Schema"],"security":[{"adminKey":[]}],"x-headband-permissions":["schema.manage"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"indexUid","in":"query","required":false,"description":"Return only this index's state.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"One state with `indexUid`, else an array of them.","content":{"application/json":{"schema":{"oneOf":[{"type":"object","required":["indexUid","version","schemaHash","settingsHash","physicalUid","status","backfillCursor"],"properties":{"indexUid":{"type":"string","description":"The logical index uid."},"version":{"type":"integer"},"schemaHash":{"type":"string"},"settingsHash":{"type":"string"},"physicalUid":{"type":"string","description":"The physical index this state describes."},"status":{"type":"string","enum":["applied","migrating","failed"]},"backfillCursor":{"type":["string","null"]}}},{"type":"array","items":{"type":"object","required":["indexUid","version","schemaHash","settingsHash","physicalUid","status","backfillCursor"],"properties":{"indexUid":{"type":"string","description":"The logical index uid."},"version":{"type":"integer"},"schemaHash":{"type":"string"},"settingsHash":{"type":"string"},"physicalUid":{"type":"string","description":"The physical index this state describes."},"status":{"type":"string","enum":["applied","migrating","failed"]},"backfillCursor":{"type":["string","null"]}}}}]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Code: `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Codes: `permission_denied` (no `schema.manage`), `branch_not_allowed`, `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `schema_state_not_found`, `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Code: `index_ambiguous` (name one branch with Headband-Branch).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"post":{"operationId":"putSchemaState","summary":"Write schema state","description":"Compare-and-set upsert of an index's schema state. `expectedParentHash` must be the stored `schemaHash`, or null when no state exists yet; anything else is 409 `schema_cas_conflict`. A new state lands on its index's branch when the scope holds the index, else on the branch an index create would land on. Answers 200 for both an insert and an update. Needs `schema.manage`, which only keys minted by `headband login` (the CLI device flow) hold.","tags":["Schema"],"security":[{"adminKey":[]}],"x-headband-permissions":["schema.manage"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["indexUid","version","schemaHash","settingsHash","physicalUid","status","expectedParentHash"],"properties":{"indexUid":{"type":"string","minLength":1},"version":{"type":"integer"},"schemaHash":{"type":"string","minLength":1},"settingsHash":{"type":"string","minLength":1},"physicalUid":{"type":"string","minLength":1},"status":{"type":"string","enum":["applied","migrating","failed"]},"backfillCursor":{"type":["string","null"],"description":"Omit or null for none."},"expectedParentHash":{"type":["string","null"],"description":"The current stored `schemaHash`, or null to expect no state yet. Must be present."}}}}}},"responses":{"200":{"description":"The stored state.","content":{"application/json":{"schema":{"type":"object","required":["indexUid","version","schemaHash","settingsHash","physicalUid","status","backfillCursor"],"properties":{"indexUid":{"type":"string","description":"The logical index uid."},"version":{"type":"integer"},"schemaHash":{"type":"string"},"settingsHash":{"type":"string"},"physicalUid":{"type":"string","description":"The physical index this state describes."},"status":{"type":"string","enum":["applied","migrating","failed"]},"backfillCursor":{"type":["string","null"]}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_json`, `invalid_body`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Code: `plan_project_limit` (a first write would add a Default project over the plan's limit).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"403":{"description":"Codes: `permission_denied` (no `schema.manage`), `branch_not_allowed`, `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `branch_not_found`, `project_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `schema_cas_conflict` (re-read and retry), `index_ambiguous`, `branch_deleting`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}},"delete":{"operationId":"deleteSchemaState","summary":"Forget schema state","description":"Deletes an index's schema state in the request's branch scope, for an index removed outside `headband schema apply`. The index itself is not touched. Needs `schema.manage`, which only keys minted by `headband login` (the CLI device flow) hold.","tags":["Schema"],"security":[{"adminKey":[]}],"x-headband-permissions":["schema.manage"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"indexUid","in":"query","required":true,"description":"The index whose state to forget.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The state is deleted.","content":{"application/json":{"schema":{"type":"object","required":["indexUid","deleted"],"properties":{"indexUid":{"type":"string"},"deleted":{"type":"boolean","const":true}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_request` (no `indexUid`), `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Codes: `permission_denied` (no `schema.manage`), `branch_not_allowed`, `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `schema_state_not_found`, `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Code: `index_ambiguous` (name one branch with Headband-Branch).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/search":{"post":{"operationId":"search","summary":"Search one index","description":"Searches one index on whichever engine powers it. Search parameters other than `index`, `q`, `experimentKey`, `variant` and `sessionId` are passed to the engine. Paging works the same on every engine: `limit` or page mode (`page`, `hitsPerPage`), capped at the engine's most per request. Under a tenant token only the token's indexes are reachable and its filter is forced. Each search counts toward the plan's monthly searches and searches a second.","tags":["Search"],"security":[{"adminKey":[]},{"searchKey":[]},{"tenantToken":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin","search","tenant"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"$ref":"#/components/parameters/HeadbandBranch"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["index"],"properties":{"index":{"type":"string","description":"The index uid."},"q":{"type":"string","description":"The query. Empty or missing searches everything."},"filter":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Filter expression. Under a tenant token the token's filter for this index is AND-ed with it; a filter that cannot be combined safely is a 400 `invalid_filter`."},"sort":{"type":"array","items":{"type":"string"}},"facets":{"type":"array","items":{"type":"string"}},"limit":{"type":"integer","minimum":0,"default":20,"description":"Hits to return. Above the engine's most per request (Typesense 250, Elasticsearch 10,000, Visor 1000, Meilisearch its `pagination.maxTotalHits`) it is lowered, and the answer carries `limitCapped: true`."},"offset":{"type":"integer","minimum":0},"page":{"type":"integer","minimum":1,"description":"Page mode: send `page` and/or `hitsPerPage`. Works on every engine."},"hitsPerPage":{"type":"integer","minimum":0,"default":20},"attributesToHighlight":{"type":"array","items":{"type":"string"}},"attributesToRetrieve":{"type":"array","items":{"type":"string"}},"attributesToCrop":{"type":"array","items":{"type":"string"}},"cropLength":{"type":"integer"},"showMatchesPosition":{"type":"boolean"},"showRankingScore":{"type":"boolean"},"matchingStrategy":{"type":"string","enum":["all","last","frequency"]},"hybrid":{"type":"object","properties":{"semanticRatio":{"type":"number"},"embedder":{"type":"string"}}},"vector":{"type":"array","items":{"type":"number"}},"mode":{"type":"string","enum":["instant","retrieval"],"description":"Visor only: `instant` (the default) or `retrieval` (BM25F). Other engines ignore it."},"experimentKey":{"type":"string","description":"Recorded with the search's analytics."},"variant":{"type":"string","description":"Recorded with the search's analytics."},"sessionId":{"type":"string","description":"Recorded with the search's analytics."}},"additionalProperties":true}}}},"responses":{"200":{"description":"The engine's answer, with Headband's paging fields.","content":{"application/json":{"schema":{"type":"object","required":["hits","query","processingTimeMs"],"properties":{"hits":{"type":"array","items":{"type":"object","additionalProperties":true}},"query":{"type":"string"},"processingTimeMs":{"type":"number"},"estimatedTotalHits":{"type":"integer"},"totalHits":{"type":"integer"},"totalHitsExact":{"type":"boolean","description":"False when the total is a lower bound (Elasticsearch past 10,000, Visor past 1000)."},"maxTotalHits":{"type":"integer","description":"Present when the total is past what paging can reach: the hits paging stops at."},"limitCapped":{"type":"boolean","description":"True when `limit` or `hitsPerPage` was lowered to the engine's most per request; that field then holds the value used."},"limit":{"type":"integer"},"page":{"type":"integer","description":"Page mode only."},"hitsPerPage":{"type":"integer","description":"Page mode only."},"totalPages":{"type":"integer","description":"Page mode only. Counts only pages that can be fetched."},"facetDistribution":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"number"}}},"facetStats":{"type":"object","additionalProperties":{"type":"object","properties":{"min":{"type":"number"},"max":{"type":"number"}}}},"queryId":{"type":"string","description":"Only for organizations with click feedback. Send it back on /v1/relevance/events to attribute clicks to this search."}},"additionalProperties":true}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"X-Headband-Click-Feedback":{"description":"Only for organizations with click feedback: `applied` when results were re-ranked by clicks, else why not (`off`, `empty_query`, `sorted`, `paged`, `deep_page`, `too_many`, `invalid`).","schema":{"type":"string","enum":["applied","off","empty_query","sorted","paged","deep_page","too_many","invalid"]}}}},"400":{"description":"Bad request. Codes include `invalid_json`, `index_required`, `invalid_index_uid`, `invalid_branch_name`, `invalid_filter`, `invalid_search_filter`, `invalid_search_sort`, `invalid_search_facets`, `invalid_search_offset`, `invalid_search_page`, `invalid_search_hits_per_page`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied` (with `requiredPermission`), `branch_not_allowed` (a branch key naming another branch), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"The index is not in the request's scope (`index_not_found`), or the Headband-Branch names no live branch (`branch_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The uid names an index on more than one branch in scope (`index_ambiguous`). Pick one with Headband-Branch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"description":"Rate limit or the plan's monthly search cap. Codes: `rate_limited` (the plan's searches a second), `usage_limit_exceeded` (monthly cap, with `plan`, `current`, `limit`, `resetsAt`, `upgradeUrl`; `Retry-After` counts to the reset). The per-key and per-IP request limits answer with no code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"description":"The engine or an auth lookup is unavailable for now. Codes include `auth_lookup_failed`, `engine_not_configured`, `instance_waking`, `index_migrating` (with `Retry-After`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}},"/v1/sync/indexes":{"get":{"operationId":"syncListIndexes","summary":"List synced indexes","description":"Lists the org's synced indexes, oldest first, with recent freshness. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"source","in":"query","required":false,"description":"Only this source's indexes.","schema":{"type":"string"}},{"name":"instance","in":"query","required":false,"description":"Only this engine instance's indexes.","schema":{"type":"string"}}],"responses":{"200":{"description":"The synced indexes.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"allOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sourceId":{"type":"string"},"instanceId":{"type":"string"},"state":{"type":"string"},"desired":{"type":"string","enum":["active","paused"]},"definitionVersion":{"type":"integer"},"servingDefinitionVersion":{"type":["integer","null"],"description":"The version the box serves. Null until it reports."},"definitionPending":{"type":"boolean","description":"A saved version waits for a rebuild."},"physicalName":{"type":"string"},"appliedLsn":{"type":["string","null"]},"renderLsnMax":{"type":["string","null"]},"lagMs":{"type":["integer","null"]},"lagBytes":{"type":["integer","null"]},"documents":{"type":["integer","null"]},"deadLetters":{"type":"integer"},"lastAppliedAt":{"type":["string","null"],"format":"date-time"},"lastCheckAt":{"type":["string","null"],"format":"date-time"},"lastCheckDrifted":{"type":["integer","null"]},"rebuildRequestedAt":{"type":["string","null"],"format":"date-time"},"build":{"oneOf":[{"type":"object","properties":{"trigger":{"type":"string"},"phase":{"type":"string"},"rendered":{"type":"integer"},"startedAt":{"type":"string"}}},{"type":"null"}]},"hold":{"oneOf":[{"type":"object","properties":{"reason":{"type":"string"},"since":{"type":"string"}}},{"type":"null"}]},"errorCode":{"type":["string","null"]},"error":{"type":["string","null"]},"pendingChanges":{"type":"boolean","description":"A change has not reached the box yet."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},{"type":"object","properties":{"freshnessP95Ms":{"type":["number","null"],"description":"Worst p95 freshness of the last two hours."}}}]}}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."},"post":{"operationId":"syncCreateIndex","summary":"Create a synced index","description":"Creates a synced index from a definition. The whole body is the definition. A source can have 50 synced indexes. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"A synced index definition, as in a `*.sync.json` file. At most 256 KiB. The box's own parser checks its structure.","properties":{"name":{"type":"string","description":"The index name. Cannot change after create."},"instance":{"type":"string","description":"The engine instance id. Cannot change after create."},"source":{"type":"string","description":"The source id. Cannot change after create."},"root":{"type":"object","properties":{"table":{"type":"string"},"key":{"type":"string"}},"required":["table","key"]},"render":{"type":"string","description":"The SQL that renders one document."},"dependencies":{"type":"array","items":{"type":"object"}},"engine":{"type":"object","description":"Engine settings, in the instance engine's own vocabulary."}},"required":["name","instance","source","root","render","dependencies"]}}}},"responses":{"201":{"description":"The synced index.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sourceId":{"type":"string"},"instanceId":{"type":"string"},"state":{"type":"string"},"desired":{"type":"string","enum":["active","paused"]},"definitionVersion":{"type":"integer"},"servingDefinitionVersion":{"type":["integer","null"],"description":"The version the box serves. Null until it reports."},"definitionPending":{"type":"boolean","description":"A saved version waits for a rebuild."},"physicalName":{"type":"string"},"appliedLsn":{"type":["string","null"]},"renderLsnMax":{"type":["string","null"]},"lagMs":{"type":["integer","null"]},"lagBytes":{"type":["integer","null"]},"documents":{"type":["integer","null"]},"deadLetters":{"type":"integer"},"lastAppliedAt":{"type":["string","null"],"format":"date-time"},"lastCheckAt":{"type":["string","null"],"format":"date-time"},"lastCheckDrifted":{"type":["integer","null"]},"rebuildRequestedAt":{"type":["string","null"],"format":"date-time"},"build":{"oneOf":[{"type":"object","properties":{"trigger":{"type":"string"},"phase":{"type":"string"},"rendered":{"type":"integer"},"startedAt":{"type":"string"}}},{"type":"null"}]},"hold":{"oneOf":[{"type":"object","properties":{"reason":{"type":"string"},"since":{"type":"string"}}},{"type":"null"}]},"errorCode":{"type":["string","null"]},"error":{"type":["string","null"]},"pendingChanges":{"type":"boolean","description":"A change has not reached the box yet."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"definition":{"type":"object","description":"A synced index definition, as in a `*.sync.json` file. At most 256 KiB. The box's own parser checks its structure.","properties":{"name":{"type":"string","description":"The index name. Cannot change after create."},"instance":{"type":"string","description":"The engine instance id. Cannot change after create."},"source":{"type":"string","description":"The source id. Cannot change after create."},"root":{"type":"object","properties":{"table":{"type":"string"},"key":{"type":"string"}},"required":["table","key"]},"render":{"type":"string","description":"The SQL that renders one document."},"dependencies":{"type":"array","items":{"type":"object"}},"engine":{"type":"object","description":"Engine settings, in the instance engine's own vocabulary."}},"required":["name","instance","source","root","render","dependencies"]}}}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Invalid JSON (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `source_not_found`, `instance_not_found`, `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `source_deleting`, `index_uid_taken`, `index_name_taken`, `index_changed`, `branch_deleting`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"Codes: `invalid_definition` (with `issues`), `index_limit_reached`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]}},"/v1/sync/indexes/validate":{"post":{"operationId":"syncValidateIndex","summary":"Validate a definition","description":"Checks a definition without saving it. An invalid definition is a 200 with `valid: false`. With `syncIndexId`, also classifies the change against that index. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"definition":{"type":"object","description":"A synced index definition, as in a `*.sync.json` file. At most 256 KiB. The box's own parser checks its structure.","properties":{"name":{"type":"string","description":"The index name. Cannot change after create."},"instance":{"type":"string","description":"The engine instance id. Cannot change after create."},"source":{"type":"string","description":"The source id. Cannot change after create."},"root":{"type":"object","properties":{"table":{"type":"string"},"key":{"type":"string"}},"required":["table","key"]},"render":{"type":"string","description":"The SQL that renders one document."},"dependencies":{"type":"array","items":{"type":"object"}},"engine":{"type":"object","description":"Engine settings, in the instance engine's own vocabulary."}},"required":["name","instance","source","root","render","dependencies"]},"syncIndexId":{"type":"string","description":"An existing synced index to compare against."}}}}}},"responses":{"200":{"description":"The report.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"valid":{"type":"boolean"},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}},"required":["code","message"]}},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}},"required":["code","message"]}},"classification":{"oneOf":[{"type":"object","properties":{"class":{"type":"string","enum":["none","live","repair","rebuild"]},"reasons":{"type":"array","items":{"type":"string"}}},"required":["class","reasons"]},{"type":"null"}]},"checkedOnBox":{"type":"boolean","description":"The box ran the source-side checks."}},"required":["valid","errors","warnings","classification","checkedOnBox"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_json`, `invalid_body`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `source_not_found`, `instance_not_found`, `sync_index_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]}},"/v1/sync/indexes/{id}":{"get":{"operationId":"syncGetIndex","summary":"Get a synced index","description":"One synced index with its definition. With `version`, returns that stored definition version instead. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The synced index id.","schema":{"type":"string"}},{"name":"version","in":"query","required":false,"description":"A stored definition version.","schema":{"type":"integer","minimum":1,"maximum":2147483647}}],"responses":{"200":{"description":"The synced index, or the definition version when `version` is set.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"oneOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sourceId":{"type":"string"},"instanceId":{"type":"string"},"state":{"type":"string"},"desired":{"type":"string","enum":["active","paused"]},"definitionVersion":{"type":"integer"},"servingDefinitionVersion":{"type":["integer","null"],"description":"The version the box serves. Null until it reports."},"definitionPending":{"type":"boolean","description":"A saved version waits for a rebuild."},"physicalName":{"type":"string"},"appliedLsn":{"type":["string","null"]},"renderLsnMax":{"type":["string","null"]},"lagMs":{"type":["integer","null"]},"lagBytes":{"type":["integer","null"]},"documents":{"type":["integer","null"]},"deadLetters":{"type":"integer"},"lastAppliedAt":{"type":["string","null"],"format":"date-time"},"lastCheckAt":{"type":["string","null"],"format":"date-time"},"lastCheckDrifted":{"type":["integer","null"]},"rebuildRequestedAt":{"type":["string","null"],"format":"date-time"},"build":{"oneOf":[{"type":"object","properties":{"trigger":{"type":"string"},"phase":{"type":"string"},"rendered":{"type":"integer"},"startedAt":{"type":"string"}}},{"type":"null"}]},"hold":{"oneOf":[{"type":"object","properties":{"reason":{"type":"string"},"since":{"type":"string"}}},{"type":"null"}]},"errorCode":{"type":["string","null"]},"error":{"type":["string","null"]},"pendingChanges":{"type":"boolean","description":"A change has not reached the box yet."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"definition":{"type":"object","description":"A synced index definition, as in a `*.sync.json` file. At most 256 KiB. The box's own parser checks its structure.","properties":{"name":{"type":"string","description":"The index name. Cannot change after create."},"instance":{"type":"string","description":"The engine instance id. Cannot change after create."},"source":{"type":"string","description":"The source id. Cannot change after create."},"root":{"type":"object","properties":{"table":{"type":"string"},"key":{"type":"string"}},"required":["table","key"]},"render":{"type":"string","description":"The SQL that renders one document."},"dependencies":{"type":"array","items":{"type":"object"}},"engine":{"type":"object","description":"Engine settings, in the instance engine's own vocabulary."}},"required":["name","instance","source","root","render","dependencies"]}}},{"type":"object","properties":{"version":{"type":"integer"},"definition":{"type":"object","description":"A synced index definition, as in a `*.sync.json` file. At most 256 KiB. The box's own parser checks its structure.","properties":{"name":{"type":"string","description":"The index name. Cannot change after create."},"instance":{"type":"string","description":"The engine instance id. Cannot change after create."},"source":{"type":"string","description":"The source id. Cannot change after create."},"root":{"type":"object","properties":{"table":{"type":"string"},"key":{"type":"string"}},"required":["table","key"]},"render":{"type":"string","description":"The SQL that renders one document."},"dependencies":{"type":"array","items":{"type":"object"}},"engine":{"type":"object","description":"Engine settings, in the instance engine's own vocabulary."}},"required":["name","instance","source","root","render","dependencies"]},"createdBy":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["version","definition","createdBy","createdAt"]}]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"`version` is not a positive integer (`invalid_version`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `sync_index_not_found`, `version_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."},"patch":{"operationId":"syncUpdateIndex","summary":"Update a synced index","description":"Saves a new definition version when it differs, and/or requests a rebuild. Every change takes effect at the next rebuild. Name, instance and source cannot change. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The synced index id.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"definition":{"type":"object","description":"A synced index definition, as in a `*.sync.json` file. At most 256 KiB. The box's own parser checks its structure.","properties":{"name":{"type":"string","description":"The index name. Cannot change after create."},"instance":{"type":"string","description":"The engine instance id. Cannot change after create."},"source":{"type":"string","description":"The source id. Cannot change after create."},"root":{"type":"object","properties":{"table":{"type":"string"},"key":{"type":"string"}},"required":["table","key"]},"render":{"type":"string","description":"The SQL that renders one document."},"dependencies":{"type":"array","items":{"type":"object"}},"engine":{"type":"object","description":"Engine settings, in the instance engine's own vocabulary."}},"required":["name","instance","source","root","render","dependencies"]},"rebuild":{"type":"boolean"}},"minProperties":1,"description":"At least one of `definition` and `rebuild`."}}}},"responses":{"200":{"description":"The synced index.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sourceId":{"type":"string"},"instanceId":{"type":"string"},"state":{"type":"string"},"desired":{"type":"string","enum":["active","paused"]},"definitionVersion":{"type":"integer"},"servingDefinitionVersion":{"type":["integer","null"],"description":"The version the box serves. Null until it reports."},"definitionPending":{"type":"boolean","description":"A saved version waits for a rebuild."},"physicalName":{"type":"string"},"appliedLsn":{"type":["string","null"]},"renderLsnMax":{"type":["string","null"]},"lagMs":{"type":["integer","null"]},"lagBytes":{"type":["integer","null"]},"documents":{"type":["integer","null"]},"deadLetters":{"type":"integer"},"lastAppliedAt":{"type":["string","null"],"format":"date-time"},"lastCheckAt":{"type":["string","null"],"format":"date-time"},"lastCheckDrifted":{"type":["integer","null"]},"rebuildRequestedAt":{"type":["string","null"],"format":"date-time"},"build":{"oneOf":[{"type":"object","properties":{"trigger":{"type":"string"},"phase":{"type":"string"},"rendered":{"type":"integer"},"startedAt":{"type":"string"}}},{"type":"null"}]},"hold":{"oneOf":[{"type":"object","properties":{"reason":{"type":"string"},"since":{"type":"string"}}},{"type":"null"}]},"errorCode":{"type":["string","null"]},"error":{"type":["string","null"]},"pendingChanges":{"type":"boolean","description":"A change has not reached the box yet."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"definition":{"type":"object","description":"A synced index definition, as in a `*.sync.json` file. At most 256 KiB. The box's own parser checks its structure.","properties":{"name":{"type":"string","description":"The index name. Cannot change after create."},"instance":{"type":"string","description":"The engine instance id. Cannot change after create."},"source":{"type":"string","description":"The source id. Cannot change after create."},"root":{"type":"object","properties":{"table":{"type":"string"},"key":{"type":"string"}},"required":["table","key"]},"render":{"type":"string","description":"The SQL that renders one document."},"dependencies":{"type":"array","items":{"type":"object"}},"engine":{"type":"object","description":"Engine settings, in the instance engine's own vocabulary."}},"required":["name","instance","source","root","render","dependencies"]}}},"saved":{"type":"boolean","description":"A new definition version was saved."},"classification":{"type":"object","properties":{"class":{"type":"string","enum":["none","live","repair","rebuild"]},"reasons":{"type":"array","items":{"type":"string"}}},"required":["class","reasons"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_json`, `invalid_body`, `empty_update`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `sync_index_not_found`, `instance_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"The definition is not valid (`invalid_definition`, with `issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."},"delete":{"operationId":"syncDeleteIndex","summary":"Delete a synced index","description":"Stops syncing the index. By default the engine index stays and becomes a plain index. With `dropIndex=1` the engine index is deleted too: 202 until the box has let go of it, and the same call again finishes it. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The synced index id.","schema":{"type":"string"}},{"name":"dropIndex","in":"query","required":false,"description":"`1` also deletes the engine index.","schema":{"type":"string","enum":["1"]}}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"const":true},"indexDropped":{"type":"boolean"},"claimed":{"type":"boolean"}},"required":["id","deleted","indexDropped","claimed"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"202":{"description":"The drop is under way (`indexDropped` false). Call again to finish.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"const":true},"indexDropped":{"const":false},"claimed":{"type":"boolean"}},"required":["id","deleted","indexDropped","claimed"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `sync_index_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"A drop has already claimed the engine index (`index_dropping`). Delete with `dropIndex=1` to finish.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."}},"/v1/sync/indexes/{id}/check":{"post":{"operationId":"syncCheckIndex","summary":"Check a synced index for drift","description":"Not available yet: after the org check, always answers 501 `not_in_this_release`. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The synced index id.","schema":{"type":"string"}}],"responses":{"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `sync_index_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"501":{"description":"Drift checks arrive in a later release (`not_in_this_release`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."}},"/v1/sync/indexes/{id}/jobs":{"get":{"operationId":"syncListIndexJobs","summary":"List a synced index's jobs","description":"The index's backfill, rebuild and check jobs, newest first. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The synced index id.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"How many. Above 200 is cut to 200.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"The jobs.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","description":"backfill, rebuild, check_sample, check_full or repair."},"trigger":{"type":"string"},"state":{"type":"string","description":"running, succeeded, failed or cancelled."},"stats":{},"error":{"type":["string","null"]},"startedAt":{"type":"string","format":"date-time"},"finishedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","kind","trigger","state","stats","error","startedAt","finishedAt"]}}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `sync_index_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."}},"/v1/sync/indexes/{id}/pause":{"post":{"operationId":"syncPauseIndex","summary":"Pause a synced index","description":"Stops applying changes to the index. The replication slot keeps running. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The synced index id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The synced index.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sourceId":{"type":"string"},"instanceId":{"type":"string"},"state":{"type":"string"},"desired":{"type":"string","enum":["active","paused"]},"definitionVersion":{"type":"integer"},"servingDefinitionVersion":{"type":["integer","null"],"description":"The version the box serves. Null until it reports."},"definitionPending":{"type":"boolean","description":"A saved version waits for a rebuild."},"physicalName":{"type":"string"},"appliedLsn":{"type":["string","null"]},"renderLsnMax":{"type":["string","null"]},"lagMs":{"type":["integer","null"]},"lagBytes":{"type":["integer","null"]},"documents":{"type":["integer","null"]},"deadLetters":{"type":"integer"},"lastAppliedAt":{"type":["string","null"],"format":"date-time"},"lastCheckAt":{"type":["string","null"],"format":"date-time"},"lastCheckDrifted":{"type":["integer","null"]},"rebuildRequestedAt":{"type":["string","null"],"format":"date-time"},"build":{"oneOf":[{"type":"object","properties":{"trigger":{"type":"string"},"phase":{"type":"string"},"rendered":{"type":"integer"},"startedAt":{"type":"string"}}},{"type":"null"}]},"hold":{"oneOf":[{"type":"object","properties":{"reason":{"type":"string"},"since":{"type":"string"}}},{"type":"null"}]},"errorCode":{"type":["string","null"]},"error":{"type":["string","null"]},"pendingChanges":{"type":"boolean","description":"A change has not reached the box yet."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `sync_index_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."}},"/v1/sync/indexes/{id}/preview":{"post":{"operationId":"syncPreviewIndex","summary":"Preview rendered documents","description":"Renders up to 20 root ids on the box without writing anything, optionally with a draft definition. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The synced index id.","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","maxItems":20,"items":{"type":"string","minLength":1,"maxLength":256}},"definition":{"type":"object","description":"A synced index definition, as in a `*.sync.json` file. At most 256 KiB. The box's own parser checks its structure.","properties":{"name":{"type":"string","description":"The index name. Cannot change after create."},"instance":{"type":"string","description":"The engine instance id. Cannot change after create."},"source":{"type":"string","description":"The source id. Cannot change after create."},"root":{"type":"object","properties":{"table":{"type":"string"},"key":{"type":"string"}},"required":["table","key"]},"render":{"type":"string","description":"The SQL that renders one document."},"dependencies":{"type":"array","items":{"type":"object"}},"engine":{"type":"object","description":"Engine settings, in the instance engine's own vocabulary."}},"required":["name","instance","source","root","render","dependencies"]}}}}}},"responses":{"200":{"description":"The preview.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}},"required":["code","message"]}},"warnings":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"path":{"type":"string"}},"required":["code","message"]}},"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"searchable":{"type":"boolean"},"doc":{"type":["object","null"]}},"required":["id","searchable","doc"]}}},"required":["errors","warnings","documents"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_json`, `invalid_body`, `too_many_ids`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `sync_index_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The index is not running on its box yet (`not_on_box_yet`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"`invalid_definition` (with `issues`), or the box refused the preview with its own code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Codes: `box_unavailable` (with `Retry-After: 30`), `auth_lookup_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."}},"/v1/sync/indexes/{id}/rebuild":{"post":{"operationId":"syncRebuildIndex","summary":"Rebuild a synced index","description":"Requests a rebuild. The box rebuilds when it next applies the source's spec. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The synced index id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The synced index.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sourceId":{"type":"string"},"instanceId":{"type":"string"},"state":{"type":"string"},"desired":{"type":"string","enum":["active","paused"]},"definitionVersion":{"type":"integer"},"servingDefinitionVersion":{"type":["integer","null"],"description":"The version the box serves. Null until it reports."},"definitionPending":{"type":"boolean","description":"A saved version waits for a rebuild."},"physicalName":{"type":"string"},"appliedLsn":{"type":["string","null"]},"renderLsnMax":{"type":["string","null"]},"lagMs":{"type":["integer","null"]},"lagBytes":{"type":["integer","null"]},"documents":{"type":["integer","null"]},"deadLetters":{"type":"integer"},"lastAppliedAt":{"type":["string","null"],"format":"date-time"},"lastCheckAt":{"type":["string","null"],"format":"date-time"},"lastCheckDrifted":{"type":["integer","null"]},"rebuildRequestedAt":{"type":["string","null"],"format":"date-time"},"build":{"oneOf":[{"type":"object","properties":{"trigger":{"type":"string"},"phase":{"type":"string"},"rendered":{"type":"integer"},"startedAt":{"type":"string"}}},{"type":"null"}]},"hold":{"oneOf":[{"type":"object","properties":{"reason":{"type":"string"},"since":{"type":"string"}}},{"type":"null"}]},"errorCode":{"type":["string","null"]},"error":{"type":["string","null"]},"pendingChanges":{"type":"boolean","description":"A change has not reached the box yet."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `sync_index_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."}},"/v1/sync/indexes/{id}/resume":{"post":{"operationId":"syncResumeIndex","summary":"Resume a synced index","description":"Resumes applying changes to a paused index. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The synced index id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The synced index.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"sourceId":{"type":"string"},"instanceId":{"type":"string"},"state":{"type":"string"},"desired":{"type":"string","enum":["active","paused"]},"definitionVersion":{"type":"integer"},"servingDefinitionVersion":{"type":["integer","null"],"description":"The version the box serves. Null until it reports."},"definitionPending":{"type":"boolean","description":"A saved version waits for a rebuild."},"physicalName":{"type":"string"},"appliedLsn":{"type":["string","null"]},"renderLsnMax":{"type":["string","null"]},"lagMs":{"type":["integer","null"]},"lagBytes":{"type":["integer","null"]},"documents":{"type":["integer","null"]},"deadLetters":{"type":"integer"},"lastAppliedAt":{"type":["string","null"],"format":"date-time"},"lastCheckAt":{"type":["string","null"],"format":"date-time"},"lastCheckDrifted":{"type":["integer","null"]},"rebuildRequestedAt":{"type":["string","null"],"format":"date-time"},"build":{"oneOf":[{"type":"object","properties":{"trigger":{"type":"string"},"phase":{"type":"string"},"rendered":{"type":"integer"},"startedAt":{"type":"string"}}},{"type":"null"}]},"hold":{"oneOf":[{"type":"object","properties":{"reason":{"type":"string"},"since":{"type":"string"}}},{"type":"null"}]},"errorCode":{"type":["string","null"]},"error":{"type":["string","null"]},"pendingChanges":{"type":"boolean","description":"A change has not reached the box yet."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `sync_index_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."}},"/v1/sync/indexes/{id}/usage":{"get":{"operationId":"syncGetIndexUsage","summary":"Get a synced index's hourly usage","description":"Hourly usage rows for one synced index, oldest first. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The synced index id.","schema":{"type":"string"}},{"name":"hours","in":"query","required":false,"description":"How many hours back. Cut to 744.","schema":{"type":"integer","minimum":1,"maximum":744,"default":48}}],"responses":{"200":{"description":"The hourly rows.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"period":{"type":"string","description":"The hour, as `YYYY-MM-DDTHH`."},"changes":{"type":"number"},"documentsWritten":{"type":"number"},"documentsRendered":{"type":"number"},"backfillDocuments":{"type":"number"},"freshnessP50Ms":{"type":["number","null"]},"freshnessP95Ms":{"type":["number","null"]},"lagMaxMs":{"type":["number","null"]}}}}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `sync_index_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."}},"/v1/sync/sources":{"get":{"operationId":"syncListSources","summary":"List sources","description":"Lists the org's Sync sources with their pipelines, oldest first. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","responses":{"200":{"description":"The sources.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["postgres","batadb"]},"host":{"type":"string"},"port":{"type":"integer"},"database":{"type":"string"},"username":{"type":"string"},"hasPassword":{"type":"boolean"},"tlsMode":{"type":"string"},"captureMode":{"type":"string"},"publicationName":{"type":"string"},"limits":{"type":"object","properties":{"retainedWarnBytes":{"type":"integer"},"retainedCriticalBytes":{"type":"integer"},"retainedHardBytes":{"type":"integer"}},"required":["retainedWarnBytes","retainedCriticalBytes","retainedHardBytes"]},"state":{"type":"string","description":"awaiting_setup, active, paused, error or deleting."},"error":{"type":["string","null"]},"serverVersion":{"type":["integer","null"]},"lastTest":{"oneOf":[{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"boxId":{"type":"string"},"checks":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"ok":{"type":"boolean"},"detail":{"type":"string"}},"required":["name","ok","detail"]}}},"required":["at","boxId","checks"]},{"type":"null"}]},"batadb":{"oneOf":[{"type":"object","properties":{"projectId":{"type":["string","null"]},"branchId":{"type":["string","null"]},"computeId":{"type":["string","null"]},"hasApiKey":{"type":"boolean"},"roleKind":{"type":["string","null"]},"roleAckAt":{"type":["string","null"],"format":"date-time"}}},{"type":"null"}],"description":"BataDB details, for a batadb source. Null otherwise."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"hasTlsCaPem":{"type":"boolean","description":"Whether a CA bundle is set. The source's own GET returns it."},"pipelines":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"boxId":{"type":"string"},"state":{"type":"string"},"retainedBytes":{"type":["integer","null"]},"sessionOpen":{"type":"boolean"},"lastHeartbeatAt":{"type":["string","null"],"format":"date-time"},"errorCode":{"type":["string","null"]},"error":{"type":["string","null"]}},"required":["id","boxId","state","retainedBytes","sessionOpen","lastHeartbeatAt","errorCode","error"]}}},"description":"A source as listed: whether it has a CA bundle, never the bundle."}}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]},"post":{"operationId":"syncCreateSource","summary":"Create a source","description":"Adds a Postgres or BataDB database to sync from. The connection URL and secrets are stored encrypted and never returned. An org can have 10 sources. Unknown fields are refused. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"},"url":{"type":"string","maxLength":2048,"description":"A postgres:// or postgresql:// URL to a direct (not pooled) endpoint."},"kind":{"type":"string","enum":["postgres","batadb"],"default":"postgres"},"tlsMode":{"type":"string","enum":["verify-full","require"],"description":"Default: from the URL's sslmode, else verify-full."},"tlsCaPem":{"type":["string","null"],"description":"A PEM bundle of certificates only, at most 256 KiB. Needs tlsMode verify-full."},"captureMode":{"type":"string","enum":["stream"],"default":"stream"},"limits":{"type":"object","properties":{"retainedWarnBytes":{"type":"integer"},"retainedCriticalBytes":{"type":"integer"},"retainedHardBytes":{"type":"integer"}}},"batadb":{"type":"object","properties":{"projectId":{"type":"string"},"branchId":{"type":"string"},"computeId":{"type":"string"},"apiKey":{"type":"string","minLength":8,"maxLength":512},"acceptBroadRole":{"const":true}},"required":["projectId","branchId","computeId","acceptBroadRole"]}},"required":["name","url"],"additionalProperties":false}}}},"responses":{"201":{"description":"The source, in awaiting_setup.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["postgres","batadb"]},"host":{"type":"string"},"port":{"type":"integer"},"database":{"type":"string"},"username":{"type":"string"},"hasPassword":{"type":"boolean"},"tlsMode":{"type":"string"},"captureMode":{"type":"string"},"publicationName":{"type":"string"},"limits":{"type":"object","properties":{"retainedWarnBytes":{"type":"integer"},"retainedCriticalBytes":{"type":"integer"},"retainedHardBytes":{"type":"integer"}},"required":["retainedWarnBytes","retainedCriticalBytes","retainedHardBytes"]},"state":{"type":"string","description":"awaiting_setup, active, paused, error or deleting."},"error":{"type":["string","null"]},"serverVersion":{"type":["integer","null"]},"lastTest":{"oneOf":[{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"boxId":{"type":"string"},"checks":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"ok":{"type":"boolean"},"detail":{"type":"string"}},"required":["name","ok","detail"]}}},"required":["at","boxId","checks"]},{"type":"null"}]},"batadb":{"oneOf":[{"type":"object","properties":{"projectId":{"type":["string","null"]},"branchId":{"type":["string","null"]},"computeId":{"type":["string","null"]},"hasApiKey":{"type":"boolean"},"roleKind":{"type":["string","null"]},"roleAckAt":{"type":["string","null"],"format":"date-time"}}},{"type":"null"}],"description":"BataDB details, for a batadb source. Null otherwise."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tlsCaPem":{"type":["string","null"],"description":"The CA bundle, which is public data. Null: system roots."}},"description":"A source. Never the connection URL, the password or the BataDB API key."}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_json`, `invalid_body`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"A source with this name exists (`source_name_taken`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"Codes: `invalid_name`, `invalid_source_url`, `invalid_tls_mode`, `tls_mode_not_available`, `invalid_ca_pem`, `ca_has_private_key`, `ca_needs_verify_full`, `invalid_limits`, `source_host_private`, `source_host_unresolvable`, `invalid_kind`, `invalid_capture_mode`, `capture_mode_not_available`, `consent_required`, `source_limit_reached`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]}},"/v1/sync/sources/{id}":{"get":{"operationId":"syncGetSource","summary":"Get a source","description":"One source with its pipelines and its CA bundle. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The source id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The source.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["postgres","batadb"]},"host":{"type":"string"},"port":{"type":"integer"},"database":{"type":"string"},"username":{"type":"string"},"hasPassword":{"type":"boolean"},"tlsMode":{"type":"string"},"captureMode":{"type":"string"},"publicationName":{"type":"string"},"limits":{"type":"object","properties":{"retainedWarnBytes":{"type":"integer"},"retainedCriticalBytes":{"type":"integer"},"retainedHardBytes":{"type":"integer"}},"required":["retainedWarnBytes","retainedCriticalBytes","retainedHardBytes"]},"state":{"type":"string","description":"awaiting_setup, active, paused, error or deleting."},"error":{"type":["string","null"]},"serverVersion":{"type":["integer","null"]},"lastTest":{"oneOf":[{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"boxId":{"type":"string"},"checks":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"ok":{"type":"boolean"},"detail":{"type":"string"}},"required":["name","ok","detail"]}}},"required":["at","boxId","checks"]},{"type":"null"}]},"batadb":{"oneOf":[{"type":"object","properties":{"projectId":{"type":["string","null"]},"branchId":{"type":["string","null"]},"computeId":{"type":["string","null"]},"hasApiKey":{"type":"boolean"},"roleKind":{"type":["string","null"]},"roleAckAt":{"type":["string","null"],"format":"date-time"}}},{"type":"null"}],"description":"BataDB details, for a batadb source. Null otherwise."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tlsCaPem":{"type":["string","null"],"description":"The CA bundle, which is public data. Null: system roots."}},"description":"A source. Never the connection URL, the password or the BataDB API key."},{"type":"object","properties":{"pipelines":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"boxId":{"type":"string"},"state":{"type":"string"},"retainedBytes":{"type":["integer","null"]},"sessionOpen":{"type":"boolean"},"lastHeartbeatAt":{"type":["string","null"],"format":"date-time"},"errorCode":{"type":["string","null"]},"error":{"type":["string","null"]}},"required":["id","boxId","state","retainedBytes","sessionOpen","lastHeartbeatAt","errorCode","error"]}}},"required":["pipelines"]}]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `source_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."},"patch":{"operationId":"syncUpdateSource","summary":"Update a source","description":"Changes a source. Send `url` or `password`, not both. Host, port and database cannot change while the source has pipelines. Only an active source can be paused. Unknown fields are refused. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The source id.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"},"url":{"type":"string","maxLength":2048},"password":{"type":"string"},"tlsMode":{"type":"string","enum":["verify-full","require"]},"tlsCaPem":{"type":["string","null"],"description":"A PEM bundle of certificates only, at most 256 KiB. Needs tlsMode verify-full. Null clears it."},"limits":{"type":["object","null"],"properties":{"retainedWarnBytes":{"type":["integer","null"]},"retainedCriticalBytes":{"type":["integer","null"]},"retainedHardBytes":{"type":["integer","null"]}},"description":"A null field resets it; null resets all limits."},"paused":{"type":"boolean"}},"minProperties":1,"additionalProperties":false}}}},"responses":{"200":{"description":"The source.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["postgres","batadb"]},"host":{"type":"string"},"port":{"type":"integer"},"database":{"type":"string"},"username":{"type":"string"},"hasPassword":{"type":"boolean"},"tlsMode":{"type":"string"},"captureMode":{"type":"string"},"publicationName":{"type":"string"},"limits":{"type":"object","properties":{"retainedWarnBytes":{"type":"integer"},"retainedCriticalBytes":{"type":"integer"},"retainedHardBytes":{"type":"integer"}},"required":["retainedWarnBytes","retainedCriticalBytes","retainedHardBytes"]},"state":{"type":"string","description":"awaiting_setup, active, paused, error or deleting."},"error":{"type":["string","null"]},"serverVersion":{"type":["integer","null"]},"lastTest":{"oneOf":[{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"boxId":{"type":"string"},"checks":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"ok":{"type":"boolean"},"detail":{"type":"string"}},"required":["name","ok","detail"]}}},"required":["at","boxId","checks"]},{"type":"null"}]},"batadb":{"oneOf":[{"type":"object","properties":{"projectId":{"type":["string","null"]},"branchId":{"type":["string","null"]},"computeId":{"type":["string","null"]},"hasApiKey":{"type":"boolean"},"roleKind":{"type":["string","null"]},"roleAckAt":{"type":["string","null"],"format":"date-time"}}},{"type":"null"}],"description":"BataDB details, for a batadb source. Null otherwise."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tlsCaPem":{"type":["string","null"],"description":"The CA bundle, which is public data. Null: system roots."}},"description":"A source. Never the connection URL, the password or the BataDB API key."}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes: `invalid_json`, `invalid_body`, `empty_update`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `source_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Codes: `source_deleting`, `source_endpoint_locked`, `source_not_active`, `source_name_taken`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"Codes: `invalid_name`, `invalid_source_url`, `invalid_tls_mode`, `tls_mode_not_available`, `invalid_ca_pem`, `ca_has_private_key`, `ca_needs_verify_full`, `invalid_limits`, `source_host_private`, `source_host_unresolvable`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."},"delete":{"operationId":"syncDeleteSource","summary":"Delete a source","description":"Deletes a source with no synced indexes and drops Headband's replication slots. Answers 202 while the slots are still being removed. Returns the DROP PUBLICATION statement for the database owner to run. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The source id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"state":{"const":"deleted"},"deleted":{"const":true}}},"dropPublicationSql":{"type":"string"}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"202":{"description":"Being deleted.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"state":{"const":"deleting"},"deleted":{"const":false}}},"dropPublicationSql":{"type":"string"}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `source_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"Remove the source's synced indexes first (`source_has_indexes`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."}},"/v1/sync/sources/{id}/setup-sql":{"get":{"operationId":"syncGetSourceSetupSql","summary":"Get a source's setup SQL","description":"The SQL that sets up the source's publication and role for its current synced indexes. Run again after adding an index: it is idempotent. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The source id.","schema":{"type":"string"}},{"name":"provider","in":"query","required":false,"description":"Default: batadb for a BataDB source, else generic. batadb is only for BataDB sources.","schema":{"type":"string","enum":["generic","rds","batadb"]}}],"responses":{"200":{"description":"The setup SQL.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"provider":{"type":"string","enum":["generic","rds","batadb"]},"sql":{"type":"string"},"dropSql":{"type":"string"},"notes":{"type":"array","items":{"type":"string"}},"tables":{"type":"array","items":{"type":"string"}},"needsPassword":{"type":"boolean"}},"required":["provider","sql","dropSql","notes","tables","needsPassword"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Unknown provider (`invalid_provider`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `source_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"A stored definition does not parse in this release (`index_definition_invalid`). Fix or delete that index first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."}},"/v1/sync/sources/{id}/test":{"post":{"operationId":"syncTestSource","summary":"Test a source","description":"Runs the source checks from an engine host. A source in awaiting_setup whose checks pass becomes active (`activated`). Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"id","in":"path","required":true,"description":"The source id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The checks.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"boxId":{"type":"string"},"checks":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"ok":{"type":"boolean"},"detail":{"type":"string"}},"required":["name","ok","detail"]}},"activated":{"type":"boolean"},"source":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["postgres","batadb"]},"host":{"type":"string"},"port":{"type":"integer"},"database":{"type":"string"},"username":{"type":"string"},"hasPassword":{"type":"boolean"},"tlsMode":{"type":"string"},"captureMode":{"type":"string"},"publicationName":{"type":"string"},"limits":{"type":"object","properties":{"retainedWarnBytes":{"type":"integer"},"retainedCriticalBytes":{"type":"integer"},"retainedHardBytes":{"type":"integer"}},"required":["retainedWarnBytes","retainedCriticalBytes","retainedHardBytes"]},"state":{"type":"string","description":"awaiting_setup, active, paused, error or deleting."},"error":{"type":["string","null"]},"serverVersion":{"type":["integer","null"]},"lastTest":{"oneOf":[{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"boxId":{"type":"string"},"checks":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"ok":{"type":"boolean"},"detail":{"type":"string"}},"required":["name","ok","detail"]}}},"required":["at","boxId","checks"]},{"type":"null"}]},"batadb":{"oneOf":[{"type":"object","properties":{"projectId":{"type":["string","null"]},"branchId":{"type":["string","null"]},"computeId":{"type":["string","null"]},"hasApiKey":{"type":"boolean"},"roleKind":{"type":["string","null"]},"roleAckAt":{"type":["string","null"],"format":"date-time"}}},{"type":"null"}],"description":"BataDB details, for a batadb source. Null otherwise."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tlsCaPem":{"type":["string","null"],"description":"The CA bundle, which is public data. Null: system roots."}},"description":"A source. Never the connection URL, the password or the BataDB API key."}},"required":["boxId","checks","activated","source"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org), `source_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"The source is being deleted (`source_deleting`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"422":{"description":"The engine host refused the test. The code is the host's own.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Codes: `no_box_available`, `box_unavailable` (both with `Retry-After`), `auth_lookup_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."}},"/v1/sync/usage":{"get":{"operationId":"syncGetUsage","summary":"Get the org's Sync usage","description":"Sync meters for one calendar month (UTC), per index and per source, with totals. Needs an org-scoped admin key with `sync.manage` (in the admin defaults). Sync is enabled per org: without it, every /v1/sync path is a 404.","parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"month","in":"query","required":false,"description":"Like `2026-10`. Default: the current month.","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"}}],"responses":{"200":{"description":"The meters.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"range":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"}},"required":["from","to"]},"indexes":{"type":"array","items":{"type":"object","properties":{"syncIndexId":{"type":"string"},"name":{"type":"string"},"sourceId":{"type":"string"},"deleted":{"type":"boolean"},"documentsWritten":{"type":"number"},"documentsRendered":{"type":"number"},"backfillDocuments":{"type":"number"},"changes":{"type":"number"},"sourceBytes":{"type":"number"},"freshnessP95MaxMs":{"type":["number","null"],"description":"The worst hour's p95 in the range."}}}},"sources":{"type":"array","items":{"type":"object","properties":{"sourceId":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"deleted":{"type":"boolean"},"sourceHours":{"type":"number"}}}},"totals":{"type":"object","properties":{"documentsWritten":{"type":"number"},"documentsRendered":{"type":"number"},"backfillDocuments":{"type":"number"},"sourceHours":{"type":"number"}}}},"required":["range","indexes","sources","totals"]}},"required":["data"]}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"`month` is malformed (`invalid_month`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not manage Sync. Codes: `permission_denied` (no `sync.manage`, or not an admin key), `org_key_required` (a branch key), `org_wide_key_required` (a key limited to chosen engines), `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"Codes: `not_found` (Sync is not enabled for this org).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"tags":["Sync"],"security":[{"adminKey":[]}],"x-headband-permissions":["sync.manage"],"x-headband-key-types":["admin"],"x-headband-availability":"Sync is enabled per org. An org without it gets 404 `not_found` on every /v1/sync path, whatever the key."}},"/v1/tasks":{"get":{"operationId":"listTasks","summary":"List tasks","description":"Tasks of one engine. Each engine numbers its own tasks, so the engine is the one the first of `indexUids` runs on; without `indexUids`, the one engine the key's indexes run on, or the project's default engine when there is no index yet. When the indexes run on several engines the answer is 400 `index_required`. Typesense, Elasticsearch and Visor keep no task queue. Admin keys only.","tags":["Tasks"],"security":[{"adminKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"limit","in":"query","required":false,"description":"Tasks per page.","schema":{"type":"integer","default":20}},{"name":"from","in":"query","required":false,"description":"The task uid to start from (the previous page's `next`).","schema":{"type":"integer"}},{"name":"statuses","in":"query","required":false,"description":"Comma-separated statuses to keep.","schema":{"type":"string"}},{"name":"types","in":"query","required":false,"description":"Comma-separated task types to keep.","schema":{"type":"string"}},{"name":"indexUids","in":"query","required":false,"description":"Comma-separated index uids. The first one picks the engine.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"A page of tasks.","content":{"application/json":{"schema":{"type":"object","required":["results"],"properties":{"results":{"type":"array","items":{"type":"object","required":["uid","status","type","enqueuedAt"],"properties":{"uid":{"type":"integer"},"batchUid":{"type":["integer","null"]},"indexUid":{"type":["string","null"]},"status":{"type":"string","description":"Such as `enqueued`, `processing`, `succeeded`, `failed`."},"type":{"type":"string"},"details":{"type":["object","null"],"additionalProperties":true},"error":{"type":["object","null"],"description":"A failed task's error. Its message names the logical index uid.","additionalProperties":true},"duration":{"type":["string","null"]},"enqueuedAt":{"type":"string","format":"date-time"},"startedAt":{"type":["string","null"],"format":"date-time"},"finishedAt":{"type":["string","null"],"format":"date-time"}},"additionalProperties":true}},"total":{"type":"integer"},"limit":{"type":"integer"},"from":{"type":["integer","null"]},"next":{"type":["integer","null"],"description":"Pass as `from` for the next page."}},"additionalProperties":true}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `index_required` (the indexes run on several engines, or there is none), `invalid_index_uid`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Codes: `permission_denied` (search keys, with `requiredKeyType: admin`), `all_indexes_key_required`, `branch_not_allowed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"`index_not_found`, or `branch_not_found`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"`index_ambiguous`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/tasks/{taskId}":{"get":{"operationId":"getTask","summary":"Get a task","description":"One task by uid. `?index=` names the index, and so the engine, to ask; without it the task is looked up on the one engine the key's indexes run on. When `index` names an index that is gone (most often the one this task deleted), every engine the scope reaches is asked. Typesense, Elasticsearch and Visor keep no task queue and answer any task uid as succeeded. Admin keys only.","tags":["Tasks"],"security":[{"adminKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin"],"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"},{"name":"taskId","in":"path","required":true,"description":"The task uid the write answered as `taskUid`.","schema":{"type":"integer"}},{"name":"index","in":"query","required":false,"description":"The uid of the task's index.","schema":{"type":"string"}},{"$ref":"#/components/parameters/HeadbandBranch"}],"responses":{"200":{"description":"The task.","content":{"application/json":{"schema":{"type":"object","required":["uid","status","type","enqueuedAt"],"properties":{"uid":{"type":"integer"},"batchUid":{"type":["integer","null"]},"indexUid":{"type":["string","null"]},"status":{"type":"string","description":"Such as `enqueued`, `processing`, `succeeded`, `failed`."},"type":{"type":"string"},"details":{"type":["object","null"],"additionalProperties":true},"error":{"type":["object","null"],"description":"A failed task's error. Its message names the logical index uid.","additionalProperties":true},"duration":{"type":["string","null"]},"enqueuedAt":{"type":"string","format":"date-time"},"startedAt":{"type":["string","null"],"format":"date-time"},"finishedAt":{"type":["string","null"],"format":"date-time"}},"additionalProperties":true}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Codes include `invalid_request` (`taskId` is not a number), `index_required` (the indexes run on several engines, or there is none), `invalid_index_uid`, `invalid_branch_name`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Codes: `permission_denied` (search keys, with `requiredKeyType: admin`), `all_indexes_key_required`, `branch_not_allowed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"The task, the index (`index_not_found`) or the branch does not exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"409":{"description":"`index_ambiguous`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}}}},"/v1/warm":{"post":{"operationId":"warm","summary":"Pre-wake indexes","description":"Asks the dedicated instances behind some indexes, or behind every index of a project, to stage or start ahead of traffic. Free, and never waits for a wake. Indexes on shared engines, or on always-on instances, answer `awake`. An index the key cannot reach answers `refused` with reason `not_found` and engine `unknown`. 60 requests a minute per key.","tags":["System"],"security":[{"adminKey":[]},{"searchKey":[]}],"x-headband-permissions":["search"],"x-headband-key-types":["admin","search"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Send `indexes` or `project`, not both.","properties":{"indexes":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string","minLength":1},"description":"Index uids. Duplicates are dropped."},"project":{"type":"string","minLength":1,"description":"A project id: warm all its indexes."},"level":{"type":"string","enum":["auto","stage","start"],"default":"auto","description":"`auto` starts an instance woken in the last 24 hours and stages any other."},"branch":{"type":["string","null"],"description":"Only `main` is accepted."}}}}}},"responses":{"202":{"description":"The state of each index.","content":{"application/json":{"schema":{"type":"object","required":["level","indexes"],"properties":{"level":{"type":"string","enum":["auto","stage","start"]},"indexes":{"type":"array","items":{"type":"object","required":["index","engine","state"],"properties":{"index":{"type":"string"},"engine":{"type":"string"},"state":{"type":"string","enum":["awake","staging","staged","starting","refused"]},"reason":{"type":"string","description":"Why it was refused, such as `not_found` or `box_unavailable`."}}}}}}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"400":{"description":"Bad request. Codes: `invalid_json`, `invalid_body`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The key may not do this. Codes: `permission_denied`, `branch_not_in_key` (a branch other than `main`), `org_wide_key_required`, `all_indexes_key_required`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"404":{"description":"A project key named another project (`project_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}}},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"parameters":[{"$ref":"#/components/parameters/XRequestIdRequest"}]}}},"components":{"securitySchemes":{"adminKey":{"type":"http","scheme":"bearer","bearerFormat":"hb_adm_...","description":"An admin key (`hb_adm_` prefix). Server-side only: never ship it to a browser. Admin keys hold the admin default permissions unless they were minted with a narrower list. The management permissions `keys.manage`, `projects.manage`, `schema.manage` and `branches.manage` are in no default set: only keys minted through the CLI device flow (`headband login`) hold them."},"searchKey":{"type":"http","scheme":"bearer","bearerFormat":"hb_src_...","description":"A search key (`hb_src_` prefix). Safe in browser code. Default permissions: `search` and `analytics.write`."},"tenantToken":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"A tenant token: a short-lived JWT signed with a search key's secret, naming the indexes it may search and the filters forced on each. Accepted only by /v1/search and /v1/multi-search."}},"headers":{"X-Request-Id":{"description":"The id of this request. Echoes a well-formed `X-Request-Id` the client sent (8 to 128 characters of `A-Z a-z 0-9 . _ : -`), else a generated `req_...` id. Quote it to support.","schema":{"type":"string"}}},"parameters":{"HeadbandBranch":{"name":"Headband-Branch","in":"header","required":false,"description":"The branch the request reaches: a branch name, a branch id, or `<project id>/<branch name>`. Omit for the default branch.","schema":{"type":"string"}},"XRequestIdRequest":{"name":"X-Request-Id","in":"header","required":false,"description":"Your own id for this request, 8 to 128 characters of `A-Z a-z 0-9 . _ : -`. Echoed back in the `X-Request-Id` response header and in error bodies. Anything else is replaced with a generated id.","schema":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,128}$"}}},"schemas":{"Error":{"type":"object","description":"Every error body. `code` is the stable, machine-readable part; `error` is for humans and may change. Some errors carry extra fields (`requiredPermission`, `maxBytes`, ...).","required":["error"],"properties":{"error":{"type":"string","description":"A human-readable message."},"code":{"type":"string","description":"A stable error code, such as `invalid_api_key`."},"message":{"type":"string","description":"Extra detail (rate-limit errors)."},"requestId":{"type":"string","description":"The request's id, as in the `X-Request-Id` header."}},"additionalProperties":true}},"responses":{"BadRequest":{"description":"The request is malformed or fails validation.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"No valid credential. Codes: `missing_authorization_header`, `invalid_api_key`, `tenant_token_not_accepted` (a tenant token outside search and multi-search), `invalid_tenant_token`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The credential is valid but may not do this. Codes include `permission_denied` (with `requiredPermission` or `requiredKeyType`), `org_wide_key_required`, `all_indexes_key_required`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"The resource does not exist, or the key cannot see it.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The request conflicts with the current state.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Gone":{"description":"The resource existed but is gone.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PreconditionFailed":{"description":"A precondition in the request no longer holds.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The declared body is over the 4 MB limit (`payload_too_large`, with `maxBytes` and `receivedBytes`). Split the batch.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"The request is well-formed but cannot be applied.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limit exceeded. Wait for `Retry-After` seconds.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"},"Retry-After":{"description":"Seconds until the bucket has room again.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed per window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Always 0 on a 429.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix time (seconds) the bucket refills.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"An unexpected server error (`internal_error`). Quote the `requestId` to support.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"BadGateway":{"description":"The search engine behind the index answered with an error.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServiceUnavailable":{"description":"A dependency is unavailable for now (for example `auth_lookup_failed`). Retry with backoff; some carry `Retry-After`.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"The search engine behind the index timed out.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}