{
  "openapi": "3.1.1",
  "info": {
    "title": "ebitex API",
    "description": "The ebitex APIs an API key can call: the Content Delivery API (read published content), the Assets and Data delivery APIs (read a channel's assets, or a partition's records), the Content Management API (author and publish content from a script or an agent), the Content Deployment API (promote content between environments from a pipeline), the Audience Management API (know the people behind your visitors, from your own server), the Audience delivery API (ask which audiences a visitor is in), and the Forms SDK API (fetch forms to render in your own application). To render published content, use the client rather than calling the Delivery API by hand: @ebitex/content-sdk for JavaScript and TypeScript, Ebitex.Content.Delivery for .NET. Between them they handle sitemaps, live preview, locale addressing, caching and reference expansion, which a hand-written client has to rebuild.",
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://api.ebitex.io"
    }
  ],
  "paths": {
    "/forms/v1/sdk/forms/{formExternalId}": {
      "get": {
        "tags": [
          "Forms SDK: Forms"
        ],
        "summary": "Get a form",
        "description": "Returns one form, by the External ID set in the Forms editor, with its fields and steps fully resolved for the request's `Accept-Language`. It includes a `submissionUrl` to post answers to and a `submittable` flag, which is `false` for a draft.\n\nSubmit every answer under the field's `submissionKey`, never its display name. Call this from your server: the key acts for your whole organization and must never reach a browser. `@ebitex/forms-sdk` renders the result.\n\n**Key:** a Forms SDK key with `forms.sdk.forms.read`.",
        "operationId": "getForm",
        "parameters": [
          {
            "name": "formExternalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks `forms.sdk.forms.read`. `ip_denied`: the caller's address is outside the key's allowed ranges."
          },
          "404": {
            "description": "No form has this External ID in the organization."
          },
          "409": {
            "description": "`submission_key_clash`: two fields share a submission key. `clashingKeys` names them; fix it in the Forms editor."
          },
          "429": {
            "description": "Rate limit or per-minute quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/forms/v1/sdk/forms": {
      "get": {
        "tags": [
          "Forms SDK: Forms"
        ],
        "summary": "List published forms",
        "description": "Lists the organization's published forms, each with its `externalId` and locale-resolved `name`. Draft and archived forms are not listed.\n\n**Key:** a Forms SDK key with `forms.sdk.forms.read`.",
        "operationId": "listForms",
        "responses": {
          "200": {
            "description": "`{ \"forms\": [{ \"externalId\": \"contact-us\", \"name\": \"Contact us\" }] }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks `forms.sdk.forms.read`. `ip_denied`: the caller's address is outside the key's allowed ranges."
          },
          "429": {
            "description": "Rate limit or per-minute quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/assets/management/v1/assets/by-hash": {
      "get": {
        "tags": [
          "Management: Assets"
        ],
        "summary": "Find assets by the hash of the file",
        "description": "Looks up assets by the SHA-256 of the file they were first uploaded from, so a script can reuse an asset instead of uploading the same file again. Send `hashes` as 1 to 50 lowercase hexadecimal SHA-256 values separated by commas.\n\nThe hash is of the bytes you sent, before any processing: an image is re-encoded and an SVG is rebuilt when it is stored, so the hash of what you download will not match. A hash that no asset was uploaded from is simply missing from the answer. Assets uploaded in the app before this lookup existed have no hash and are never found; if two assets share one, the oldest is returned.\n\nVideos and documents are not hashed.\n\n**Key:** an Assets management key (`assets.management.read` or `assets.management.write`).",
        "operationId": "findAssetsByHash",
        "parameters": [
          {
            "name": "hashes",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`[{ \"hash\": \"…\", \"assetId\": \"…\", \"kind\": \"image\", \"visibility\": \"private\", \"name\": \"…\" }]`"
          },
          "400": {
            "description": "`validation_failed`: `hashes` is missing, names more than 50, or holds a value that is not a SHA-256 in hexadecimal."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key is not an Assets management key. `permission_denied`: a read key was used for a write. `app_not_available`: Assets is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "429": {
            "description": "`too_many_failed_authentications`: too many bad keys from this address; `rate_limited`: too many requests from the organization. `Retry-After` says how long to wait."
          }
        }
      }
    },
    "/assets/management/v1/assets/{id}": {
      "get": {
        "tags": [
          "Management: Assets"
        ],
        "summary": "Get an asset",
        "description": "Returns one asset as the Assets app shows it: its name, kind, visibility, folder, alt text and caption, and each rendition with its URL, dimensions and size. A private asset's links are signed and short-lived.\n\n**Key:** an Assets management key (`assets.management.read` or `assets.management.write`).",
        "operationId": "getManagedAsset",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key is not an Assets management key. `permission_denied`: a read key was used for a write. `app_not_available`: Assets is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "404": {
            "description": "`not_found`: there is no such asset in the organization."
          },
          "429": {
            "description": "`too_many_failed_authentications`: too many bad keys from this address; `rate_limited`: too many requests from the organization. `Retry-After` says how long to wait."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Assets"
        ],
        "summary": "Update an asset's name, folder and text",
        "description": "Replaces an asset's `name`, `folderId`, and its `title`, `alt` and `caption`. Each text is `{ \"default\": \"…\", \"locales\": { \"fr\": \"…\" } }`; this call replaces all of them, so a text you leave out is cleared: read the asset first and send back what you want kept. Send the `rowVersion` from the asset you read to be refused if someone changed it since. Set `alt` on every image you upload: it is the text a page shows when the image cannot be seen.\n\n**Key:** an Assets management key with `assets.management.write`.",
        "operationId": "updateManagedAsset",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAssetRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`validation_failed`: the name is empty or too long, a text has no `default`, or the folder does not exist."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key is not an Assets management key. `permission_denied`: a read key was used for a write. `app_not_available`: Assets is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "404": {
            "description": "`not_found`: there is no such asset in the organization."
          },
          "409": {
            "description": "The asset changed since `rowVersion` was read."
          },
          "429": {
            "description": "`too_many_failed_authentications`: too many bad keys from this address; `rate_limited`: too many requests from the organization. `Retry-After` says how long to wait."
          }
        }
      }
    },
    "/assets/management/v1/assets/{id}/visibility": {
      "put": {
        "tags": [
          "Management: Assets"
        ],
        "summary": "Make an asset public",
        "description": "Makes a private asset public. This is one way: a public link may already be cached or shared, so there is no call to make an asset private again; delete the asset to revoke it. Making an image public counts against the plan's public-image allowance.\n\nUses the same limits as the Assets app: storage and public-image quotas, the per-type size limits, and the same checks on what a file really is (the declared type is ignored). A request that would go over a quota answers `403 quota_exceeded`.\n\n**Key:** an Assets management key with `assets.management.write`.",
        "operationId": "setManagedAssetVisibility",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetVisibilityRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`validation_failed`: `visibility` is not `private` or `public`. `visibility_transition_not_supported`: the asset is already public."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key is not an Assets management key. `permission_denied`: a read key was used for a write. `app_not_available`: Assets is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "404": {
            "description": "`not_found`: there is no such asset in the organization."
          },
          "429": {
            "description": "`too_many_failed_authentications`: too many bad keys from this address; `rate_limited`: too many requests from the organization. `Retry-After` says how long to wait."
          }
        }
      }
    },
    "/assets/management/v1/folders/ensure": {
      "put": {
        "tags": [
          "Management: Assets"
        ],
        "summary": "Make sure a folder exists",
        "description": "Takes a `path` such as `Website/Screenshots` and creates whichever folders along it are missing, returning the id of the last one. Calling it again with the same path changes nothing. A path is 1 to 8 names of at most 200 characters, separated by `/`.\n\n**Key:** an Assets management key with `assets.management.write`.",
        "operationId": "ensureAssetFolder",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnsureFolderRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`validation_failed`: the path is empty, too deep, or has a name that is too long."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key is not an Assets management key. `permission_denied`: a read key was used for a write. `app_not_available`: Assets is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "429": {
            "description": "`too_many_failed_authentications`: too many bad keys from this address; `rate_limited`: too many requests from the organization. `Retry-After` says how long to wait."
          }
        }
      }
    },
    "/assets/management/v1/uploads": {
      "post": {
        "tags": [
          "Management: Assets"
        ],
        "summary": "Start an upload",
        "description": "Plans an upload and returns where to send the bytes. Give the file's `kind` (`image`, `video` or `document`), `sizeBytes`, `filename` and `contentType`, and optionally `visibility` and `folderId`. The answer carries an `uploadId` and a presigned `PUT` URL (or part URLs for a file over 100 MB): send the file there yourself, then call `complete`.\n\nNothing is stored until `complete`. An upload that is never completed is removed after 24 hours.\n\nUses the same limits as the Assets app: storage and public-image quotas, the per-type size limits, and the same checks on what a file really is (the declared type is ignored). A request that would go over a quota answers `403 quota_exceeded`.\n\n**Key:** an Assets management key with `assets.management.write`.",
        "operationId": "startManagedAssetUpload",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StartUploadRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The plan: `{ \"uploadId\": \"…\", \"method\": \"put\", \"url\": \"…\", \"headers\": {} }`."
          },
          "400": {
            "description": "`validation_failed`: a member is missing or out of range."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`quota_exceeded`: storage, video minutes or the public-image allowance would be exceeded. `plan_required`: public documents need a paid plan.\n\n`scope_denied`: the key is not an Assets management key. `permission_denied`: a read key was used for a write. `app_not_available`: Assets is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "429": {
            "description": "`too_many_failed_authentications`: too many bad keys from this address; `rate_limited`: too many requests from the organization. `Retry-After` says how long to wait."
          }
        }
      }
    },
    "/assets/management/v1/uploads/{id}/complete": {
      "post": {
        "tags": [
          "Management: Assets"
        ],
        "summary": "Complete an upload",
        "description": "Call this once the file has been sent. The file is checked for what it actually is, an image is re-encoded without its metadata and given a thumbnail, an SVG is rebuilt, and the asset is created. Calling it again for an upload already completed is harmless.\n\nA video may still be processing: the answer is then `202` with `{ \"status\": \"processing\" }`; call again until you get `201`.\n\n**Key:** an Assets management key with `assets.management.write`.",
        "operationId": "completeManagedAssetUpload",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/CompleteUploadRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new asset, as `GET /assets/{id}` returns it."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key is not an Assets management key. `permission_denied`: a read key was used for a write. `app_not_available`: Assets is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "404": {
            "description": "`not_found`: the upload does not exist, or has already finished and been removed."
          },
          "409": {
            "description": "`upload_incomplete`: the file has not arrived yet."
          },
          "422": {
            "description": "The file was refused (not an allowed type, too large, or unreadable); the answer names the reason, and the upload is removed."
          },
          "429": {
            "description": "`too_many_failed_authentications`: too many bad keys from this address; `rate_limited`: too many requests from the organization. `Retry-After` says how long to wait."
          }
        }
      }
    },
    "/assets/management/v1/uploads/{id}": {
      "get": {
        "tags": [
          "Management: Assets"
        ],
        "summary": "Check an upload",
        "description": "Returns the state of an upload that has not finished: `pending`, `processing` or `failed`, with a `failureCode` when it failed. A finished upload is removed, so `404` means it finished or is gone; look the asset up by hash.\n\n**Key:** an Assets management key with `assets.management.write`.",
        "operationId": "getManagedAssetUpload",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key is not an Assets management key. `permission_denied`: a read key was used for a write. `app_not_available`: Assets is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "404": {
            "description": "`not_found`: the upload finished, or does not exist."
          },
          "429": {
            "description": "`too_many_failed_authentications`: too many bad keys from this address; `rate_limited`: too many requests from the organization. `Retry-After` says how long to wait."
          }
        }
      }
    },
    "/data/delivery/v1/models": {
      "get": {
        "tags": [
          "Delivery: Data"
        ],
        "summary": "List the models a key can read",
        "description": "Lists the models in the key's partition that it may read, and names that partition (`partition.key`), each model with the fields it may see: the field's `id`, `name`, `type`, whether it is `mandatory`, a `list` of values or `localized`, and, for a field the model declared queryable, which of `filter`, `sort` and `facet` it supports. A model's `parent` is the key of the model it inherits from, or `null` when there is none or the key may not read it. A model's `nameField` is the id of the field that carries a record's name, or `null`. A field that has settings carries them as `settings`; a `component` field's model constraint is `settings.allowedModels`, a list of model keys that already includes every model the key can read that descends from one named (an empty list means no model the key can read, an absent one any model: absent and empty are different answers). An abstract model is listed so its fields can be understood, but holds no records.\n\nRead this before you query: it is the list of what you may ask.\n\nWhat a key sees depends on how it is restricted. A key with no origin or IP restriction is a server key and reads every model and field in its partition. A key restricted to origins or addresses is browser-safe and reads only the models their owners marked public, and only the fields marked public inside them. Something a key may not see answers exactly as something that does not exist. Folders and their access rules govern who may edit a record, not what a key reads.\n\n**Key:** a Data delivery key (`data.delivery.read`), minted for one partition in Data. A key with an origin or IP restriction is browser-safe and sees only what models mark public.",
        "operationId": "listDataModels",
        "responses": {
          "200": {
            "description": "`{ \"partition\": { \"key\": \"default\" }, \"models\": [{ \"key\": \"dog\", \"name\": \"Dog\", \"abstract\": false, \"parent\": null, \"fields\": [] }] }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or its partition binding was removed."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks `data.delivery.read`. `app_not_available`: Data is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/data/delivery/v1/records": {
      "get": {
        "tags": [
          "Delivery: Data"
        ],
        "summary": "List, filter and search records",
        "description": "Lists records, one page at a time. Without `model` it lists every readable model's records; with `model=<key>` it lists that model's records and those of every model that inherits from it.\n\n`search` matches the start of a record's name, or the start of any word in it, without regard to case: `fly` finds `Drakeshead Flycatcher`, `ly` does not. `filter[field]=value` (or `filter[field][op]=value`) narrows by a declared field; several filters are combined with AND. The operators are `eq` (the default), `in` (comma-separated), `prefix` (text), `gt`, `gte`, `lt`, `lte` (numbers and dates), and `exists` (`true` or `false`). A reference field filters by the key of the record it points at with `eq` or `in`: `filter[sire]=rex-of-drakeshead` lists a sire's offspring. Dates are RFC 3339 instants such as `2026-06-01T00:00:00Z`, unless the field is a calendar date (`2026-06-01`) or a time of day (`16:00` or `16:00:30`), which take their own form and have no time zone. Filters, field sorts and facets need `model`.\n\n`sort` is `name` (the default) or `updated`, or a field the model declared sortable; prefix `-` for descending. Records with no value for the sort field come last in either direction. `limit` is 1 to 100 and defaults to 25. `nextCursor` is `null` on the last page; send it back as `cursor` with the same query for the next. A cursor is bound to its query and stays valid while records change: no record repeats or is skipped because another was added.\n\n`facets=breed,colour` (at most 5 fields the model declared facetable) adds `facets`: for each field the 20 most common values with their counts, over every record the filters match, not just the page. The built-in facet `model` counts the matches per concrete model (`facets=model` with `model=animal` counts the birds and the cats), for when the kind of record is the model and not a field.\n\n`keys=a,b,c` reads up to 50 records by key in one round trip, in the order asked, with the keys that could not be read listed under `missing`. It cannot be combined with the other query parameters except `generations` and `expand`, which work as they do on `GET /records/{key}` and apply to every item.\n\nOnly fields a model declared queryable can be filtered, sorted or faceted, and only the kinds it declared. A field that is private, undeclared or absent answers the same `400 field_not_queryable`.\n\nWhat a key sees depends on how it is restricted. A key with no origin or IP restriction is a server key and reads every model and field in its partition. A key restricted to origins or addresses is browser-safe and reads only the models their owners marked public, and only the fields marked public inside them. Something a key may not see answers exactly as something that does not exist. Folders and their access rules govern who may edit a record, not what a key reads.\n\n**Key:** a Data delivery key (`data.delivery.read`), minted for one partition in Data. A key with an origin or IP restriction is browser-safe and sees only what models mark public.",
        "operationId": "listDataRecords",
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`field_not_queryable`, `invalid_filter`, `invalid_limit`, `invalid_generations`, `invalid_keys`, `model_required`, `invalid_search`, or `invalid_cursor`: the cursor belongs to a different query."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or its partition binding was removed."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks `data.delivery.read`. `app_not_available`: Data is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "404": {
            "description": "`model_not_found`: no model has that key, or the key may not read it."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/data/delivery/v1/records/{key}": {
      "get": {
        "tags": [
          "Delivery: Data"
        ],
        "summary": "Get a record by key",
        "description": "Returns one record: its `key`, its `model`, its `name`, its `version`, when it was last changed (`updatedAtUtc`) and its `values`, keyed by field id. A reference to another record is `{ \"mode\": \"reference\", \"provider\": \"data\", \"key\": \"…\" }`; an inline value is `{ \"mode\": \"inline\", \"model\": \"…\", \"document\": { … } }`.\n\n`generations` (0 to 5, default 0) follows references that many hops and puts the target beside its pointer as `record`, in the same shape. `expand` is a comma-separated list of reference field ids to follow, at any depth; without it every reference is followed. Following costs one query per hop however many references there are, and stops at 200 expanded records in a request, setting `meta.truncated` to `true`. A record that is its own ancestor is expanded once and the repeat stays a bare pointer; a record that appears in two branches, such as a shared ancestor, is expanded in each. A reference the key may not read, or whose target is missing, stays a bare pointer.\n\nThe response carries an `ETag`; send it back as `If-None-Match` and an unchanged record answers `304`. Responses are `private, no-cache`: revalidate rather than store.\n\nWhat a key sees depends on how it is restricted. A key with no origin or IP restriction is a server key and reads every model and field in its partition. A key restricted to origins or addresses is browser-safe and reads only the models their owners marked public, and only the fields marked public inside them. Something a key may not see answers exactly as something that does not exist. Folders and their access rules govern who may edit a record, not what a key reads.\n\n**Key:** a Data delivery key (`data.delivery.read`), minted for one partition in Data. A key with an origin or IP restriction is browser-safe and sees only what models mark public.",
        "operationId": "getDataRecord",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`invalid_generations`: `generations` is not a whole number from 0 to 5."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or its partition binding was removed."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks `data.delivery.read`. `app_not_available`: Data is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "404": {
            "description": "`record_not_found`: there is no such record, or the key may not read it."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/whoami": {
      "get": {
        "tags": [
          "Audience: People"
        ],
        "summary": "Describe the calling key",
        "description": "Reports what the Audience management key you are calling with is: the organization it belongs to, its own id and name, and its scopes.\n\nIt works with any valid, bound management key whatever its scopes, so a key that can only read can find out that reading is all it can do. Scopes are tiers: `audience.management.write` needs `audience.management.read`, and `audience.management.erase` needs both.\n\n**Key:** any valid Audience management key, whatever its scopes.",
        "operationId": "getAudienceKey",
        "responses": {
          "200": {
            "description": "`{ \"organizationId\", \"organizationName\", \"organizationSlug\", \"keyId\", \"keyName\", \"scopes\": [ … ] }`. `scopes` is sorted."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/schema": {
      "get": {
        "tags": [
          "Audience: People"
        ],
        "summary": "Get the custom fields",
        "description": "Returns the organization's custom fields for people: each field's `key`, `label`, `type`, whether it is required and its settings. Values you send in `attributes` are checked against this.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "getAudienceSchema",
        "responses": {
          "200": {
            "description": "`{ \"fields\": [ { \"key\", \"label\", \"type\", … } ], \"version\" }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/individuals": {
      "get": {
        "tags": [
          "Audience: People"
        ],
        "summary": "List individuals",
        "description": "Lists the people in the organization, in name order, 25 to a page by default. A person merged into another is not listed.\n\n`search` finds people whose name, or any identity value, starts with the text. `source` limits the list to one source kind (`person`, `import`, `key`, `app` or `connector`). `page` starts at 1 and `pageSize` is at most 100.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "listAudienceIndividuals",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [ { \"id\", \"name\", … } ], \"totalCount\", \"page\", \"pageSize\" }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "post": {
        "tags": [
          "Audience: People"
        ],
        "summary": "Create an individual",
        "description": "Creates a person. Send a `name`, at least one identity in `identities`, or both: a person with neither is refused. `attributes` carries custom-field values, checked against the organization's schema (see `GET /schema`); `channelPermissions` records what the person agreed to, each `{ \"channel\", \"status\", \"basis\", \"note\" }`.\n\nAn identity is an `email`, a `phone` number or an `external` key. Emails are trimmed and lower-cased; a phone number must be international (`+` then 8 to 15 digits, spaces, dashes, dots and brackets allowed and removed), because no country is ever guessed; an external key names the foreign `system` (`stripe`, `crm`) and keeps its case. An external key is always verified. An email or phone number is verified only when the request says how, with `verified: { \"method\": … }`; nothing is ever verified by inference.\n\nA verified identity belongs to one person in an organization. Adding one that another person already holds verified is refused with `409 identity_verified_elsewhere` and the holder's id, unless the request sets `mergeOnConflict: true`: then the person you were writing to is merged into the holder, who survives. The merge is recorded and can be reverted by a person in the dashboard.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "createAudienceIndividual",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateIndividualRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "`{ \"id\", \"name\", \"displayName\", \"source\": { \"kind\", \"key\", \"label\" }, \"identities\": [ … ], \"channelPermissions\": [ … ], \"visitorLinks\": [ … ], \"attributes\": { … }, \"mergedInto\", \"interactionCount\", \"createdAtUtc\", \"updatedAtUtc\", \"rowVersion\" }`. `name` is the display name, else the primary email, else the primary phone, else the first external key, else \"Unnamed\"."
          },
          "400": {
            "description": "`validation_failed`: a field is invalid; `errors` is keyed by field, and a custom field as `attributes.<key>`. `individual_needs_name_or_identity`: neither a name nor an identity was sent."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`quota_exceeded`: the plan's individual limit (`audience.max_individuals`) is reached; `quotaKey` names it. Nothing is created and everyone already held stays as they are.\n\n`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`not_found`: no such route."
          },
          "409": {
            "description": "`identity_verified_elsewhere`: a verified identity is already held by another person; `holder` names them."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/individuals/{id}": {
      "get": {
        "tags": [
          "Audience: People"
        ],
        "summary": "Get an individual",
        "description": "Returns one person with their identities, channel permissions, linked browsers and custom-field values.\n\nA person who was merged into another answers `200` with `mergedInto` set to the survivor's id, so a stored id keeps resolving.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "getAudienceIndividual",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"id\", \"name\", \"displayName\", \"source\": { \"kind\", \"key\", \"label\" }, \"identities\": [ … ], \"channelPermissions\": [ … ], \"visitorLinks\": [ … ], \"attributes\": { … }, \"mergedInto\", \"interactionCount\", \"createdAtUtc\", \"updatedAtUtc\", \"rowVersion\" }`. `name` is the display name, else the primary email, else the primary phone, else the first external key, else \"Unnamed\"."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`individual_not_found`: no such person in this organization.\n\n`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Audience: People"
        ],
        "summary": "Update an individual",
        "description": "Replaces a person's `name` and `attributes`. Send the `rowVersion` you read: if the person changed since, the call answers `409 concurrency_conflict` and nothing is written.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "updateAudienceIndividual",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateIndividualRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"id\", \"name\", \"displayName\", \"source\": { \"kind\", \"key\", \"label\" }, \"identities\": [ … ], \"channelPermissions\": [ … ], \"visitorLinks\": [ … ], \"attributes\": { … }, \"mergedInto\", \"interactionCount\", \"createdAtUtc\", \"updatedAtUtc\", \"rowVersion\" }`. `name` is the display name, else the primary email, else the primary phone, else the first external key, else \"Unnamed\"."
          },
          "400": {
            "description": "`validation_failed`: a field is invalid; a custom field is keyed `attributes.<key>`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`individual_not_found`: no such person in this organization.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`concurrency_conflict`: the person changed since `rowVersion` was read."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/individuals/{id}/identities": {
      "post": {
        "tags": [
          "Audience: People"
        ],
        "summary": "Add an identity",
        "description": "Adds an email, phone number or external key to a person: `{ \"type\", \"value\", \"system\", \"verified\": { \"method\" }, \"mergeOnConflict\" }`.\n\nAn identity is an `email`, a `phone` number or an `external` key. Emails are trimmed and lower-cased; a phone number must be international (`+` then 8 to 15 digits, spaces, dashes, dots and brackets allowed and removed), because no country is ever guessed; an external key names the foreign `system` (`stripe`, `crm`) and keeps its case. An external key is always verified. An email or phone number is verified only when the request says how, with `verified: { \"method\": … }`; nothing is ever verified by inference.\n\nA verified identity belongs to one person in an organization. Adding one that another person already holds verified is refused with `409 identity_verified_elsewhere` and the holder's id, unless the request sets `mergeOnConflict: true`: then the person you were writing to is merged into the holder, who survives. The merge is recorded and can be reverted by a person in the dashboard.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "addAudienceIdentity",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IdentityRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The identity as stored, with its `normalizedValue`, `isVerified`, `verificationMethod` and `isPrimary`."
          },
          "400": {
            "description": "`validation_failed`: the type, value or system is invalid."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`individual_not_found`: no such person in this organization.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`identity_verified_elsewhere`: another person holds this verified identity; `holder` names them."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/individuals/{id}/identities/{identityId}": {
      "delete": {
        "tags": [
          "Audience: People"
        ],
        "summary": "Remove an identity",
        "description": "Removes one identity from a person. If it was the primary identity of its type, no other becomes primary until one is chosen.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "removeAudienceIdentity",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "identityId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`individual_not_found` or `identity_not_found`: no such person or identity.\n\n`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/individuals/{id}/channel-permissions/{channel}": {
      "put": {
        "tags": [
          "Audience: People"
        ],
        "summary": "Set a channel permission",
        "description": "Records whether a person may be contacted on a channel (`email`, `sms`, `phone` or `post`): `{ \"status\": \"granted\" | \"denied\", \"basis\": \"consent\" | \"contract\" | \"legitimate-interest\", \"note\" }`. It is a fact with its own source and time, separate from any browser's consent to be recognised.\n\nA channel with no record is **unknown**, and unknown means do not send. Every change is also written to the person's interactions.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "setAudienceChannelPermission",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "channel",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetChannelPermissionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The permission as stored: `{ \"channel\", \"status\", \"basis\", \"source\", \"recordedAtUtc\", \"note\" }`."
          },
          "400": {
            "description": "`validation_failed`: the channel, status or basis is not one of the listed values, or the note is over 500 characters."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`individual_not_found`: no such person in this organization.\n\n`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/individuals/{id}/interactions": {
      "get": {
        "tags": [
          "Audience: People"
        ],
        "summary": "List a person's interactions",
        "description": "Lists what has been recorded about a person, newest first, including those of anyone merged into them. Page with `before`, the `occurredAtUtc` of the last row you received, and `limit` (at most 100). This is a list: nothing here counts, groups or sums.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "listAudienceInteractions",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [ { \"id\", \"kind\", \"source\", \"sourceEventId\", \"occurredAtUtc\", \"recordedAtUtc\", \"data\" } ], \"next\" }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`individual_not_found`: no such person in this organization.\n\n`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "post": {
        "tags": [
          "Audience: People"
        ],
        "summary": "Record an interaction",
        "description": "Appends one fact about a person: `{ \"kind\", \"sourceEventId\", \"occurredAt\", \"data\" }`. `kind` is lower-case, at most 64 characters, and yours to choose, prefixed with your system's name (`crm.signup`); it is never interpreted. `data` is JSON of at most 16 KiB and 8 levels.\n\n**Idempotent.** Send a `sourceEventId` and the same event sent again answers `200` with the interaction already recorded and writes nothing, so a retry is safe. Page views are not stored: kind `page-view` is refused.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "addAudienceInteraction",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppendInteractionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The interaction as stored. `201` when it was new, `200` when `sourceEventId` had already been recorded."
          },
          "400": {
            "description": "`validation_failed`: the kind, data or event id is invalid. `kind_not_accepted`: page views are not accepted."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`individual_not_found`: no such person in this organization.\n\n`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/identify": {
      "post": {
        "tags": [
          "Audience: People"
        ],
        "summary": "Identify a person",
        "description": "The one event-shaped write: tell Audience that you now know who someone is. `{ \"identity\": { \"type\", \"value\", \"system\", \"verified\": { \"method\" } }, \"visitor\", \"consent\": { \"cmp\", \"gpc\", \"dnt\", \"cmpString\" }, \"name\", \"locale\", \"attributes\", \"interaction\": { \"kind\", \"sourceEventId\", \"occurredAt\", \"data\" }, \"source\": { \"label\" }, \"account\": { \"externalId\", \"name\", \"role\", \"attributes\" }, \"verification\": { \"audienceId\", \"returnUrl\", \"message\": { \"subject\", \"text\", \"html\" } }, \"attribution\": { \"source\", \"medium\", \"campaign\" } }`.\n\nIn one transaction it finds the person by the identity (a verified match first, then an unverified match on exactly one person; with two or more it creates a new person and says so with `ambiguous`), adds the identity, fills **empty** name, `locale` (a language code such as `fr` or `en-GB`, used to choose the language messages are written in) and custom fields only (an event never overwrites what a person typed), records the interaction, and, when asked, links the visitor.\n\nA verified identity belongs to one person in an organization. Adding one that another person already holds verified is refused with `409 identity_verified_elsewhere` and the holder's id, unless the request sets `mergeOnConflict: true`: then the person you were writing to is merged into the holder, who survives. The merge is recorded and can be reverted by a person in the dashboard. `identify` always merges, because an identification event is the automatic consolidation.\n\n**Linking a visitor.** `visitor` is the visitor token your page holds. It is linked to the person only if it reads as a valid, unrevoked recognition token **and** the device's own consent signals say recognition is granted. You must forward the device's signals in `consent`: the consent-management choice, the Global Privacy Control header and the Do Not Track header, exactly as the browser sent them. Sending none is treated as no consent. `linked` says whether it worked, and `linkReason` says why not: `no_visitor`, `token_invalid`, `session_token`, `recognition_not_granted`, `visitor_linked_elsewhere` or `revoked`.\n\n**Naming an account.** When the organization has accounts turned on, `account` adds the person to an account found by its `externalId` and creates the account if it does not exist (`name` is used for a new account and defaults to the external id; `attributes` fill an existing account's **empty** values only). `role` is a label you choose. Accounts are never merged or matched by anything but the external id. If the plan's account limit is reached the person is still identified and the answer says `accountSkipped: \"account_quota_exceeded\"`; an account that already exists can still be joined.\n\n**Asking for a verification link.** `verification` asks Audience to email the person a one-time link that proves they hold the address, as part of this identification. It needs a `visitor` token (the proof is for that device) and an email identity, and it sends the link only when the person is in a verified-access audience (`audienceId` names one; leave it out for any). It never fails the identification: `verification.status` says what happened (`sent`, `replayed`, `no-visitor`, `no-email`, `throttled` or `not-a-member`), and a replay of the same `sourceEventId` sends nothing. `returnUrl` is where the confirm page offers to continue (an absolute https address, treated as none when it is not acceptable), so it can be a page, a download page or a file's own address. `message` is your own words for the email: `subject` is one line and `text` and the optional `html` must each contain `{{link}}`; `{{returnUrl}}`, `{{name}}` and `{{audience}}` are also filled in, and values are inserted escaped. Leave `message` out for the built-in wording.\n\n**Where the person came from.** `attribution` is the three campaign tags of the link they followed (`utm_source`, `utm_medium` and `utm_campaign`, as `source`, `medium` and `campaign`; each at most 100 characters). Audience records it **once**, as the person's first touch: a person with no Awareness pass gets one, opened with those three facts; a person whose open Awareness pass has none of the three is filled in; anyone else is left exactly as they are, and sending it again is never an error. The campaign text is matched to a campaign that already has it as an alias and **never registers a new campaign**, because anyone can write a link. It never fails the identification: `attribution.status` says `recorded`, `filled`, `kept` or `none`, and `attribution.campaignResolved` whether the text matched a campaign.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "identifyAudienceIndividual",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IdentifyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"individual\": { \"id\", \"name\" }, \"created\", \"identityAdded\", \"merged\": { \"survivorId\" }, \"linked\", \"linkReason\", \"ambiguous\", \"account\": { \"id\", \"externalId\", \"created\", \"memberAdded\" }, \"accountSkipped\" }`. `account` and `accountSkipped` appear only when `account` was sent. `verification` appears only when `verification` was sent. `attribution` (`{ \"status\", \"campaignResolved\" }`) appears only when `attribution` was sent."
          },
          "400": {
            "description": "`validation_failed`: the identity, name, attributes, interaction or verification message is invalid. `kind_not_accepted`: page views are not accepted."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`quota_exceeded`: the identity matched nobody and the plan's individual limit is reached, so no person was created (an identity that matches someone is still found and linked).\n\n`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`not_found`: no such route."
          },
          "409": {
            "description": "`accounts_not_enabled`: `account` was sent but the organization has not turned accounts on; nothing was written."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/individuals/{id}/forget": {
      "post": {
        "tags": [
          "Audience: People"
        ],
        "summary": "Forget a person",
        "description": "Erases a person and the personal data held about them across the suite: their identities, linked browsers, interactions and permissions here, and the form submissions from those browsers and identities. It cannot be undone.\n\nThe run is fail-closed: browsers are revoked first, then each part of the suite erases in order, and the first failure stops the run, leaves the person in place and answers `500` so the call can be retried. A receipt with no personal data is kept.\n\n**Key:** an Audience management key with `audience.management.erase` (write also needs read; erase needs both).",
        "operationId": "forgetAudienceIndividual",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"erasureId\", \"participants\": [ { \"key\", \"counts\": { … } } ] }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`individual_not_found`: no such person in this organization.\n\n`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          },
          "500": {
            "description": "`<app>_erasure_failed`: that part of the suite could not erase; nothing later ran and the person was kept. Retry."
          }
        }
      }
    },
    "/audience/management/v1/individuals/{id}/stages": {
      "get": {
        "tags": [
          "Audience: Lifecycle"
        ],
        "summary": "List a person's lifecycle stages",
        "description": "Returns the person's journey: for each of the five stages, the open pass if there is one and the closed passes before it, newest first. A person can hold at most one open pass in a stage, and any number of closed ones.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "listAudienceIndividualStages",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"stages\": [ { \"stage\", \"open\": `{ \"id\", \"stage\", \"state\": { \"id\", \"name\", \"kind\", \"isTerminal\" }, \"outcome\", \"openedAtUtc\", \"stateEnteredAtUtc\", \"closedAtUtc\", \"facts\": { … }, \"source\": { \"kind\", \"key\", \"label\" }, \"availableTransitions\": [ { \"id\", \"name\", \"toStateId\" } ], \"rowVersion\", \"campaignId\", \"campaign\": { \"id\", \"name\", \"kind\" } }`. On an awareness pass, `campaignId` and `campaign` are read-only: the campaign the pass's `campaign` fact resolves to (an alias the organization already holds, or an external campaign registered the first time the text is seen). Both are absent on any other stage and when the text resolves to nothing., \"closed\": [ … ] } ] }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`individual_not_found`: no such person in this organization.\n\n`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/individuals/{id}/stages/{stage}": {
      "post": {
        "tags": [
          "Audience: Lifecycle"
        ],
        "summary": "Open a stage for a person",
        "description": "Opens a pass for the person in a stage (`awareness`, `acquisition`, `conversion`, `retention` or `advocacy`), in the stage's initial state. `facts` is an object of the stage's own facts (for example `campaign` and `medium` for awareness, `score` for acquisition, `kind` and `valueMinor` for conversion); a fact the stage does not have is refused, never ignored.\n\nA person has at most one open pass per stage: opening one that is already open is refused.\n\nA key holds no role. It can fire a transition only when that transition has no role allow-list; one that does answers `403 transition_not_allowed`. A key never holds the permission that overrides a role rule an Owner set, because anyone who can mint a key could otherwise use it to override one. The key's name, and the fact that a key made the move, are what the person's history and the organization's activity log record.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "openAudienceIndividualStage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "stage",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OpenStageRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "`{ \"id\", \"stage\", \"state\": { \"id\", \"name\", \"kind\", \"isTerminal\" }, \"outcome\", \"openedAtUtc\", \"stateEnteredAtUtc\", \"closedAtUtc\", \"facts\": { … }, \"source\": { \"kind\", \"key\", \"label\" }, \"availableTransitions\": [ { \"id\", \"name\", \"toStateId\" } ], \"rowVersion\", \"campaignId\", \"campaign\": { \"id\", \"name\", \"kind\" } }`. On an awareness pass, `campaignId` and `campaign` are read-only: the campaign the pass's `campaign` fact resolves to (an alias the organization already holds, or an external campaign registered the first time the text is seen). Both are absent on any other stage and when the text resolves to nothing."
          },
          "400": {
            "description": "`validation_failed`: a fact is invalid. `unknown_fact`: the stage has no such fact."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`individual_not_found` or `stage_not_found`.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`stage_already_open`: the person already has an open pass in this stage. `individual_merged`: the person was merged into another; use the survivor."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/individuals/{id}/stages/{stage}/rows/{rowId}": {
      "put": {
        "tags": [
          "Audience: Lifecycle"
        ],
        "summary": "Change a pass's facts",
        "description": "Replaces the facts of an open pass. Send the `rowVersion` you read; a pass someone else changed since answers `409 concurrency_conflict`. Moving a pass between states is a separate call, so a facts edit can never skip a rule.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "updateAudienceStageFacts",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "stage",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "rowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateStageFactsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"id\", \"stage\", \"state\": { \"id\", \"name\", \"kind\", \"isTerminal\" }, \"outcome\", \"openedAtUtc\", \"stateEnteredAtUtc\", \"closedAtUtc\", \"facts\": { … }, \"source\": { \"kind\", \"key\", \"label\" }, \"availableTransitions\": [ { \"id\", \"name\", \"toStateId\" } ], \"rowVersion\", \"campaignId\", \"campaign\": { \"id\", \"name\", \"kind\" } }`. On an awareness pass, `campaignId` and `campaign` are read-only: the campaign the pass's `campaign` fact resolves to (an alias the organization already holds, or an external campaign registered the first time the text is seen). Both are absent on any other stage and when the text resolves to nothing."
          },
          "400": {
            "description": "`validation_failed` or `unknown_fact`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`stage_row_not_found`: no such pass for this person.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`stage_row_closed`: a closed pass is a record and is not edited. `concurrency_conflict`: the pass changed since you read it."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/individuals/{id}/stages/{stage}/rows/{rowId}/transitions": {
      "post": {
        "tags": [
          "Audience: Lifecycle"
        ],
        "summary": "Move a pass to another state",
        "description": "Fires one of the pass's `availableTransitions`. The engine checks the transition's condition and, if it has one, runs its actions; a transition into a terminal state closes the pass with its outcome, and an automatic transition that follows it runs in the same call. `comment` is kept with the move.\n\nA key holds no role. It can fire a transition only when that transition has no role allow-list; one that does answers `403 transition_not_allowed`. A key never holds the permission that overrides a role rule an Owner set, because anyone who can mint a key could otherwise use it to override one. The key's name, and the fact that a key made the move, are what the person's history and the organization's activity log record.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "moveAudienceStageRow",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "stage",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "rowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoveStageRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"id\", \"stage\", \"state\": { \"id\", \"name\", \"kind\", \"isTerminal\" }, \"outcome\", \"openedAtUtc\", \"stateEnteredAtUtc\", \"closedAtUtc\", \"facts\": { … }, \"source\": { \"kind\", \"key\", \"label\" }, \"availableTransitions\": [ { \"id\", \"name\", \"toStateId\" } ], \"rowVersion\", \"campaignId\", \"campaign\": { \"id\", \"name\", \"kind\" } }`. On an awareness pass, `campaignId` and `campaign` are read-only: the campaign the pass's `campaign` fact resolves to (an alias the organization already holds, or an external campaign registered the first time the text is seen). Both are absent on any other stage and when the text resolves to nothing."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`transition_not_allowed`: the transition has a role allow-list, and a key holds no role.\n\n`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`stage_row_not_found`.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`invalid_transition`: that transition does not leave the pass's current state. `transition_vetoed`: the transition's condition is not met. `outcome_blocked`: a closing action failed and the pass was left where it was. `stage_row_closed`."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/stages/{stage}/rows": {
      "get": {
        "tags": [
          "Audience: Lifecycle"
        ],
        "summary": "Walk the open passes in a stage",
        "description": "Returns the open passes in a stage in the order they entered their current state, longest-waiting first, in pages that cost the same however deep you are: pass `nextCursor` back as `cursor` until it is absent. A pass that moves while you walk is seen at most once and never skipped.\n\n`stateId`, `search` (the person's name or an identity value), `ownerExternalUserId` and `olderThanDays` (time in the current state) narrow the walk. `limit` is at most 100.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "listAudienceStageRows",
        "parameters": [
          {
            "name": "stage",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "stateId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ownerExternalUserId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "olderThanDays",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [ { \"person\": { \"id\", \"name\", \"primaryEmail\" }, \"row\": … } ], \"nextCursor\" }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`stage_not_found`.\n\n`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/stages/{stage}/summary": {
      "get": {
        "tags": [
          "Audience: Lifecycle"
        ],
        "summary": "Count a stage by state",
        "description": "Returns the true number of open passes in each state of the stage, and how many are open and closed in all. These are counts, never a sample, so a dashboard built on them is right at any size.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "getAudienceStageSummary",
        "parameters": [
          {
            "name": "stage",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"states\": [ { \"id\", \"name\", \"kind\", \"isTerminal\", \"count\" } ], \"openCount\", \"closedCount\" }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`stage_not_found`.\n\n`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/review": {
      "get": {
        "tags": [
          "Audience: Lifecycle"
        ],
        "summary": "List the review queue",
        "description": "Returns what needs a person's decision: suggested duplicates (`merge-suggestion`) and tasks (`task`). `kind` and `status` (`open`, `resolved` or `dismissed`, default `open`) filter the list; `page` starts at 1 and `pageSize` is at most 100. The queue never applies anything: a suggestion is only ever a suggestion until someone resolves it.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "listAudienceReviewItems",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [ { \"id\", \"kind\", \"status\", \"resolution\", \"title\", \"detail\", \"subject\": { \"id\", \"name\" }, \"other\", \"assigneeExternalUserId\", \"dueAtUtc\", \"rowVersion\" } ], \"totalCount\", \"page\", \"pageSize\" }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/individuals/{id}/tasks": {
      "post": {
        "tags": [
          "Audience: Lifecycle"
        ],
        "summary": "Create a task about a person",
        "description": "Adds a task to the review queue. `title` is required (200 characters at most); `note`, `assigneeExternalUserId`, `assigneeLabel` and `dueAtUtc` are optional. A person can have at most 200 open tasks.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "createAudienceTask",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTaskRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The review item, as listed by `GET /review`."
          },
          "400": {
            "description": "`validation_failed`: the title is missing or too long."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`individual_not_found`.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`too_many_open_tasks`. `individual_merged`: add the task on the person it was merged into."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/review/{id}/resolve": {
      "post": {
        "tags": [
          "Audience: Lifecycle"
        ],
        "summary": "Resolve a review item",
        "description": "Closes an open item with a `resolution`: `done` for a task; `merged` or `kept-separate` for a suggestion. `merged` needs `survivorId`, one of the two people, and runs the same merge a person would, in the same call; it can be reverted from the person's page. `kept-separate` is permanent: the pair is never suggested again.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "resolveAudienceReviewItem",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolveReviewRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The review item, as listed by `GET /review`."
          },
          "400": {
            "description": "`validation_failed`: the resolution does not fit the item's kind."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`review_item_not_found`.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`review_item_closed`. `survivor_not_in_pair`: `survivorId` is not one of the two people. `concurrency_conflict`."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/review/{id}/dismiss": {
      "post": {
        "tags": [
          "Audience: Lifecycle"
        ],
        "summary": "Dismiss a review item",
        "description": "Closes an open item without deciding it. A person can reopen a dismissed item in the dashboard.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "dismissAudienceReviewItem",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The review item, as listed by `GET /review`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`review_item_not_found`.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`review_item_closed`: the item is not open."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/audiences": {
      "get": {
        "tags": [
          "Audience: Audiences"
        ],
        "summary": "List audiences",
        "description": "Lists the organization's audiences by key, each with its rule and, for an audience whose members are stored, how many people are in it.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "listAudienceAudiences",
        "responses": {
          "200": {
            "description": "`[ `{ \"id\", \"externalId\", \"name\", \"predicate\": { … }, \"revision\", \"scope\", \"storedMembership\", \"memberCount\", \"membershipCurrent\", \"createdAtUtc\", \"updatedAtUtc\", \"rowVersion\", \"behaviours\": [ … ] }`. `scope` is `context` (reads only the request's own context), `individual` (reads only what Audience knows about a recognised person) or `mixed`. ]`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "post": {
        "tags": [
          "Audience: Audiences"
        ],
        "summary": "Create an audience",
        "description": "Creates an audience from a `externalId` (lowercase letters, digits and hyphens, 3 to 64 characters, unique in the organization), a `name` and a `predicate`.\n\n`predicate` is one predicate tree: `equals`, `notEquals`, `in`, `compare` (`lt`, `lte`, `gt`, `gte`), `exists`, `notExists`, `allOf` and `anyOf`, each naming a `property` of a flat bag. The bag is the request's own context (the members the presentation app sends) plus, for a person Audience recognises on that device, members under `individual`: `individual.recognised`, `individual.stages.open`, `individual.stages.reached`, `individual.stage.<stage>.state`, `individual.stage.acquisition.score`, `individual.attribute.<field>` and `individual.permission.<channel>`, and, for an organization that uses accounts, `individual.account.member` and `individual.account.attribute.<field>` (an account's custom field, as a list over the person's accounts, so it matches when any of their accounts does). Any other `individual.*` name is refused.\n\nAn audience that reads `individual.*` is never true for a visitor Audience cannot recognise, even when its predicate is a negation.\n\nA predicate may name another audience as `audience.member.<id>` (true when the person is in it), and the audience takes on the scope of everything it names; audiences cannot name each other in a loop, and one another names cannot be deleted. `behaviours` declares things a person did (`{ id, kind, filter, minCount, days }`, kinds `form.submitted`, `audience.entered`, `stage.moved` and `content.viewed` (page views in the category `filter` names, over 1 to 365 days), at most 5), which the predicate reads as `individual.did.<id>`. With `scope: \"account\"` and `minMembers` (1 to 1,000) a behaviour counts what the people in a person's accounts did instead: it is true for the person when, in any of their accounts, at least `minMembers` members each did it at least `minCount` times in the window. A member who joins an account brings what they did, and one who leaves takes it. Account names and account behaviours are refused with `409 accounts_not_enabled` while the organization has accounts off; an audience saved earlier is kept, and has no members until they are turned on.\n\n`scorePoints` (-100 to 100, never 0; send `0` to remove) makes the audience add that many points to the lead score of everyone in it. The score is the sum over the audiences a person is in, held between 0 and 100. Only an audience that reads a person and nothing about the request may add points, and one that adds points cannot read a score, directly or through an audience it names. While any audience adds points the acquisition `score` fact is calculated, and writing it answers `409 score_is_computed`.\n\nAn organization can hold at most 200 audiences.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "createAudienceAudience",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SaveAudienceRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "`{ \"id\", \"externalId\", \"name\", \"predicate\": { … }, \"revision\", \"scope\", \"storedMembership\", \"memberCount\", \"membershipCurrent\", \"createdAtUtc\", \"updatedAtUtc\", \"rowVersion\", \"behaviours\": [ … ] }`. `scope` is `context` (reads only the request's own context), `individual` (reads only what Audience knows about a recognised person) or `mixed`."
          },
          "400": {
            "description": "`validation_failed`: the key, name or predicate is invalid; `errors` is keyed by field."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`not_found`: no such route."
          },
          "409": {
            "description": "`audience_limit_reached`: the organization already holds 200 audiences."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/audiences/{id}": {
      "get": {
        "tags": [
          "Audience: Audiences"
        ],
        "summary": "Get an audience",
        "description": "Returns one audience. The `id` is what a personalized variant stores, so it never changes when the audience is renamed.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "getAudienceAudience",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"id\", \"externalId\", \"name\", \"predicate\": { … }, \"revision\", \"scope\", \"storedMembership\", \"memberCount\", \"membershipCurrent\", \"createdAtUtc\", \"updatedAtUtc\", \"rowVersion\", \"behaviours\": [ … ] }`. `scope` is `context` (reads only the request's own context), `individual` (reads only what Audience knows about a recognised person) or `mixed`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`audience_not_found`: no such audience in this organization.\n\n`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Audience: Audiences"
        ],
        "summary": "Change an audience",
        "description": "Renames an audience and replaces its rule. The key is set once and cannot change. Send the `rowVersion` you read; an audience someone else changed since answers `409 concurrency_conflict`. Changing the rule starts a new revision, and who is in an audience that stores its members is brought up to date in the background (`membershipCurrent` is `false` until it is).\n\n`predicate` is one predicate tree: `equals`, `notEquals`, `in`, `compare` (`lt`, `lte`, `gt`, `gte`), `exists`, `notExists`, `allOf` and `anyOf`, each naming a `property` of a flat bag. The bag is the request's own context (the members the presentation app sends) plus, for a person Audience recognises on that device, members under `individual`: `individual.recognised`, `individual.stages.open`, `individual.stages.reached`, `individual.stage.<stage>.state`, `individual.stage.acquisition.score`, `individual.attribute.<field>` and `individual.permission.<channel>`, and, for an organization that uses accounts, `individual.account.member` and `individual.account.attribute.<field>` (an account's custom field, as a list over the person's accounts, so it matches when any of their accounts does). Any other `individual.*` name is refused.\n\nAn audience that reads `individual.*` is never true for a visitor Audience cannot recognise, even when its predicate is a negation.\n\nA predicate may name another audience as `audience.member.<id>` (true when the person is in it), and the audience takes on the scope of everything it names; audiences cannot name each other in a loop, and one another names cannot be deleted. `behaviours` declares things a person did (`{ id, kind, filter, minCount, days }`, kinds `form.submitted`, `audience.entered`, `stage.moved` and `content.viewed` (page views in the category `filter` names, over 1 to 365 days), at most 5), which the predicate reads as `individual.did.<id>`. With `scope: \"account\"` and `minMembers` (1 to 1,000) a behaviour counts what the people in a person's accounts did instead: it is true for the person when, in any of their accounts, at least `minMembers` members each did it at least `minCount` times in the window. A member who joins an account brings what they did, and one who leaves takes it. Account names and account behaviours are refused with `409 accounts_not_enabled` while the organization has accounts off; an audience saved earlier is kept, and has no members until they are turned on.\n\n`scorePoints` (-100 to 100, never 0; send `0` to remove) makes the audience add that many points to the lead score of everyone in it. The score is the sum over the audiences a person is in, held between 0 and 100. Only an audience that reads a person and nothing about the request may add points, and one that adds points cannot read a score, directly or through an audience it names. While any audience adds points the acquisition `score` fact is calculated, and writing it answers `409 score_is_computed`.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "updateAudienceAudience",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SaveAudienceRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"id\", \"externalId\", \"name\", \"predicate\": { … }, \"revision\", \"scope\", \"storedMembership\", \"memberCount\", \"membershipCurrent\", \"createdAtUtc\", \"updatedAtUtc\", \"rowVersion\", \"behaviours\": [ … ] }`. `scope` is `context` (reads only the request's own context), `individual` (reads only what Audience knows about a recognised person) or `mixed`."
          },
          "400": {
            "description": "`validation_failed`: the name or predicate is invalid, or the key was changed."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`audience_not_found`.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`concurrency_conflict`: the audience changed since you read it."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Audience: Audiences"
        ],
        "summary": "Delete an audience",
        "description": "Deletes an audience and who was stored as in it. A person's history keeps the record that they entered or left it.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "deleteAudienceAudience",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`audience_not_found`.\n\n`not_found`: no such route."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/accounts": {
      "get": {
        "tags": [
          "Audience: Accounts"
        ],
        "summary": "List accounts",
        "description": "Lists the organization's accounts in name order, 25 to a page by default. `search` finds accounts whose name, any word of it, or external id starts with the text. `page` starts at 1 and `pageSize` is at most 100.\n\nAccounts are optional and off until the organization turns them on in Audience settings; every account route answers `409 accounts_not_enabled` until then. An account is a group of people the organization tracks as one thing (a company, a household, a school). It is identified by an **external id** you supply (for example your own customer id), which is case-sensitive and unique in the organization: there is no fuzzy matching, so two accounts are the same account only when the external id is the same. A person can belong to any number of accounts, with an optional role in each.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "listAudienceAccounts",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [ … ], \"totalCount\", \"page\", \"pageSize\" }`, each item as an account."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`not_found`: no such route."
          },
          "409": {
            "description": "`accounts_not_enabled`: the organization has not turned accounts on."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/accounts/{externalId}": {
      "get": {
        "tags": [
          "Audience: Accounts"
        ],
        "summary": "Get an account",
        "description": "Returns one account by the external id you gave it. The external id is case-sensitive.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "getAudienceAccount",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"id\", \"externalId\", \"name\", \"attributes\": { … }, \"source\": { \"kind\", \"key\", \"label\" }, \"memberCount\", \"createdAtUtc\", \"updatedAtUtc\", \"rowVersion\", \"firstTouch\": { \"source\", \"medium\", \"campaign\", \"campaignId\" } }`; `firstTouch` is absent until a first touch is recorded."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`account_not_found`: no account with that external id in this organization.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`accounts_not_enabled`: the organization has not turned accounts on."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Audience: Accounts"
        ],
        "summary": "Create or update an account",
        "description": "Creates the account when no account has this external id, or updates it. `{ \"name\", \"attributes\": { … }, \"attribution\": { \"source\", \"medium\", \"campaign\" } }`: a `name` replaces the name (a new account without one is named by its external id); `attributes` are merged key by key over the existing values, so a key you do not send stays as it is and a key sent as `null` is cleared. Values are checked against the account fields the organization defines in Audience settings. `attribution` is the account's **first touch** (where it came from, for example the UTM values of the visit that started a trial): it is recorded once, on an account that has none, and never changed afterwards, so sending a different one later is ignored. Each member is at most 100 characters; a missing one is recorded as `(none)`. Sending the same body twice changes nothing.\n\nAccounts are optional and off until the organization turns them on in Audience settings; every account route answers `409 accounts_not_enabled` until then. An account is a group of people the organization tracks as one thing (a company, a household, a school). It is identified by an **external id** you supply (for example your own customer id), which is case-sensitive and unique in the organization: there is no fuzzy matching, so two accounts are the same account only when the external id is the same. A person can belong to any number of accounts, with an optional role in each.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "upsertAudienceAccount",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/UpsertAccountRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{ \"id\", \"externalId\", \"name\", \"attributes\": { … }, \"source\": { \"kind\", \"key\", \"label\" }, \"memberCount\", \"createdAtUtc\", \"updatedAtUtc\", \"rowVersion\", \"firstTouch\": { \"source\", \"medium\", \"campaign\", \"campaignId\" } }`; `firstTouch` is absent until a first touch is recorded. `201` when it was created, `200` when it already existed."
          },
          "400": {
            "description": "`validation_failed`: a field is invalid; a custom field is keyed `attributes.<key>`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`quota_exceeded`: creating this account would pass the plan's account limit (`audience.max_accounts`); `quotaKey` names it. Updating an existing account is never refused.\n\n`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`not_found`: no such route."
          },
          "409": {
            "description": "`accounts_not_enabled`: the organization has not turned accounts on."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Audience: Accounts"
        ],
        "summary": "Delete an account",
        "description": "Deletes the account and its memberships. **No person is touched**: they stay, with everything else Audience holds about them. This cannot be undone. It needs the `audience.management.erase` scope. Forgetting a person removes their memberships and never deletes an account.\n\n**Key:** an Audience management key with `audience.management.erase` (write also needs read; erase needs both).",
        "operationId": "deleteAudienceAccount",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No body."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`account_not_found`: no account with that external id in this organization.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`accounts_not_enabled`: the organization has not turned accounts on."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/accounts/{externalId}/members/{individualId}": {
      "put": {
        "tags": [
          "Audience: Accounts"
        ],
        "summary": "Add a person to an account",
        "description": "Adds the person to the account, or changes their role. `{ \"role\" }`: an optional label of up to 100 characters that you choose; Audience does not read it. The same call twice changes nothing. A person who was merged into another cannot be added.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "setAudienceAccountMember",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "individualId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/SetMemberRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{ \"accountId\", \"individualId\", \"added\" }`. `201` when the membership is new, `200` when it already existed."
          },
          "400": {
            "description": "`validation_failed`: the role is too long."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`account_not_found` or `individual_not_found`.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`accounts_not_enabled`: the organization has not turned accounts on."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Audience: Accounts"
        ],
        "summary": "Remove a person from an account",
        "description": "Removes the membership. The person and the account are untouched. Removing a membership that is not there answers `204` too: it is already the state you asked for.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "removeAudienceAccountMember",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "individualId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No body."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`account_not_found`: no account with that external id in this organization.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`accounts_not_enabled`: the organization has not turned accounts on."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/accounts/{externalId}/stages": {
      "get": {
        "tags": [
          "Audience: Accounts"
        ],
        "summary": "Get an account's lifecycle",
        "description": "Returns the account's journey: for each of the five stages, the open pass if there is one and the last ten closed ones, each with where it stands and the moves open to this key.\n\nAn account moves through the same five lifecycle stages a person does (awareness, acquisition, conversion, retention, advocacy), each with the states and moves the organization defined in Audience settings. An account has at most one open pass per stage. A key holds no role, so it can only fire a move that has no role restriction.\n\n**Key:** an Audience management key with `audience.management.read` (write also needs read; erase needs both).",
        "operationId": "getAudienceAccountStages",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"stages\": [ { \"stage\", \"open\", \"history\": [ … ] } ] }`, each pass as ``{ \"id\", \"stage\", \"openedAtUtc\", \"stateEnteredAtUtc\", \"closedAtUtc\", \"outcome\", \"state\": { \"id\", \"name\", … }, \"facts\": { \"source\", \"medium\", \"campaign\" }, \"availableTransitions\": [ … ], \"rowVersion\" }`. `facts` is the first touch the pass was opened with.`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`account_not_found`: no account with that external id in this organization.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`accounts_not_enabled`: the organization has not turned accounts on."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/accounts/{externalId}/stages/{stage}": {
      "post": {
        "tags": [
          "Audience: Accounts"
        ],
        "summary": "Open a pass for an account",
        "description": "Opens the account's pass in a stage (`awareness`, `acquisition`, `conversion`, `retention` or `advocacy`) at the first state of the organization's definition. `{ \"openedAt\" }` is optional: when the stage really began, as an ISO 8601 time, so a producer can report an activation or a payment after the fact. It cannot be in the future and cannot be more than a year before the account was created. Without it the pass starts now. The first touch recorded on the account is copied onto the pass.\n\nAn account moves through the same five lifecycle stages a person does (awareness, acquisition, conversion, retention, advocacy), each with the states and moves the organization defined in Audience settings. An account has at most one open pass per stage. A key holds no role, so it can only fire a move that has no role restriction.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "openAudienceAccountStage",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "stage",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/OpenAccountStageRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "`{ \"id\", \"stage\", \"openedAtUtc\", \"stateEnteredAtUtc\", \"closedAtUtc\", \"outcome\", \"state\": { \"id\", \"name\", … }, \"facts\": { \"source\", \"medium\", \"campaign\" }, \"availableTransitions\": [ … ], \"rowVersion\" }`. `facts` is the first touch the pass was opened with."
          },
          "400": {
            "description": "`validation_failed`: `openedAt` is in the future or too far before the account was created."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`account_not_found` or `stage_not_found`.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`stage_already_open`: the account already has an open pass in that stage; `rowId` names it. Or `accounts_not_enabled`."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/management/v1/accounts/{externalId}/stages/{stage}/rows/{rowId}/transitions": {
      "post": {
        "tags": [
          "Audience: Accounts"
        ],
        "summary": "Move an account's pass",
        "description": "Fires one of the moves open from the pass's current state. `{ \"transitionId\", \"comment\" }`: the `transitionId` is one of the pass's `availableTransitions`. A move into a final state closes the pass (completed or dropped).\n\nAn account moves through the same five lifecycle stages a person does (awareness, acquisition, conversion, retention, advocacy), each with the states and moves the organization defined in Audience settings. An account has at most one open pass per stage. A key holds no role, so it can only fire a move that has no role restriction.\n\n**Key:** an Audience management key with `audience.management.write` (write also needs read; erase needs both).",
        "operationId": "moveAudienceAccountStage",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "stage",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "rowId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoveStageRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"id\", \"stage\", \"openedAtUtc\", \"stateEnteredAtUtc\", \"closedAtUtc\", \"outcome\", \"state\": { \"id\", \"name\", … }, \"facts\": { \"source\", \"medium\", \"campaign\" }, \"availableTransitions\": [ … ], \"rowVersion\" }`. `facts` is the first touch the pass was opened with."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or it was not created from the Audience settings page."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`transition_not_allowed`: that move is restricted to roles, and a key holds none.\n\n`scope_denied`: the key lacks this route's scope. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Audience is not enabled for the organization."
          },
          "404": {
            "description": "`account_not_found` or `stage_row_not_found`.\n\n`not_found`: no such route."
          },
          "409": {
            "description": "`invalid_transition`, `stage_row_closed`, or `accounts_not_enabled`."
          },
          "429": {
            "description": "`rate_limited`: the key spent its requests-per-minute budget; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/delivery/v1/resolve": {
      "post": {
        "tags": [
          "Delivery: Audiences"
        ],
        "summary": "Resolve which audiences a visitor is in",
        "description": "Call this from the presentation app, once when a session starts and again before the token expires or when something may have changed (a form was submitted, a sign-in link was followed). It returns the audiences the visitor is in and a signed **audience token** that proves them to Content: send it in the `X-Ebitex-Audiences` header on your Content requests. Content trusts nothing else: a list of audiences the caller assembled itself, in `ctx` or anywhere, selects and unlocks nothing. Content's own delivery never calls this. The SDKs do all of this for you.\n\n`ctx` is a flat object of the session's own properties: a string, number, boolean or a list of those for each. Members under `individual` are reserved and refused. `visitor` is the device's visitor token and `consent` its raw consent signals (`cmp`, `gpc`, `dnt`, `cmpString`); Audience resolves consent itself and uses what it knows about a person only when recognition is granted on that device. Otherwise the context alone decides, and `recognised` is `false` with the `reason`. `audiences` optionally narrows the answer to the audiences your content uses (read them from `GET /content/delivery/v1/audiences`; at most 64), which keeps the token small. A request that would need more than 64 is refused, and a token never silently drops one.\n\nEvery visitor gets a token, an anonymous one included. Each grant carries an `assurance`: `verified` (a stored member who proved control of their email), `declared` (a stored member) or `context` (an audience worked out from the context you sent, which anyone can obtain, so treat it as public copy). Only stored audiences can restrict access. An audience flagged for **verified access** is granted only while the visitor holds a live proof of an email address the person holds.\n\nThe token lasts 15 minutes and carries no personal data and no visitor identity. For a visitor who is not recognised the answer is the same for every visitor with the same context, and Audience keeps it briefly; for a recognised person it is theirs alone. The response is `private, no-store`: do not cache it in a shared cache.\n\nThis key is its own kind. With no origin or IP restriction it is a server key; with one it may be used from a browser, and the restriction is enforced on every request.\n\n**Key:** audience.delivery.read",
        "operationId": "resolveAudiences",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolveAudiencesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"audiences\": [ \"<audience id>\", … ], \"recognised\": true, \"reason\": null, \"access\": { \"token\": \"a1.…\", \"expiresAtUtc\": \"…\", \"grants\": [ { \"audienceId\": \"…\", \"assurance\": \"verified\" | \"declared\" | \"context\" } ] } }`. `reason` is `token_invalid`, `session_token`, `revoked`, `recognition_not_granted` or `no_visitor` when a presented visitor was not recognised."
          },
          "400": {
            "description": "`validation_failed`: `ctx` is not a flat object, is too large (8 KiB, 64 members) or names a reserved `individual` member. `too_many_audiences`: more than 64 audiences were named, or the visitor holds more than 64 and none were named."
          },
          "401": {
            "description": "`invalid_api_key`: the key is missing, revoked, expired or not an Audience delivery key.\n\n`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`origin_denied` or `ip_denied`: the key's restriction does not admit this request. `app_not_available`.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "`rate_limited`: the key's per-minute budget is spent; `Retry-After` says when it resets.\n\nQuota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/delivery/v1/sign-in": {
      "post": {
        "tags": [
          "Delivery: Audiences"
        ],
        "summary": "Ask for a sign-in link",
        "description": "Call this when a visitor who is not let in by a **verified access** audience asks to sign in. It emails the address a single-use link, valid for 15 minutes. Following it and pressing the button on the page it opens proves the address for **that visitor token** (the one sent here), and the next `resolve` for it includes the audience as verified. There is no password, and nothing to store: resolve again after the visitor says they have followed the link.\n\n`email` is the address; `audienceId` optionally names the audience the visitor is trying to get into, so the link is sent only if the person is in it; `visitor` is the device's visitor token (either kind); `consent` is as for `resolve`. When the device has granted recognition the proof is remembered for 30 days, otherwise it lasts the session (at most 4 hours). `returnUrl` is where the page offers to take the visitor back: an `https` address (or `http` on localhost) without credentials, and, from a browser, the origin the request came from. It is shown as a link naming the site and is never followed automatically.\n\n**The answer never depends on the address.** It is always `202` with the same body, whether the address is unknown, a non-member, throttled, or the organization has no email channel, so the route cannot be used to find out who is a member. At most three links are sent to one address in an hour. The response is `private, no-store`.\n\n**Key:** audience.delivery.read",
        "operationId": "startAudienceSignIn",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StartSignInRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`202` with `{ \"message\": \"…\" }`, always the same text."
          },
          "401": {
            "description": "`invalid_api_key`: the key is missing, revoked, expired or not an Audience delivery key.\n\n`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`origin_denied` or `ip_denied`: the key's restriction does not admit this request. `app_not_available`.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "`rate_limited`: the key's per-minute budget is spent; `Retry-After` says when it resets.\n\nQuota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/delivery/v1/sign-in/handoff": {
      "post": {
        "tags": [
          "Delivery: Audiences"
        ],
        "summary": "Verify the browser that followed a link",
        "description": "A verification link opened on another device (a phone, from a mail) proves the device that *asked*. When that link had a `returnUrl`, the page it opens offers a \"Continue\" link that carries a one-time code in its URL fragment, `#ebitex-verify=<code>`. Call this from the page it leads to, with the code and **this** device's visitor token, and this device is proved for the same person and address as if it had followed the link, so the next `resolve` for it includes the audience as verified. `@ebitex/content-sdk` does it for you when the client has an `audience` option: it reads the fragment, removes it from the address bar, calls this and resolves again (`audience: { handoff: false }` turns that off).\n\nThe code is issued only when the person presses the button on the verification page (opening the link, as a mail scanner does, issues none), works once, and expires after 10 minutes. `visitor` is the device's visitor token (either kind) and `consent` is as for `resolve`: the proof is remembered for 30 days when the device has granted recognition, otherwise for the session (at most 4 hours).\n\n**The answer never depends on the code.** It is always `202` with the same body, whether the code was valid, used, expired or never issued, and whether the device was revoked, so the route cannot be used to find out anything. The response is `private, no-store`.\n\n**Key:** audience.delivery.read",
        "operationId": "redeemAudienceSignInHandoff",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HandoffRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`202` with `{ \"message\": \"…\" }`, always the same text."
          },
          "401": {
            "description": "`invalid_api_key`: the key is missing, revoked, expired or not an Audience delivery key.\n\n`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`origin_denied` or `ip_denied`: the key's restriction does not admit this request. `app_not_available`.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "`rate_limited`: the key's per-minute budget is spent; `Retry-After` says when it resets.\n\nQuota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/delivery/v1/visitors/session": {
      "post": {
        "tags": [
          "Delivery: Visitors"
        ],
        "summary": "Get or refresh a visitor token and the consent state",
        "description": "Call this when a page loads, and again whenever your consent tool's answer changes. It tells you whether this device may be recognised and gives you the token to use from now on. Nothing about the visitor is stored by this call.\n\n`visitor` is the token the device already holds, if any. `consent` is what you know about the visitor's consent: `cmp` (`granted` or `denied`, from your own consent banner), `gpc` and `dnt` (the browser's Global Privacy Control and Do Not Track settings, which are also read from the request's `Sec-GPC` and `DNT` headers) and `cmpString` (your banner's own consent string, kept as evidence). Send what you saw; ebitex decides. The organization's default applies when nothing is sent, and until an owner changes it that default is not to recognise.\n\n**Store the token only while `consent.recognition` is `granted`**, and remove it as soon as it is not. When recognition is granted you receive a long-lived `recognition` token. When it is not, you receive a `session` token that expires a few hours after it was issued, whatever the visitor does, and that must not be kept across visits. A token that has been forgotten, has expired or does not verify is never an error: the response carries a fresh token instead.\n\n`consent.messages` holds the organization's own wording, in the visitor's language, for a notice (`recognitionDeclined`), a confirmation (`forgetConfirm`) and a result (`forgetDone`). The response is `private, no-store`.\n\n**Key:** audience.delivery.read",
        "operationId": "startVisitorSession",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/VisitorSessionRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{ \"visitor\": \"v1.…\", \"consent\": { \"recognition\": \"granted\" | \"denied\", \"source\": \"cmp\" | \"globalPrivacyControl\" | \"doNotTrack\" | \"organizationDefault\", \"policyVersion\", \"tokenKind\": \"recognition\" | \"session\", \"tokenLifetimeSeconds\", \"messages\": { … } } }`."
          },
          "401": {
            "description": "`invalid_api_key`: the key is missing, revoked, expired or not an Audience delivery key.\n\n`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`origin_denied` or `ip_denied`: the key's restriction does not admit this request. `app_not_available`.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "413": {
            "description": "The body is larger than 16 KiB."
          },
          "429": {
            "description": "`rate_limited`: the key's per-minute budget is spent; `Retry-After` says when it resets.\n\nQuota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/delivery/v1/visitors/forget": {
      "post": {
        "tags": [
          "Delivery: Visitors"
        ],
        "summary": "Forget a device",
        "description": "Call this when a visitor asks to be forgotten: from a link in your footer, a button on a privacy page, or your own consent tool. The token stops working for good, and what that device unlocked is ended: the link between the device and a person in Audience, a connected contact, and any emailed-link verification. Remove the token from your own storage afterwards.\n\nForgetting a device is **not** erasing a person. The person, and anything they submitted, are left alone; erasing them is a separate action in Audience.\n\n`visitor` is the token the device holds. **The answer never depends on the token.** It is always `200` with the organization's own `forgetDone` wording, whether the token was valid, already forgotten, expired or made up, so the route cannot be used to find out which tokens are real. Calling it again is harmless. The response is `private, no-store`.\n\n**Key:** audience.delivery.read",
        "operationId": "forgetVisitor",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/VisitorForgetBody"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{ \"message\": \"…\" }`, the organization's `forgetDone` wording."
          },
          "401": {
            "description": "`invalid_api_key`: the key is missing, revoked, expired or not an Audience delivery key.\n\n`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`origin_denied` or `ip_denied`: the key's restriction does not admit this request. `app_not_available`.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "413": {
            "description": "The body is larger than 16 KiB."
          },
          "429": {
            "description": "`rate_limited`: the key's per-minute budget is spent; `Retry-After` says when it resets.\n\nQuota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/delivery/v1/interaction": {
      "post": {
        "tags": [
          "Delivery: Visitors"
        ],
        "summary": "Record something a recognised visitor did",
        "description": "Call this when a visitor does something your site wants on their timeline in Audience, for example `pricing-viewed`. It is **not** a page-view tracker: nothing is recorded for a visitor ebitex cannot attribute to a person, and each device may record at most 200 interactions in any 24 hours.\n\n`visitor` is the device's token and `consent` its raw consent signals, as for `visitors/session`. The interaction is recorded only when the device is **recognised**: a recognition token that has not been forgotten, consent granted on this device, and a link from the device to a person (made when your site or a form identifies them). Otherwise the answer is `202` with `recorded: false` and the reason (`no_visitor`, `token_invalid`, `session_token`, `revoked` or `recognition_not_granted`), and nothing is stored. This call never creates a person or links a device to one.\n\n`name` is lower-case words joined by hyphens, at most 59 characters. It is stored as the interaction kind `site.<name>`. `page-view` is refused. `properties` may hold only **declared visit properties**, typed as declared (declare them under **Settings**, **Visit properties** in the Audience app); any other member is refused, so free-form personal data cannot reach the timeline. The properties, nested at most 8 levels, must fit in 16 KiB. `interactionId` is optional: sending the same one again from the same device records nothing new and answers `duplicate: true`, so a retry is safe.\n\nA browser's `Sec-GPC` and `DNT` headers count even if you do not forward them. The response is `private, no-store`.\n\n**Key:** audience.delivery.read",
        "operationId": "recordVisitorInteraction",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/RecordInteractionRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`200` with `{ \"recorded\": true, \"duplicate\": false, \"reason\": null }` when the interaction is on the person's timeline (`duplicate` is `true` for a replay), or `202` with `{ \"recorded\": false, \"duplicate\": false, \"reason\": \"…\" }` when nothing was recorded."
          },
          "400": {
            "description": "`validation_failed` (a bad `name` or `interactionId`, or a property of the wrong type), `undeclared_property` (the `errors` member names each one) or `name_not_accepted` (`page-view`)."
          },
          "401": {
            "description": "`invalid_api_key`: the key is missing, revoked, expired or not an Audience delivery key.\n\n`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`origin_denied` or `ip_denied`: the key's restriction does not admit this request. `app_not_available`.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "413": {
            "description": "The body is larger than 16 KiB."
          },
          "429": {
            "description": "`interaction_cap_reached` (this device has recorded 200 interactions in the last 24 hours) or `rate_limited` (the key's per-minute budget is spent; `Retry-After` says when it resets).\n\nQuota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/audience/delivery/v1/event": {
      "post": {
        "tags": [
          "Delivery: Visitors"
        ],
        "summary": "Record a behavioural event for a recognised visitor",
        "description": "Call this when a visitor does something your site wants counted toward audiences, for example viewing a page in a category. An **event** is not an **interaction**: an interaction (`/interaction`) is a meaningful fact that appears on the person's timeline, while an event is a high-volume signal that is kept only briefly and shown nowhere as a row. Audiences read counts of events (for example \"viewed a page in Espresso at least three times in the last seven days\"), never the events themselves.\n\nSend one event (`kind`, `subject`, optional `value` and `eventId`) or up to 20 in `events`. `visitor` is the device's token and `consent` its raw consent signals, as for `visitors/session`. An event is recorded only when the device is **recognised**: a recognition token that has not been forgotten, consent granted on this device, and a link from the device to a person. Otherwise the answer is `202` with `recorded: false` and the reason, and **nothing is stored**: an anonymous visitor's events are never kept. This call never creates a person or links a device to one.\n\n`kind` is a registered kind. The first is `content.viewed`, whose `subject.categories` lists the page's category ids **including their ancestors** (a published page's `classification` already has them), at most 16, and whose optional `value` is seconds on the page, 0 to 86400. `eventId` makes a retry safe: the same one again for the same subject on the same day records nothing new.\n\nRecording is asynchronous and never slows your page: a `200` means the event was queued, and under heavy load it can be dropped (`event_queue_full`). A switched-off feature, an unconfigured store, an organization at its monthly allowance and a device over its daily soft limit all answer `202` with a reason, never an error. A browser's `Sec-GPC` and `DNT` headers count even if you do not forward them. The response is `private, no-store`.\n\n**Key:** audience.delivery.read",
        "operationId": "recordVisitorEvent",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/RecordEventRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`200` with `{ \"recorded\": true, \"reason\": null, \"results\": [{ \"recorded\": true, \"reason\": null }] }` when at least one event was queued, or `202` with `recorded: false` and a `reason` (`no_visitor`, `token_invalid`, `session_token`, `revoked`, `recognition_not_granted`, `events_disabled`, `events_unconfigured`, `event_allowance_reached`, `event_cap_reached`) when none was. `results` has one entry per event, in order."
          },
          "400": {
            "description": "`validation_failed` (an unregistered `kind`, a category that is not an id, none or more than 16 categories, a bad `value` or `eventId`, or more than 20 events; the `errors` member names each)."
          },
          "401": {
            "description": "`invalid_api_key`: the key is missing, revoked, expired or not an Audience delivery key.\n\n`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`origin_denied` or `ip_denied`: the key's restriction does not admit this request. `app_not_available`.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "413": {
            "description": "The body is larger than 32 KiB."
          },
          "429": {
            "description": "`rate_limited`: the key's per-minute budget is spent; `Retry-After` says when it resets.\n\nQuota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/items": {
      "get": {
        "tags": [
          "Management: Bundles and publishing"
        ],
        "summary": "List the environment's items",
        "description": "A flat inventory of the key's environment: every contract, template, component, adapter, stream, category group, category, folder and page node, by id and name. Use it to check whether something exists without exporting it.\n\n- `kind` narrows the listing to one of `contract`, `template`, `component`, `adapter`, `stream`, `categoryGroup`, `category`, `folder` or `node`.\n- `limit` is the page size, 1 to 200 (default 200). Pass `nextCursor` back as `cursor`, exactly as received, with the same `kind`; `null` means there are no more.\n\nEach item carries a `naturalKey`, the stable, environment-independent name an import plan reports and `baseTokens` is keyed by. Categories, folders and nodes have no `externalId`, so their `naturalKey` is a path. `parentId` is a component's folder, a folder's or node's parent, and a category's parent category or, at the top level, its group.\n\nComponents, folders and page nodes in a branch the key's role cannot view are left out, as they are from every other listing.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listManagementItems",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [ { \"kind\", \"id\", \"externalId\", \"naturalKey\", \"name\", \"parentId\" } ], \"nextCursor\": \"…\" | null }`"
          },
          "400": {
            "description": "`validation_failed`: an unknown `kind`; the response lists the valid ones. `invalid_cursor`: the cursor is malformed or belongs to a different listing; restart from the first page."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/export": {
      "post": {
        "tags": [
          "Management: Bundles and publishing"
        ],
        "summary": "Export a bundle",
        "description": "Builds a transfer bundle from the key's environment and returns it: the document `/import-plan` and `/import` take. Nothing is stored; the bundle exists only in the response.\n\nIn an organization with more than one realm, the bundle names the realm it was exported from (`source.realmKey`) and the realm that owns each item (`realmKey`), and includes the ancestor realms' items the export needs. An organization with one realm writes no keys at all.\n\nSend exactly one of:\n\n- `roots`: a list of `{ \"kind\", \"id\" }`. Each root brings its dependencies with it (the contracts, templates, adapters, categories and referenced components it needs). A page node does not bring its child pages or standalone components nobody references, so name every node, stream and standalone component you want as a root of its own.\n- `wholeEnvironment: true`: every item in the environment, including ones nothing references. This is how to adopt an existing environment into source control.\n\nA bundle carries blob ids, never blob bytes. Move media with `POST /blobs`.\n\nThe key acts as its role. Contracts, templates, adapters, streams and categories each need the role to hold that kind's manage permission (`content.contracts.manage`, `content.templates.manage`, `content.adapters.manage`, `content.streams.manage`, `content.taxonomy.manage`), and components and page nodes are subject to the branch access rules. The export is checked against everything the bundle would contain, dependencies included: exporting a component brings its contract, so it needs `content.contracts.manage`. A whole-environment export contains every item, so a key whose role cannot view some branch, or cannot manage some kind, cannot make one.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "exportManagementBundle",
        "responses": {
          "200": {
            "description": "The bundle document, as JSON. Its `formatVersion` says which bundle format it is written in."
          },
          "400": {
            "description": "`validation_failed`: no body, or not exactly one of `roots` and `wholeEnvironment: true`. `unknown_property`: the body has a property this API does not recognize; the response names it in `property`. `invalid_json`: the body is not valid JSON."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the bundle would contain a kind the key's role cannot manage; `permission` names the permission it lacks.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "The bundle would contain a component or page node in a branch the key's role cannot view."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/import-plan": {
      "post": {
        "tags": [
          "Management: Bundles and publishing"
        ],
        "summary": "Plan an import",
        "description": "Compares a bundle with the key's environment and reports, per item, what an import would do. It writes nothing, so it is safe to run from a pull-request check with a read-only key.\n\n`identityMode` says how bundle items are matched to what is already there: `externalId` (the default) matches on each item's own natural key, which is what a content model kept in source control needs; `preserve` matches on the bundle's ids; `fresh` treats every item as new.\n\nEach item's `state` is `new`, `identical`, `update` or `conflict`. A differing item is `update` only when you prove the environment has not changed since you last saw it: record each item's `stateToken` under its `naturalKey`, and send that map back as `baseTokens` next time. Without a matching token every differing item is a `conflict`. An item whose `realmKey` names a realm above the key's own is written into that realm, and its plan line says so with `realmKey` and `realmName`; every other item is written into the key's realm. Realm keys are ignored for `fresh` and for a bundle from another organization. `blockers` lists anything that would make `/import` refuse: a component owned by an ancestor realm that the bundle would overwrite, an item owned by an ancestor realm that this key may not write to, or an existing component or page that differs from the bundle while its publishing workflow holds it in a state that does not allow edits, such as review. `warnings` includes one line starting `quota:` for each plan limit (components, pages, categories) that the items in state `new` would take the organization past. It is a warning, not a blocker, because leaving items out of the import can bring it back under. Send `validate: true` to also check every document the way an import does, in a transaction that is rolled back: each document an import would refuse comes back as a warning starting `would_be_refused:`. It costs about as much as the import itself, so it is off by default, and a bundle that creates categories is not checked (a `validation_skipped:` warning says so). A clean plan does not promise a valid import: the plan limits are checked again at import, and so are the documents when `validate` was not set. Plan again before every import; a plan is only true of the environment as it was when it ran.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "planManagementImport",
        "responses": {
          "200": {
            "description": "`{ \"items\": [ { \"kind\", \"id\", \"externalId\", \"naturalKey\", \"name\", \"implicit\", \"state\", \"stateToken\" } ], \"warnings\": [ … ], \"blockers\": [ … ] }`. `implicit` marks an item that is present only as another item's dependency. `realmKey` and `realmName` appear only on an item written into a realm other than the key's own."
          },
          "400": {
            "description": "`validation_failed`: no body, `bundle` is not a bundle document, or `identityMode` is unknown. `unsupported_format_version`: the bundle's `formatVersion` is not one this server accepts; the response gives `supplied` and `supported`. `unknown_property`: the body has a property this API does not recognize; the response names it in `property`. `invalid_json`: the body is not valid JSON."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/import": {
      "post": {
        "tags": [
          "Management: Bundles and publishing"
        ],
        "summary": "Import a bundle",
        "description": "Writes a bundle into the key's environment. The server plans the import again itself, with the same `identityMode` and `baseTokens` you would send to `/import-plan`, and never trusts a plan you saw earlier.\n\n`items` selects what is written, as `{ \"kind\", \"id\", \"confirmOverwrite\" }` using the bundle's own ids. Name every item the plan did not call `identical`. An item the plan calls `new` must be named, or the import is refused, because nothing else would create it. An item planned as `conflict` is written only with `confirmOverwrite: true`: that flag means \"overwrite what is there\", so re-plan before reaching for it. Dependencies are still resolved across the whole bundle, so written items reference the right rows.\n\nThe import runs as a background job: this returns `202` with an operation id, and the content is not written when it returns. Poll `GET /operations/{id}` until the job succeeds or fails. A large bundle would otherwise hold one request open past the timeout of whatever proxy sits in front of the API.\n\nThe import is one transaction. If any selected item carries an invalid document, nothing is written and the job fails with an `error` listing every invalid item and its failing fields, so you can fix them all and resubmit once. If the items the import would create would take the organization past a plan limit on components, pages or categories, nothing is written and the job fails with `error: \"quota_exceeded\"` and a `quotas` list giving each exceeded limit's `quotaKey`, `limit`, `current` count and how many the import was `adding`. Only created items count, so an import that only updates existing content is never refused for this. Everything the server can refuse before starting — a blocked plan, an unconfirmed overwrite, an omitted new item, a malformed bundle — is still answered immediately, as an error status on this call rather than as a failed job.\n\nThe key acts as its role. Contracts, templates, adapters, streams and categories each need the role to hold that kind's manage permission (`content.contracts.manage`, `content.templates.manage`, `content.adapters.manage`, `content.streams.manage`, `content.taxonomy.manage`), and components and page nodes are subject to the branch access rules. Only the items `items` selects are checked, because only they are written: importing a component does not need `content.contracts.manage` unless its contract is selected too. A component needs `edit` both where it is now and in the folder the bundle files it in, since an import that changes its folder moves it. A page node needs `edit` on itself and on the parent it is filed under.\n\n**Key:** a management key with `content.management.write`.",
        "operationId": "importManagementBundle",
        "responses": {
          "200": {
            "description": "`{ \"id\", \"status\" }`. The finished job's `result` is `{ \"items\": [ { \"kind\", \"id\", \"imported\", \"error\" } ] }`."
          },
          "400": {
            "description": "`validation_failed`: no body, `bundle` is not a bundle document, or `identityMode` is unknown. `required_item_omitted`: items the plan calls `new` are missing from `items`; the response names them. `unsupported_format_version`: the bundle's `formatVersion` is not one this server accepts; the response gives `supplied` and `supported`. `unknown_property`: the body has a property this API does not recognize; the response names it in `property`. `invalid_json`: the body is not valid JSON."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: a selected item is of a kind the key's role cannot manage; `permission` names the permission it lacks. `access_denied`: the key's role cannot `edit` a folder or page node a selected item is in or would be filed under.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "A selected item is in, or would be filed under, a folder or page node the key's role cannot view."
          },
          "409": {
            "description": "`import_blocked`: the plan has blockers, listed in `blockers`; nothing is written, including items that are not blocked. `confirm_overwrite_required`: items differ from the environment and were not confirmed; they are listed in `items`. Re-plan before confirming."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/blobs": {
      "post": {
        "tags": [
          "Management: Bundles and publishing"
        ],
        "summary": "Upload a blob",
        "description": "Stores raw bytes as a blob. Send the file itself as the body with its media type as `Content-Type`; the response is the descriptor a media field stores.\n\nStorage is content-addressed within the organization, so uploading bytes it already holds stores nothing new and returns the existing blob's id. That makes it safe to upload a whole asset directory on every run. To find out what is already stored without uploading, use `GET /blobs`.\n\n**Key:** a management key with `content.management.write`.",
        "operationId": "uploadManagementBlob",
        "responses": {
          "200": {
            "description": "`{ \"blobId\": \"…\", \"contentType\": \"image/png\", \"sizeBytes\": 88213 }`"
          },
          "400": {
            "description": "`validation_failed`: no `Content-Type` header, or the bytes could not be stored."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "413": {
            "description": "`blob_too_large`: the upload is over the size limit given in `maxBytes`."
          },
          "429": {
            "description": "`quota_exceeded`: the organization's blob storage quota is full; the response gives the `quota` key and its `limit`.\n\nRate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "get": {
        "tags": [
          "Management: Bundles and publishing"
        ],
        "summary": "Find stored blobs by hash",
        "description": "Answers \"which of these files does the organization already hold?\" for one or more SHA-256 digests of the bytes, given as repeated `hash` parameters (64 hex characters each, up to 100 per request). It writes nothing, so a read-only key can resolve a content model's media ids and plan it without uploading.\n\nA digest with no stored bytes is simply absent from the result: that is the answer, not an error. Upload the missing files with `POST /blobs`.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "resolveManagementBlobs",
        "responses": {
          "200": {
            "description": "`{ \"blobs\": [ { \"hash\", \"blobId\", \"contentType\", \"sizeBytes\" } ] }`"
          },
          "400": {
            "description": "`validation_failed`: no `hash`, more than 100, or one that is not a 64-character hex digest (named in `hash`)."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/publish-plan": {
      "post": {
        "tags": [
          "Management: Bundles and publishing"
        ],
        "summary": "Plan a publish",
        "description": "Starts a job that works out what publishing some content would involve. `roots` is a list of `{ \"kind\", \"id\" }` naming components and page nodes. The plan resolves everything those roots depend on and reports, per item, its version, publish status, validation errors, and workflow state, including whether a workflow blocks publishing it.\n\n`deliveryEnvironmentId` names where to publish. It can be left out when the environment publishes to exactly one delivery environment; `GET /topology` lists them.\n\nThe job runs in the background. Poll `GET /operations/{id}` until its `status` is `succeeded`, then read the plan from its `result` and send the items you want to `POST /publish`.\n\n**Key:** a management key with `content.management.publish`.",
        "operationId": "planManagementPublish",
        "responses": {
          "202": {
            "description": "`{ \"id\": \"…\", \"status\": \"queued\" }`: the job's id, to poll. The finished job's `result` is `{ \"items\": [ { \"kind\", \"id\", \"name\", \"path\", \"headVersionNumber\", \"publishStatus\", \"validAgainstOwnPin\", \"ownPinErrors\", \"validAgainstLatest\", \"latestErrors\", \"workflowDefinitionName\", \"workflowStateName\", \"workflowPublishBlocked\", \"activeRunPinnedVersionNumber\" } ], \"danglingReferences\": [ … ] }`."
          },
          "400": {
            "description": "`validation_failed`: no body. `delivery_target_not_mapped`: `deliveryEnvironmentId` is not a delivery environment this environment publishes to. `no_delivery_target_mapped`: this environment publishes to no delivery environment. `ambiguous_delivery_target`: it publishes to several, listed in `mappedDeliveryEnvironmentIds`; name one as `deliveryEnvironmentId`. `unknown_property`: the body has a property this API does not recognize; the response names it in `property`. `invalid_json`: the body is not valid JSON."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          },
          "503": {
            "description": "`bulk_operations_paused`: background jobs are temporarily paused. Retry later."
          }
        }
      }
    },
    "/content/management/v1/publish": {
      "post": {
        "tags": [
          "Management: Bundles and publishing"
        ],
        "summary": "Publish content",
        "description": "Starts a job that publishes specific versions of components and page nodes. `items` is a list of `{ \"kind\", \"id\", \"versionNumber\", \"include\" }`, normally built from a finished plan: use the plan item's `activeRunPinnedVersionNumber` when it has one and its `headVersionNumber` otherwise. Send the same `deliveryEnvironmentId` the plan used.\n\n**Set `include: true` on every item you want published.** An item without it is skipped, and a job that skips everything still succeeds having published nothing. Do not pass plan items through unchanged: their extra properties are refused.\n\nThe job runs in the background; poll `GET /operations/{id}`. It publishes items in order and stops at the first one it cannot publish. Items before that one stay published, `result` lists them, and the operation fails with `error` naming the blocking item and the reason: `access_denied`, `workflow_not_publishable`, `workflow_pinned_version_mismatch` (a workflow has pinned a different version), `version_not_found`, `publish_blocked` (the content does not validate) or `quota_exceeded`.\n\n**Key:** a management key with `content.management.publish`.",
        "operationId": "publishManagementContent",
        "responses": {
          "202": {
            "description": "`{ \"id\": \"…\", \"status\": \"queued\" }`: the job's id, to poll. The finished job's `result` is `{ \"items\": [ { \"kind\", \"id\", \"published\", \"warnings\" } ] }`."
          },
          "400": {
            "description": "`validation_failed`: no body. `delivery_target_not_mapped`: `deliveryEnvironmentId` is not a delivery environment this environment publishes to. `no_delivery_target_mapped`: this environment publishes to no delivery environment. `ambiguous_delivery_target`: it publishes to several, listed in `mappedDeliveryEnvironmentIds`; name one as `deliveryEnvironmentId`. `unknown_property`: the body has a property this API does not recognize; the response names it in `property`. `invalid_json`: the body is not valid JSON."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          },
          "503": {
            "description": "`bulk_operations_paused`: background jobs are temporarily paused. Retry later."
          }
        }
      }
    },
    "/content/management/v1/operations/{id}": {
      "get": {
        "tags": [
          "Management: Bundles and publishing"
        ],
        "summary": "Get a job's status",
        "description": "Reports a background job started by `POST /publish-plan`, `POST /publish` or `POST /import`, by the `id` the start call returned. `status` moves from `queued` to `running` and ends at `succeeded` or `failed`.\n\nPoll until it finishes. A succeeded job's `result` holds its output: the plan, the list of published items, or the per-item import outcomes. A failed job's `error` says why — for an import refused on document validation it is the full per-item list — and `result` may still list work completed before the failure. Only jobs in the key's own environment can be read.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getManagementOperation",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"id\", \"kind\": \"publish-plan\" | \"publish-execute\" | \"import-execute\", \"status\", \"result\", \"error\" }`. `result` and `error` are both JSON objects, not strings."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "`operation_not_found`: no job with this id exists in the key's environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/topology": {
      "get": {
        "tags": [
          "Management: Key"
        ],
        "summary": "Get the environment's neighbours",
        "description": "Where the key's authoring environment sits: the delivery environments it publishes to, the environments it promotes to and is promoted from.\n\nOnly direct neighbours are reported, each by id and name only. Nothing here lets the key act on a neighbour: every write still lands in the key's own environment. The platform does not record which environment is production, so that is not reported either.\n\nUse it to pick a `deliveryEnvironmentId` before publishing when there is more than one delivery target, and to learn whether content you write here is promoted into other environments.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getManagementTopology",
        "responses": {
          "200": {
            "description": "`{ \"environment\": { \"id\", \"name\", \"kind\": \"authoring\" }, \"publishesTo\": [ { \"id\", \"name\" } ], \"promotesTo\": [ … ], \"promotedFrom\": [ … ] }`. Lists are empty rather than absent, and ordered the way the environments are ordered in Content."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/whoami": {
      "get": {
        "tags": [
          "Management: Key"
        ],
        "summary": "Describe the calling key",
        "description": "Reports what the key you are calling with is: the organization it belongs to, the authoring environment it reads and writes, the realm it acts in, the role it acts as, its own id and name, its scopes, and the permissions of the role it acts as.\n\nA management key is an opaque string, so call this before acting on a key you did not mint yourself: it tells you where your writes will land, and which scopes you hold, so you can skip calls that would only be refused. A scope is the key's ceiling and a permission is the role's, so a key with the authoring scope can still be refused: creating a Contract needs the `content.contracts.manage` permission, and `permissions` is how to find out first. It works with any valid key, whatever its scopes, so a key that can only publish can still find out that publishing is all it can do.\n\n**Key:** any valid management key, whatever its scopes.",
        "operationId": "getManagementKey",
        "responses": {
          "200": {
            "description": "`{ \"organizationId\", \"organizationName\", \"organizationSlug\", \"environmentId\", \"environmentName\", \"realmId\", \"realmKey\", \"realmName\", \"roleId\", \"roleName\", \"keyId\", \"keyName\", \"scopes\": [ … ], \"permissions\": [ … ] }`. `scopes` and `permissions` are sorted; `permissions` is absent when the role is not known. `realmKey` is `root` for a key minted before realms; a key is bound to one realm, so the realm is the key's and is never chosen per request."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/blobs/{id}": {
      "get": {
        "tags": [
          "Management: Bundles and publishing"
        ],
        "summary": "Download a blob",
        "description": "Streams a stored blob's bytes with its stored content type, by the `blobId` an upload or a media field gave you. Blobs belong to the organization, so a blob uploaded through any environment's key can be read.\n\nThis returns stored bytes whether or not anything using them is published. For media on a live page, use the public URL delivery gives a media field instead.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "downloadAuthoringBlob",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No blob with this id exists in the organization. The response has no body."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/contracts": {
      "get": {
        "tags": [
          "Management: Contracts"
        ],
        "summary": "List contracts",
        "description": "Lists the contracts in the key's environment, ordered by name, as summaries: `id`, `externalId`, `name`, `isAbstract`, `parentContractId` and `defaultFolderId`. A contract synced from an external content provider carries `providerSourceKey`, and every contract carries `realmId`, the realm that owns it; a contract synced from a provider is read-only here, and so is one owned by a realm above the key's own.\n\n`category` takes a category id and is repeatable; a contract must be tagged with every one given. Sub-categories are not included.\n\nPaging is by number: `page` starts at 1 and `pageSize` defaults to 20. A `pageSize` above 100 is reduced to 100, and the response's own `pageSize` is the one used, so page on that rather than on what you asked for. Offset paging is not stable while the environment is being written to: a row created or deleted ahead of your position shifts every later page, so a walk can repeat some rows and skip others. To enumerate everything, for example to build an external id to id index before an apply, use `GET /items` with `kind`, which pages by cursor and neither repeats nor skips a row.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listContracts",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [ … ], \"totalCount\": 42, \"page\": 1, \"pageSize\": 20 }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "post": {
        "tags": [
          "Management: Contracts"
        ],
        "summary": "Create a contract",
        "description": "Creates a contract: a content type and the ordered list of fields its components carry. Send `name`, `externalId` (lowercase kebab-case, 3 to 64 characters, unique in the environment), `isAbstract`, an optional `parentContractId` to inherit another contract's fields, and `fields`. Each field names its `fieldTypeKey` (see `listFieldTypes`), its own kebab-case `externalId`, its modifiers (`isMandatory`, `isEnumerable`, `isLocalizable`, `isContextual`, `isPersonalizable`), `settings` matching that field type's settings schema, and an optional `defaultValue`. A field may not reuse an external id an ancestor already declares.\n\nA field's allow-list settings (`allowedContractIds`, `allowedTemplateIds`, `allowedCategoryGroupIds`, `allowedKinds`, `allowedInlineKinds`, `allowedProtocols`, `allowedContentTypes` and `markdownFeatures`) have three states: leave the setting out and anything is allowed, give a list and exactly those are allowed, give an empty list and nothing is allowed. An empty list is never the same as leaving the setting out. `allowedModes` is the one exception: it must allow at least one mode, so leave it out to allow either, and an empty list is refused.\n\n`titleFieldPath` optionally names the field that titles this contract's pages in navigation: a field external id, or up to four joined with `.` to reach into inline values. Only its shape is checked, so a path may name a field that does not exist yet; a path that never resolves falls back to the page's own name.\n\n`referenceTree` optionally tells the component editor to draw fields that reference components of the same kind, such as a pedigree's `sire` and `dam` or a bill of materials' `components`, as one editable tree. It is `{ \"label\": \"Pedigree\", \"fields\": [\"sire\", \"dam\"], \"depth\": 4, \"repeats\": \"allow\", \"layout\": \"chart\" }`, where `fields` lists field external ids, `depth` is 2 to 6 (default 4), `repeats` is `allow` (default) or `never` (each component may appear once in the tree) and `layout` is `chart` or `outline`. It changes how the fields are edited and nothing else: it creates no contract version and changes no component or delivery response. A loop, where a component would become its own ancestor, is always refused when a component is written, including a loop that runs through a value described inline in one of the named fields: the editor draws through inline values, so the check follows them. An empty `fields` list opts out of a setting inherited from a parent. Only its shape is checked, so it may name a field that does not exist yet.\n\n`externalLinks` optionally lists up to five links the component editor shows for a component of this contract, each `{ \"label\": \"Print pedigree\", \"url\": \"{origin}/archive/{id}/certificate\", \"site\": \"Drakeshead\", \"requires\": \"published\" }`. The `url` is a template that begins with `{origin}`, `https://` or `http://` and may use `{id}`, `{externalId}`, `{field:<path>}` (a field external id, or up to four joined with `.`) and `{key:<path>}` (the key of the reference at that path; empty when none is chosen). `{origin}` stands for the address of the Site named in `site` and must be followed by `/`; a token is never allowed in the scheme or host of a literal address, so a field value cannot change where a link points. `requires` is `none` (default) or `published`, which enables the link once the component is published. Like `referenceTree` it is presentation only: no contract version, no component change and no delivery response. An empty list opts out of a setting inherited from a parent. A save replaces the whole setting, so send it on every update or it is cleared.\n\nA new contract always starts at version 1. Add `?dryRun=true` to run the same validation and change classification without saving anything. The response is `{ classification, currentLatestVersionNumber, resultingVersionNumber, breakingReasons }`, where `classification` is `new`, `additive`, `breaking` or `noChange`, and each breaking reason is `{ fieldExternalId, reason }`.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "createContract",
        "parameters": [
          {
            "name": "dryRun",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateContractRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The new contract, shaped like `getContract`, with `201 Created`. With `dryRun=true`, `200` and the change classification instead."
          },
          "400": {
            "description": "`validation_failed`: the body is invalid. `errors` maps each failing member (such as `externalId` or `fields[2].settings`) to a message. Common causes: an unknown field type, settings that do not match the field type's schema, a missing or cyclic parent, or a duplicate external id."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.contracts.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "409": {
            "description": "`duplicate_external_id`: another contract in this environment already has this external id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/contracts/{id}": {
      "get": {
        "tags": [
          "Management: Contracts"
        ],
        "summary": "Get a contract",
        "description": "Returns one contract with its own fields in order, its `latestVersion` (`versionNumber`, `isBreakingFromPrevious` and the `compiledSchema` a component document is validated against), and the `rowVersion` to send back when you update it.\n\n`fields` lists only the fields this contract declares itself; inherited fields are part of `latestVersion.compiledSchema`. `titleFieldPath` is this contract's own declaration, while `effectiveTitleFieldPath` and `titleFieldDeclaredByContractId` report the one that applies after inheritance. `referenceTree`, `effectiveReferenceTree` and `referenceTreeDeclaredByContractId` work the same way for the tree setting, and `externalLinks`, `effectiveExternalLinks` and `externalLinksDeclaredByContractId` for the links.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getContract",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No contract with this id exists in the key's environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Contracts"
        ],
        "summary": "Update a contract",
        "description": "Replaces a contract's definition. The body has the same members as a create, and `fields` is the complete new list in order: a field left out is removed. Send the `rowVersion` from your last read so an edit somebody else made in between is refused rather than overwritten.\n\nEach save is classified against the current definition. A **breaking** change (removing a field, adding a mandatory one, changing the parent, tightening a setting) cuts a new version, and every contract that inherits from this one gets a new version too; components already pinned to an older version keep validating against it. An **additive** change updates the latest version in place, and a save that changes nothing writes no version. When the contract is used as a template's settings contract, each such template also records a new version.\n\nAdd `?dryRun=true` to run the same validation and change classification without saving anything. The response is `{ classification, currentLatestVersionNumber, resultingVersionNumber, breakingReasons }`, where `classification` is `new`, `additive`, `breaking` or `noChange`, and each breaking reason is `{ fieldExternalId, reason }`.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "updateContract",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateContractRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated contract, shaped like `getContract`. With `dryRun=true`, the change classification instead."
          },
          "400": {
            "description": "`validation_failed`: the body is invalid. `errors` maps each failing member (such as `externalId` or `fields[2].settings`) to a message."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.contracts.manage`. `inherited_owner_not_permitted`: the item is owned by a realm above the key's own (`ownerRealmId`). A person can edit it from the app and the change is written in the realm that owns it, but a key acts in one realm and never writes through to another: change it with a key bound to the owning realm.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No contract with this id exists in the key's environment."
          },
          "409": {
            "description": "`concurrency_conflict`: the contract changed since `rowVersion`; the body carries `currentRowVersion` and `currentUpdatedAtUtc`, so re-read and reapply your change. `duplicate_external_id`: another contract already has this external id. `contract_read_only`: the contract is synced from a content provider and changes only when that provider is refreshed."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Management: Contracts"
        ],
        "summary": "Delete a contract",
        "description": "Deletes a contract and its version history. It is refused while anything depends on it: a contract that inherits from it, a component of it, a folder or template that lists it as allowed or supported, a template using it as its settings contract, an adapter reading or producing it, a stream sourcing it, or a field that names it in `allowedContractIds`.\n\n`getContractUsages` lists the same dependents with names, so you can remove them first.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "deleteContract",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "`204 No Content`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.contracts.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No contract with this id exists in the key's environment."
          },
          "409": {
            "description": "`has_dependents`: other data depends on this contract. `blockers` groups the dependents' ids by reason: `has_children`, `has_components` (a count), `referenced_by_folder_constraints`, `referenced_by_template_constraints`, `referenced_by_template_settings`, `referenced_by_adapter_constraints`, `referenced_by_streams`, `referenced_by_proposals` and `referenced_by_field_constraints`. `contract_read_only`: the contract is synced from a content provider. `inherited_read_only`: the contract is owned by a realm above the key's own."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/contracts/{id}/versions": {
      "get": {
        "tags": [
          "Management: Contracts"
        ],
        "summary": "List contract versions",
        "description": "Lists a contract's versions, newest first, each with `versionNumber`, `isBreakingFromPrevious` and `createdAtUtc`. A component pins the version it was saved against, so an older version stays valid for the components that use it.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listContractVersions",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No contract with this id exists."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/contracts/{id}/versions/{versionNumber}": {
      "get": {
        "tags": [
          "Management: Contracts"
        ],
        "summary": "Get a contract version",
        "description": "Returns one version of a contract as it was frozen: `resolvedFieldsSnapshot` (every field, inherited ones included), the `compiledSchema` documents pinned to this version are validated against, and the `inheritanceChainSnapshot`. `fieldNames` maps each field's external id to its current display name, since the snapshot itself carries none.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getContractVersion",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "versionNumber",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No contract with this id exists, or it has no version with this number."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/contracts/{id}/usages": {
      "get": {
        "tags": [
          "Management: Contracts"
        ],
        "summary": "List where a contract is used",
        "description": "Lists everything that depends on a contract: the same dependents that block `deleteContract`, with names. Each item is `{ kind, id, name, detail, path }`, where `kind` is `contract`, `component`, `folder`, `template`, `adapter` or `stream` and `detail` says why it depends on this contract. For a component, `path` is the name of its folder.\n\nThe list is not narrowed by folder access, so it always explains a refused delete.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getContractUsages",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [ { \"kind\": \"component\", \"id\": \"…\", \"name\": \"…\", \"detail\": \"…\", \"path\": \"…\" } ] }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No contract with this id exists in the key's environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/field-types": {
      "get": {
        "tags": [
          "Management: Contracts"
        ],
        "summary": "List field types",
        "description": "Lists the field types a contract field can use in this organization, including any a plugin contributes. Each has a `key` (the `fieldTypeKey` a field names), a `displayName`, the JSON Schema its `settings` must match (`settingsSchema`), and `isDeprecated`.\n\nA deprecated type still works for a field that already has it, but a contract save that gives a new field one is refused with a `400` naming its successor, for example `component` in place of `reference` or `nested`. Read this list rather than hard-coding field types or their settings: it is the authoritative description of what a field can hold.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listFieldTypes",
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/folders": {
      "get": {
        "tags": [
          "Management: Folders"
        ],
        "summary": "List folders",
        "description": "Lists every folder in the key's authoring environment that the key's role can view, as a flat list ordered by name. Rebuild the tree from `parentFolderId`, which is `null` for a top-level folder.\n\nEach folder carries `effectiveActions` (what the key's role may do in it: `view`, `edit`, `delete`, `publish`) and `effectiveAllowedContractIds`: the contracts components in it must satisfy, taken from the nearest folder up the tree that declares any, or `null` when anything is allowed.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listFolders",
        "responses": {
          "200": {
            "description": "`[ { \"id\": \"…\", \"name\": \"Blog\", \"parentFolderId\": null, \"effectiveActions\": [\"view\", \"edit\"], \"effectiveAllowedContractIds\": null } ]`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "post": {
        "tags": [
          "Management: Folders"
        ],
        "summary": "Create a folder",
        "description": "Creates a folder from `{ name, parentFolderId }` and answers `201 Created` with it. Omit `parentFolderId` for a top-level folder.\n\nCreating inside a folder needs `edit` on that folder under the branch access rules, which apply to the key as they do to its role. The new folder inherits its parent's access rules and allowed contracts until it declares its own.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "createFolder",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateFolderRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may view the parent folder but not edit it.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "`parentFolderId` names no folder, or one the key's role cannot view."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/folders/counts": {
      "get": {
        "tags": [
          "Management: Folders"
        ],
        "summary": "Count components per folder",
        "description": "Counts the components each folder holds directly, in one request, so a folder tree can show badges without listing every folder's contents.\n\n`byFolder` has an entry for every folder the key's role can view, zero for an empty one. `unfiled` counts components filed at the top level and `total` is `unfiled` plus every visible folder's own count. Components in a branch the key's role cannot view are in none of the three, so the numbers never reveal hidden content. A folder's count does not include its subfolders; add them up from `parentFolderId` if you want a branch total.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getFolderCounts",
        "responses": {
          "200": {
            "description": "`{ \"total\": 29, \"unfiled\": 2, \"byFolder\": { \"<folder id>\": 14 } }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/folders/{id}": {
      "put": {
        "tags": [
          "Management: Folders"
        ],
        "summary": "Rename or move a folder",
        "description": "Sets a folder's `name` and `parentFolderId`. This is a full replace: omitting `parentFolderId` moves the folder to the top level. A folder cannot be moved into itself or one of its own descendants.\n\nThe key needs `edit` on the folder, and on the new parent when moving it. Moving a folder changes which access rules and allowed contracts its contents inherit. Folders owned by a realm above the key's own are read-only here.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "updateFolder",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateFolderRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`cycle`: the new parent is the folder itself or one of its descendants."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not edit the folder or the new parent. `inherited_owner_not_permitted`: the item is owned by a realm above the key's own (`ownerRealmId`). A person can edit it from the app and the change is written in the realm that owns it, but a key acts in one realm and never writes through to another: change it with a key bound to the owning realm.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No folder has this id in the key's environment, it is in a branch the key's role cannot view, or `parentFolderId` names no folder."
          },
          "409": {
            "description": "`inherited_read_only`: the folder is owned by a realm above the key's own (`ownerRealmId`) and cannot be moved here; renaming it is written through to the owning realm from the app."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Management: Folders"
        ],
        "summary": "Delete a folder",
        "description": "Deletes an empty folder and answers `204 No Content`. A folder that still contains child folders or components is refused: move or delete its contents first.\n\nThe key needs `delete` on the folder under the branch access rules. Folders owned by a realm above the key's own cannot be deleted here.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "deleteFolder",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may view the folder but not delete it.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No folder has this id in the key's environment, or it is in a branch the key's role cannot view."
          },
          "409": {
            "description": "`not_empty`: the folder contains child folders or components. `inherited_read_only`: the folder is owned by a realm above the key's own."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/folders/{id}/access-rules": {
      "get": {
        "tags": [
          "Management: Folders"
        ],
        "summary": "Get a folder's access rules",
        "description": "The access rules declared directly on this folder, as role and action pairs. Actions are `view`, `edit`, `delete` and `publish`.\n\nRules cascade down the tree per action, and the nearest folder that declares any rule for an action decides it: only the roles listed there may take that action. A folder with no rules for an action inherits its parent's, and an action no folder restricts is open to every role. This returns only what is declared here, not what is inherited.\n\nNeeds the key's role to hold `content.access.manage`.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getFolderAccessRules",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`[ { \"roleDefinitionId\": \"…\", \"action\": \"edit\" } ]`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.access.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No folder has this id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Folders"
        ],
        "summary": "Replace a folder's access rules",
        "description": "Replaces every rule declared on this folder with `{ rules: [ { roleDefinitionId, action } ] }`. An empty list removes them all, so the folder inherits from its parent again.\n\n`edit`, `delete` and `publish` each imply `view`: a role given any of them is also given `view`, and the response shows the stored set with those rows added. Restricting `view` hides the folder and everything under it from every role not listed, except roles holding `content.access.manage`, which bypass all branch rules.\n\nNeeds the key's role to hold `content.access.manage`.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "replaceFolderAccessRules",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplaceAccessRulesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The rules as stored, implied `view` rows included."
          },
          "400": {
            "description": "`validation_failed`: an action other than `view`, `edit`, `delete` or `publish`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.access.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No folder has this id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/folders/{id}/allowed-contracts": {
      "get": {
        "tags": [
          "Management: Folders"
        ],
        "summary": "Get a folder's allowed contracts",
        "description": "The contracts declared directly on this folder as the only ones its components may use. An empty list means this folder declares nothing and inherits from the nearest folder above that does; `effectiveAllowedContractIds` on the folder listing gives the resolved answer.\n\nA component satisfies the constraint when its contract is one of those listed or inherits from one. The constraint is checked when a component is created in or moved into the folder; components already there are not rechecked, and report `conformsToFolderConstraint`.\n\nNeeds the key's role to hold `content.access.manage`.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getFolderAllowedContracts",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`[ { \"contractId\": \"…\" } ]`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.access.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No folder has this id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Folders"
        ],
        "summary": "Replace a folder's allowed contracts",
        "description": "Replaces this folder's allowed contracts with `{ contractIds: [ … ] }`. Duplicates are ignored, and an empty list removes the constraint so the folder inherits from its parent again. Every id must name an existing contract.\n\nComponents already in the folder are not moved or rejected; the new constraint applies to the next create or move.\n\nNeeds the key's role to hold `content.access.manage`.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "replaceFolderAllowedContracts",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplaceAllowedContractsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The allowed contracts as stored: `[ { \"contractId\": \"…\" } ]`"
          },
          "400": {
            "description": "`validation_failed`: one or more contract ids do not exist."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.access.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No folder has this id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/components": {
      "get": {
        "tags": [
          "Management: Components"
        ],
        "summary": "List components",
        "description": "Lists the components in the key's authoring environment, drafts included, as summaries without their documents. Components in branches the key's role cannot view are left out, and `totalCount` counts only the ones it can see.\n\n- `folderId` lists one folder's direct contents; `rootOnly=true` lists only components filed at the root.\n- `contractId` matches one contract exactly; `adheresToContractId` also matches every contract that inherits from it. Repeat `adheresToContractId` to match several contracts and everything inheriting from any of them, as in `?adheresToContractId=a&adheresToContractId=b`.\n- `categoryId` matches components tagged with, or holding a category field value of, that category or any of its sub-categories.\n- `search` is a case-insensitive substring match on name and external id; `externalId` is an exact match.\n- `sort` is `name` (the default) or `updated` (most recently changed first).\n- `publishStatus` is `draft`, `published`, `modified` or `unpublished`; repeat it to match several, as in `?publishStatus=draft&publishStatus=modified`. It filters before paging, so `totalCount` is the filtered count.\n\nEach summary carries the pinned and latest contract version numbers, whether it satisfies its folder's allowed contracts, and `effectiveActions`: what the key's role may do to it.\n\nPaging is by number: `page` starts at 1 and `pageSize` defaults to 20. A `pageSize` above 100 is reduced to 100, and the response's own `pageSize` is the one used, so page on that rather than on what you asked for. Offset paging is not stable while the environment is being written to: a row created or deleted ahead of your position shifts every later page, so a walk can repeat some rows and skip others. To enumerate everything, for example to build an external id to id index before an apply, use `GET /items` with `kind`, which pages by cursor and neither repeats nor skips a row.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listAuthoringComponents",
        "parameters": [
          {
            "name": "folderId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "rootOnly",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "contractId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "categoryId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "externalId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "adheresToContractId",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "publishStatus",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [ … ], \"totalCount\": 42, \"page\": 1, \"pageSize\": 20 }`"
          },
          "400": {
            "description": "`invalid_sort`: `sort` is neither `name` nor `updated`. `invalid_publish_status`: a `publishStatus` is not one of the four statuses."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "post": {
        "tags": [
          "Management: Components"
        ],
        "summary": "Create a component",
        "description": "Creates a component of a contract from `{ name, externalId, contractId, folderId, document }` (and, optionally, `madeFor`, an optional advisory note of the channels it is made for, a list drawn from `email` and `web`. It never blocks a save or a publish, and an empty or missing note means unknown), and answers `201 Created` with the full component. Omit `folderId` to file it at the root. The component is pinned to the contract's latest version, and `document` must validate against it: an invalid document is refused with each failing field's path in `errors`.\n\nThe contract cannot be abstract, and it must satisfy the target folder's allowed contracts (the nearest folder up the tree that declares any). `externalId` must be unique in the environment.\n\nCreating into a folder needs `edit` on that folder under the branch access rules, which apply to the key as they do to its role. The organization's plan caps how many components it may hold across all its environments.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "createComponent",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateComponentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "OK"
          },
          "400": {
            "description": "`validation_failed`: `errors` names each problem, by field path for the document, or under `contractId` when the contract does not exist or has no version. `abstract_contract`: an abstract contract cannot be instantiated; use one that inherits from it. `folder_contract_constraint_violation`: the folder does not allow components of this contract."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may view the folder but not edit it. `quota_exceeded`: the organization has reached its plan's component limit.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "`folderId` names no folder in this environment, or one the key's role cannot view."
          },
          "409": {
            "description": "`duplicate_external_id`: a component with this external id already exists in the environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/components/{id}": {
      "get": {
        "tags": [
          "Management: Components"
        ],
        "summary": "Get a component",
        "description": "Returns one component's current authoring state: its `document`, contract, folder, and the contract version it is pinned to. It is the working copy, not what is published; `publishStatus` is `draft`, `published`, `modified` or `unpublished`.\n\nWhen a newer contract version exists, `validatesAgainstLatest` says whether the document would pass it, and `latestValidationErrors` lists what would fail. Keep `rowVersion` to send back with an update.\n\nBranch access rules apply to the key as they do to its role: a component in a folder the role cannot view reads as not found. `effectiveActions` says what the role may do to this component.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getAuthoringComponent",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No component has this id in the key's environment, or it is in a branch the key's role cannot view."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Components"
        ],
        "summary": "Update a component",
        "description": "Replaces a component's `name`, `folderId` and `document`. This is a full replace: omitting `folderId` moves the component to the root. The document is validated against the component's pinned contract version; send `repinToLatest: true` to validate against the contract's latest version instead and, on success, move the pin there. The external id and the contract cannot be changed.\n\n`madeFor` is an optional advisory note of the channels it is made for, a list drawn from `email` and `web`. It never blocks a save or a publish, and an empty or missing note means unknown; unlike the members above it is not a full replace: omit it to leave the note alone, send `[]` to clear it, and an unknown channel is refused with `400 validation_failed`.\n\nWhen a document fails against a pinned version that is not the contract's latest, the `400` also carries `pinnedContractVersion`, `latestContractVersion`, `validatesAgainstLatest` (whether the same document passes the latest version) and a `message` saying what to send. A document that looks right against the contract you just read, but fails, is usually this case.\n\nSend the `rowVersion` from your last read to make the update conditional: if the component changed in the meantime the write is refused with `409 concurrency_conflict` and the current `currentRowVersion`. Without it, the last write wins.\n\nThe key needs `edit` on the component's folder, and on the target folder too when moving it, which must also allow the component's contract. When a workflow governs the component, it can only be edited in an editable workflow state. A component owned by a realm above the key's own can be edited from the app (the change is written in the realm that owns it), but a key never writes through to another realm.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "updateComponent",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateComponentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`validation_failed`: the document does not validate; `errors` names each failing field's path, and a failure against a pin behind the contract's latest also carries `pinnedContractVersion`, `latestContractVersion`, `validatesAgainstLatest` and `message`. `folder_contract_constraint_violation`: the target folder does not allow components of this contract."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not edit the component's folder or the target folder. `inherited_owner_not_permitted`: the item is owned by a realm above the key's own (`ownerRealmId`). A person can edit it from the app and the change is written in the realm that owns it, but a key acts in one realm and never writes through to another: change it with a key bound to the owning realm.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No component has this id in the key's environment, it is in a branch the key's role cannot view, or `folderId` names no folder."
          },
          "409": {
            "description": "`concurrency_conflict`: the component changed since `rowVersion` was read; re-read it and retry. `inherited_read_only`: the move is not allowed because the component is owned by a realm above the key's own (`ownerRealmId`); an inherited component stays in the folder its source put it in."
          },
          "422": {
            "description": "`workflow_state_not_editable`: the component's workflow state (`state`, in `definition`) does not allow edits; move it to an editable state first."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Management: Components"
        ],
        "summary": "Delete a component",
        "description": "Deletes a component and answers `204 No Content`. The delete is refused while anything still depends on it: another component or a page referencing it, an Adapter looking it up by its rules, a live published copy in any delivery environment, or a translation job still in progress. Remove the references, unpublish it, or let the job finish, then try again. `GET …/usages/closure` lists everything that holds it.\n\nThe key needs `delete` on the component's folder under the branch access rules. Components owned by a realm above the key's own cannot be deleted here.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "deleteComponent",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may view the component but not delete it.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No component has this id in the key's environment, or it is in a branch the key's role cannot view."
          },
          "409": {
            "description": "`referenced_by`: still referenced; `usages` lists the components and pages that reference it and `adapters` the Adapters that look it up. `component_published`: still published in the `deliveryEnvironments` listed; unpublish it first. `component_has_translation_jobs`: a translation job for it is still in progress. `inherited_read_only`: the component is owned by a realm above the key's own."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/components/move": {
      "post": {
        "tags": [
          "Management: Components"
        ],
        "summary": "Move components to a folder",
        "description": "Moves up to 200 components into the folder `folderId`, or to the top level when it is omitted or `null`. Send `{ \"componentIds\": [\"…\"], \"folderId\": \"…\" }`.\n\nEach component goes through the same checks as an update that changes only the folder: the key needs `edit` on the component's folder and on the target, the target must allow the component's contract, a workflow must leave it editable, and a component owned by a realm above the key's own is read-only. A move is not all-or-nothing: the answer lists what moved and, for each component that did not, the code an update would have returned. Moving records a new version of the component, as any change to its folder does.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "moveComponents",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoveComponentsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"moved\": [\"…\"], \"skipped\": [ { \"id\": \"…\", \"error\": \"folder_contract_constraint_violation\", \"message\": \"…\" } ] }`. A skipped `error` is one of `already_there`, `not_found`, `access_denied`, `folder_contract_constraint_violation`, `inherited_read_only`, `workflow_state_not_editable` or `conflict`."
          },
          "400": {
            "description": "`invalid_request`: `componentIds` is empty or has more than 200 ids."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/components/{id}/usages": {
      "get": {
        "tags": [
          "Management: Components"
        ],
        "summary": "List a component's references",
        "description": "Lists every component and page whose content references this component, with the referencing field's path. A page entry carries `nodeId` and its `nodePath`; a component entry carries `componentId`. These are the references that block a delete.\n\nThe list is not filtered by branch access, so it stays complete even when a referencing component sits in a branch the key's role cannot view. Only ids, names and field paths are returned, never the referencing content.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listComponentUsages",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`[ { \"componentId\": \"…\", \"nodeId\": null, \"name\": \"Home hero\", \"fieldPath\": \"hero\", \"nodePath\": null } ]`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No component has this id, or it is in a branch the key's role cannot view."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/components/{id}/usages/closure": {
      "get": {
        "tags": [
          "Management: Components"
        ],
        "summary": "Get everything that holds a component",
        "description": "Everything that would stop this component being deleted, in one list. Each item has a `kind`:\n\n- `component` and `node`: content referencing it. `detail` names the field, or the Template and Adapter it is bound through; `path` is the component's folder or the page's path.\n- `adapter`: an Adapter whose rules look it up.\n- `publication`: a delivery environment where it is still published. Unpublish to release it.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getComponentUsageClosure",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [ { \"kind\": \"node\", \"id\": \"…\", \"name\": \"About\", \"detail\": \"Bound via Template \\\"Statement\\\"\", \"path\": \"/about\" } ] }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No component has this id, or it is in a branch the key's role cannot view."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/templates": {
      "get": {
        "tags": [
          "Management: Templates"
        ],
        "summary": "List templates",
        "description": "Lists the templates in the key's environment, ordered by name. Each summary carries `id`, `externalId`, `name`, `supportedContractIds`, `settingsContractId` and `latestVersionNumber`, plus `realmId`, the realm that owns it. A template owned by a realm above the key's own is read-only here.\n\nPaging is by number: `page` starts at 1 and `pageSize` defaults to 20. A `pageSize` above 100 is reduced to 100, and the response's own `pageSize` is the one used, so page on that rather than on what you asked for. Offset paging is not stable while the environment is being written to: a row created or deleted ahead of your position shifts every later page, so a walk can repeat some rows and skip others. To enumerate everything, for example to build an external id to id index before an apply, use `GET /items` with `kind`, which pages by cursor and neither repeats nor skips a row.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listTemplates",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [ … ], \"totalCount\": 12, \"page\": 1, \"pageSize\": 20 }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "post": {
        "tags": [
          "Management: Templates"
        ],
        "summary": "Create a template",
        "description": "Creates a template: a way of presenting content of the contracts it supports, with settings an editor fills in each time it is used. Send `name`, `externalId` (lowercase kebab-case, 3 to 64 characters, unique in the environment) and `supportedContractIds`.\n\nGive the settings in exactly one of two ways. `settingsFields` takes a field list, shaped like a contract's `fields`, and creates a new settings contract named after the template with the external id `template-settings-<template id>`. `settingsContractId` reuses an existing contract, which must not be abstract or inherited. Either way the settings contract is an ordinary contract: edit its fields with `updateContract`, share it between templates, and it outlives the template.\n\nA new template always starts at version 1. Add `?dryRun=true` to run the same validation and change classification without saving anything. The response is `{ classification, currentLatestVersionNumber, resultingVersionNumber, breakingReasons }`, where `classification` is `new`, `additive`, `breaking` or `noChange`, and each breaking reason is `{ fieldExternalId, reason }`.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "createTemplate",
        "parameters": [
          {
            "name": "dryRun",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTemplateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The new template, shaped like `getTemplate`, with `201 Created`. With `dryRun=true`, `200` and the change classification instead."
          },
          "400": {
            "description": "`validation_failed`: the body is invalid. `errors` maps each failing member (such as `externalId` or `fields[2].settings`) to a message. Includes sending both or neither of `settingsFields` and `settingsContractId`, an unknown supported contract, and a settings contract that is missing, abstract or inherited."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.templates.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "409": {
            "description": "`duplicate_external_id`: another template in this environment already has this external id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/templates/{id}": {
      "get": {
        "tags": [
          "Management: Templates"
        ],
        "summary": "Get a template",
        "description": "Returns one template with its `supportedContractIds`, its `settingsContractId` and that contract's current `settingsFields`, its `latestVersion` (with the settings contract version it pins and the settings schema), and the `rowVersion` to send back when you update it.\n\n`settingsFields` is for reading only. To change the settings, update the settings contract itself.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getTemplate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No template with this id exists in the key's environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Templates"
        ],
        "summary": "Update a template",
        "description": "Updates a template's `name`, `externalId` and `supportedContractIds`. `supportedContractIds` is the complete new list. Send the `rowVersion` from your last read so an edit somebody else made in between is refused rather than overwritten.\n\nSettings fields are not edited here; update the settings contract with `updateContract`. To point the template at a different existing contract, send `settingsContractId`; leaving it out keeps the current one. Repointing is always a breaking change and cuts a new version. Otherwise the change is classified from the supported-contract list: a breaking change cuts a new version, and presentations already pinned to the old one keep it.\n\nAdd `?dryRun=true` to run the same validation and change classification without saving anything. The response is `{ classification, currentLatestVersionNumber, resultingVersionNumber, breakingReasons }`, where `classification` is `new`, `additive`, `breaking` or `noChange`, and each breaking reason is `{ fieldExternalId, reason }`.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "updateTemplate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "dryRun",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateTemplateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated template, shaped like `getTemplate`. With `dryRun=true`, the change classification instead."
          },
          "400": {
            "description": "`validation_failed`: the body is invalid. `errors` maps each failing member (such as `externalId` or `fields[2].settings`) to a message."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.templates.manage`. `inherited_owner_not_permitted`: the item is owned by a realm above the key's own (`ownerRealmId`). A person can edit it from the app and the change is written in the realm that owns it, but a key acts in one realm and never writes through to another: change it with a key bound to the owning realm.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No template with this id exists in the key's environment."
          },
          "409": {
            "description": "`concurrency_conflict`: the template changed since `rowVersion`; the body carries `currentRowVersion` and `currentUpdatedAtUtc`, so re-read and reapply your change. `duplicate_external_id`: another template already has this external id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Management: Templates"
        ],
        "summary": "Delete a template",
        "description": "Deletes a template and its version history. It is refused while a component or page presents content through it, while a presentation field lists it in `allowedTemplateIds`, or while a page restricts its children to it.\n\nThe settings contract is not deleted with the template; delete it separately if nothing else uses it. `getTemplateUsageClosure` lists what blocks the delete.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "deleteTemplate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "`204 No Content`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.templates.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No template with this id exists in the key's environment."
          },
          "409": {
            "description": "`has_dependents`: other data depends on this template. `blockers` groups the dependents by reason: `used_by_presentations`, `referenced_by_field_constraints` (contract ids) and `referenced_by_node_restrictions` (page ids). `inherited_read_only`: the template is owned by a realm above the key's own."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/templates/{id}/versions": {
      "get": {
        "tags": [
          "Management: Templates"
        ],
        "summary": "List template versions",
        "description": "Lists a template's versions, newest first, each with `versionNumber`, `isBreakingFromPrevious` and `createdAtUtc`. A presentation pins the template version it was saved against.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listTemplateVersions",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No template with this id exists."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/templates/{id}/versions/{versionNumber}": {
      "get": {
        "tags": [
          "Management: Templates"
        ],
        "summary": "Get a template version",
        "description": "Returns one version of a template as it was frozen: its `supportedContractIds`, the `settingsContractVersionNumber` it pins, and that settings version's resolved fields and compiled schema, which settings saved against this version are validated against.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getTemplateVersion",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "versionNumber",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No template with this id exists, or it has no version with this number."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/templates/{id}/usages": {
      "get": {
        "tags": [
          "Management: Templates"
        ],
        "summary": "List a template's presentations",
        "description": "Lists every presentation that uses the template: each component field or page that presents content through it. Each item carries `componentId` or `nodeId` (exactly one is set), `name`, `fieldPath`, the `pinnedVersionNumber`, and, for a page, its `nodePath`.\n\nThis is the `used_by_presentations` blocker of `deleteTemplate`. For every dependent, constraints included, use `getTemplateUsageClosure`. The list is not narrowed by folder access.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getTemplateUsages",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No template with this id exists."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/templates/{id}/usages/closure": {
      "get": {
        "tags": [
          "Management: Templates"
        ],
        "summary": "List where a template is used",
        "description": "Lists everything that depends on a template: presentations in components and pages, contracts whose presentation fields allow it, and pages that restrict their children to it. These are the dependents that block `deleteTemplate`.\n\nEach item is `{ kind, id, name, detail, path }`, where `kind` is `component`, `node` or `contract`, `detail` says why it depends on the template, and `path` is a page's path. The list is not narrowed by folder access.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getTemplateUsageClosure",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [ { \"kind\": \"node\", \"id\": \"…\", \"name\": \"…\", \"detail\": \"…\", \"path\": \"/about\" } ] }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No template with this id exists in the key's environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/sites": {
      "get": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "List sites",
        "description": "Lists the sites in the key's authoring environment, in their authored order. A site is the root node of a page tree: its id is the node id every other Experience route takes, and the root id delivery calls `site`.\n\nOnly sites the key's role can view are listed. `effectiveActions` says what the role may do on each (`view`, `edit`, `delete`, `publish`).\n\nPass `details=true` to add `hostnames` (the hosts mapped to the site in the delivery environments this environment targets, in order) and `pageCount` (every node under the site root) to each entry.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listAuthoringSites",
        "parameters": [
          {
            "name": "details",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`[{ \"id\": \"…\", \"name\": \"Marketing\", \"effectiveActions\": [\"view\", \"edit\"] }]`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "post": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Create a site",
        "description": "Creates a site: a new root node, with only a `name`. A site has no slug, and its path is always `/`. It is added at the end of the site order.\n\nThe new site has no payload and no hostnames. Add pages under it with `POST /experience/nodes`, set its own page with `PUT /experience/nodes/{id}/payload`, and publish it before delivery can serve it.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "createSite",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSiteRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created. The new site as a node, in the shape `GET /experience/nodes/{id}` returns."
          },
          "400": {
            "description": "`validation_failed`: `name` is missing or blank."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`quota_exceeded`: the organization has reached its plan's limit on Experience nodes.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "409": {
            "description": "`duplicate_site_name`: a site with this name already exists in the environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/nodes/{id}": {
      "get": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Get a node",
        "description": "Returns one node in the authoring environment: a site root or any page beneath it.\n\n- `slug` is `{ \"default\": \"about-us\", \"locales\": { \"fr\": \"a-propos\" } }`, or `null` on a site root. `path` is the node's address computed from its ancestors' slugs.\n- `payloadKind` is `presentation` (a page; the envelope is in `presentationPayload`), `redirect` (with `redirectTargetNodeId`), or `null` for a node with nothing to serve.\n- `effectiveAllowedTemplateIds` is the Template restriction that applies here, inherited from the nearest node that sets one, or `null` for none.\n- `publishStatus` is `draft`, `published`, `modified` or `unpublished`.\n- `rowVersion` is the value to send back on an update or move to detect a concurrent edit.\n- `excludeFromSitemap` is the node's own setting; `sitemapExcludedByAncestor` names the ancestor that excludes it, if any.\n- `madeFor` is the advisory channel note, an empty list when unknown.\n\nThe key acts as its role, so branch access rules apply: a node the role cannot view answers `404`, and one it can view but not change answers `403 access_denied`.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getNode",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not view this node.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id in the key's environment, or the key's role cannot see it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Update a node",
        "description": "Replaces a node's `name` and `slug`. Both are required and replace what is stored; a site root takes a `name` only and must not send a slug. A slug value is lowercase kebab-case, 3 to 64 characters, in the default and in every locale override, and must be unique among the node's siblings in every locale.\n\n`excludeFromSitemap` keeps the page out of sitemaps; omit it to leave the current setting alone. `madeFor` is an optional advisory note of the channels it is made for, a list drawn from `email` and `web`. It never blocks a save or a publish, and an empty or missing note means unknown; omit it to leave the note alone and send `[]` to clear it. Send `rowVersion` from your last read to refuse the write if someone else changed the node since.\n\nThis route never touches the payload; use `PUT /experience/nodes/{id}/payload` for that. A changed slug moves the page's address when it is republished, and the old published path then redirects to the new one. Nothing here reaches delivery until the node is published. The key acts as its role, so branch access rules apply: a node the role cannot view answers `404`, and one it can view but not change answers `403 access_denied`. A workflow can also lock the node against edits.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "updateNode",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateNodeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`validation_failed`: a missing name, a missing or malformed slug, or a slug on a site root; `errors` names each field. `reserved_path`: the resulting path is reserved and cannot be used by a page."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not edit this node.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id, or the key's role cannot see it."
          },
          "409": {
            "description": "`duplicate_site_name`: another site already has this name. `duplicate_slug`: a sibling already resolves to this slug; `localeSlot` and `conflictingNodeId` say where. `concurrency_conflict`: the node changed since `rowVersion`; re-read it (`currentRowVersion` is in the body) and retry."
          },
          "422": {
            "description": "`workflow_state_not_editable`: the node's workflow state does not allow edits; `state` and `definition` name it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Delete a node",
        "description": "Deletes a node, together with the access rules and Template restriction set on it. A site root is deleted the same way.\n\nDeletion is refused while anything still depends on the node: children (delete or move them first), a redirect or a link field pointing at it (see `GET /experience/nodes/{id}/usages`), or a live published version in any delivery environment (unpublish it first). The key acts as its role, so branch access rules apply: a node the role cannot view answers `404`, and one it can view but not change answers `403 access_denied`.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "deleteNode",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not delete this node.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id, or the key's role cannot see it."
          },
          "409": {
            "description": "`not_empty`: the node has children. `has_dependents`: redirects or link fields target it; `redirected_from` lists them. `node_published`: the node is live in the delivery environments `deliveryEnvironments` names; unpublish it there first."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/nodes/{id}/children": {
      "get": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "List a node's children",
        "description": "Lists a node's direct children, in their authored order. Walk a tree one level at a time from a site id returned by `GET /experience/sites`.\n\nChildren the key's role cannot view are left out. Each item carries `id`, `name`, `slug`, `parentNodeId`, `payloadKind`, `effectiveActions` and `publishStatus`; fetch a node for its path and payload.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listNodeChildren",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not view this node.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id, or the key's role cannot see it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/nodes/category-filter": {
      "get": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Find nodes by category",
        "description": "Finds the nodes classified under a category, given as `categoryId`. Sub-categories count, and so does a category chosen in a category field of the node's payload, as well as a category the node is tagged with.\n\n`matchingNodeIds` are the matches the key's role can view. `ancestorNodeIds` are the ancestors of those matches, so a tree loaded one level at a time can be expanded down to each of them.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "filterNodesByCategory",
        "parameters": [
          {
            "name": "categoryId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"matchingNodeIds\": [\"…\"], \"ancestorNodeIds\": [\"…\"] }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/nodes": {
      "post": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Create a node",
        "description": "Creates a page under `parentNodeId`, with a `name` and a `slug` (`{ \"default\": \"about-us\", \"locales\": { \"fr\": \"a-propos\" } }`; `locales` may be empty). Slug values are lowercase kebab-case, 3 to 64 characters, and must be unique among siblings in every locale. The node is added at the end of its parent's children.\n\nA new node has no payload, so it is an address with nothing to serve until you set one with `PUT /experience/nodes/{id}/payload`. To create a site, use `POST /experience/sites`. Nothing here reaches delivery until the node is published.\n\nThe key's role needs `edit` on the parent.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "createNode",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateNodeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created. The new node, in the shape `GET /experience/nodes/{id}` returns."
          },
          "400": {
            "description": "`validation_failed`: a missing name or slug, or a malformed slug; `errors` names each field. `reserved_path`: the resulting path is reserved and cannot be used by a page."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not edit the parent. `quota_exceeded`: the organization has reached its plan's limit on Experience nodes.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has the id `parentNodeId`, or the key's role cannot see it."
          },
          "409": {
            "description": "`duplicate_slug`: a sibling already resolves to this slug; `localeSlot` and `conflictingNodeId` say where."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/nodes/{id}/move": {
      "put": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Move a node",
        "description": "Moves a node, with everything beneath it, under `newParentNodeId`. It is added at the end of the new parent's children; follow with `PUT /experience/nodes/reorder` to put it somewhere else. A site root cannot be moved.\n\nThe key's role needs `edit` on the node and on its new parent. Send `rowVersion` from your last read to refuse the move if the node changed since. The page's published address changes only when it is republished.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "moveNode",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoveNodeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`invalid_move`: the node is a site root, or the new parent is the node itself or one of its descendants."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not edit the node or the new parent.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id or `newParentNodeId`, or the key's role cannot see it."
          },
          "409": {
            "description": "`duplicate_slug`: a child of the new parent already resolves to this node's slug; `localeSlot` and `conflictingNodeId` say where. `concurrency_conflict`: the node changed since `rowVersion`; re-read it and retry."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/nodes/reorder": {
      "put": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Reorder sibling nodes",
        "description": "Sets the order of one parent's children. Send `parentNodeId` and `orderedNodeIds`, which must list **every** current child of that parent exactly once, in the order you want. A `null` `parentNodeId` orders the environment's sites instead.\n\nA list that is missing a child or names one that is not there is refused: someone else has changed the tree, so list the children again and retry. The key's role needs `edit` on the parent, or on every site when ordering sites.\n\nOrder reaches delivery navigation when the reordered pages are republished.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "reorderNodes",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReorderNodesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The reordered children, in their new order, in the shape `GET /experience/nodes/{id}/children` returns."
          },
          "400": {
            "description": "`incomplete_sibling_set`: `orderedNodeIds` does not match the parent's current children; re-read them with `GET /experience/nodes/{id}/children` and retry."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not edit the parent, or one of the sites.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "The parent, or one of the sites, does not exist or the key's role cannot see it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/nodes/{id}/usages": {
      "get": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "List what points at a node",
        "description": "Lists what targets this node: redirect nodes, and components or nodes whose link fields point at it. Each of these blocks the node's deletion.\n\nEach item is `{ nodeId, name, path }`. For a component, `nodeId` holds the component's id and `path` is `null`. For which field each reference sits in, and where the node is published, use `GET /experience/nodes/{id}/usages/closure`.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listNodeUsages",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not view this node.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id, or the key's role cannot see it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/nodes/{id}/usages/closure": {
      "get": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Get where a node is used",
        "description": "Everything that would stop this node being deleted, with detail. Each item is `{ kind, id, name, detail, path }`:\n\n- `node`: a redirect to this node, or a node whose link field targets it. `path` is that node's path.\n- `component`: a component whose link field targets it. `detail` names the field and `path` the component's folder.\n- `publication`: a delivery environment where the node is live; `id` is that environment. Unpublish it there before deleting.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getNodeUsageClosure",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [{ \"kind\": \"node\", \"id\": \"…\", \"name\": \"Old about page\", \"detail\": \"Redirects here\", \"path\": \"/about-us\" }] }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not view this node.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id, or the key's role cannot see it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/nodes/{id}/payload": {
      "put": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Set a node's payload",
        "description": "Sets what a node serves, replacing whatever it served before. `kind` picks one of two shapes:\n\n- `presentation`: a page. `presentation` is a Presentation envelope — `template` (a Template id), `templateVersion`, `settings`, and a `component` binding, either a reference to an existing component (`provider`, `key`, `contextualValues`) or an inline one. It is validated like a Presentation field, and the Template must be allowed at this node.\n- `redirect`: `targetNodeId` names the node to redirect to, in the same environment.\n\nA node's name and slug are set with `PUT /experience/nodes/{id}`, never here. Nothing here reaches delivery until the node is published. The key acts as its role, so branch access rules apply: a node the role cannot view answers `404`, and one it can view but not change answers `403 access_denied`. A workflow can also lock the node against edits.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "setNodePayload",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetPayloadRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The node, in the shape `GET /experience/nodes/{id}` returns."
          },
          "400": {
            "description": "`validation_failed`: an unknown `kind`, the member that kind needs is missing, the envelope is invalid (each problem is listed in `errors` under a path starting `payload`), or `targetNodeId` names no node. `invalid_redirect_target`: a node cannot redirect to itself."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not edit this node.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id, or the key's role cannot see it."
          },
          "422": {
            "description": "`workflow_state_not_editable`: the node's workflow state does not allow edits; `state` and `definition` name it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Clear a node's payload",
        "description": "Removes a node's page or redirect, leaving an address with nothing to serve. Its children and their addresses are unaffected. Once published, the node's own path no longer answers with a page.\n\nThe key acts as its role, so branch access rules apply: a node the role cannot view answers `404`, and one it can view but not change answers `403 access_denied`. A workflow can also lock the node against edits.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "clearNodePayload",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The node, in the shape `GET /experience/nodes/{id}` returns."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role may not edit this node.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id, or the key's role cannot see it."
          },
          "422": {
            "description": "`workflow_state_not_editable`: the node's workflow state does not allow edits; `state` and `definition` name it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/nodes/{id}/access-rules": {
      "get": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Get a node's access rules",
        "description": "The access rules set directly on this node, as `[{ roleDefinitionId, action }]`, where `action` is `view`, `edit`, `delete` or `publish`. Rules apply to the node and everything beneath it, and for each action the nearest node that sets a rule for it wins. An empty list means the node inherits.\n\nThe key's role needs the `content.access.manage` permission.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getNodeAccessRules",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.access.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Replace a node's access rules",
        "description": "Replaces every rule set directly on this node with `rules`, a list of `{ roleDefinitionId, action }`. `edit`, `delete` and `publish` each imply `view`, which is added for you. An empty list removes the node's own rules, so it inherits again.\n\nRules also govern the key itself, since it acts as a role: removing its role's `edit` here stops the key editing this branch. The key's role needs the `content.access.manage` permission.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "replaceNodeAccessRules",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplaceAccessRulesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The rules now set on the node, including any implied `view` rules."
          },
          "400": {
            "description": "`validation_failed`: an action other than `view`, `edit`, `delete` or `publish`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.access.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/nodes/{id}/allowed-templates": {
      "get": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Get a node's allowed Templates",
        "description": "The Template ids set directly on this node as its restriction. A restriction applies to the node and everything beneath it, and the nearest node that sets one wins. An empty list means the node sets none; `effectiveAllowedTemplateIds` on the node says what actually applies.\n\nThe key's role needs the `content.access.manage` permission.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getNodeAllowedTemplates",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.access.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Experience"
        ],
        "summary": "Replace a node's allowed Templates",
        "description": "Replaces the node's own Template restriction with `templateIds`. A page payload set at this node or below must then use one of these Templates, unless a nearer node sets its own list. An empty list removes the restriction here.\n\nExisting payloads are not re-checked; the restriction applies the next time each one is set. The key's role needs the `content.access.manage` permission.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "replaceNodeAllowedTemplates",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplaceAllowedTemplatesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The Template ids now set on the node."
          },
          "400": {
            "description": "`validation_failed`: one or more Template ids do not exist in the environment."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.access.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/preview/resolve": {
      "post": {
        "tags": [
          "Management: Checks and previews"
        ],
        "summary": "Resolve a draft preview",
        "description": "Resolves a component or page as delivery would, from its current saved draft rather than its published version. Changes nothing.\n\nSend `kind` (`component` or `node`) and `id`. To preview unsaved edits, send them as `document`: a component's whole document, or `{ \"payload\": … }` for a page. `ctx` is the visitor context used for personalization — omit it, or send `{}`, when there is none. `locale` picks localized values, and a `resolveDepth` above 0 expands references from their drafts as well. Links are composed as a delivery environment would compose them; name it with `deliveryEnvironmentId` when the key's environment publishes to more than one.\n\nA third `kind`, `presentation`, previews one presentation inside another entity's document, shown as a page would show it. Send `owner` (`{ \"kind\": \"component\" or \"node\", \"id\": … }`, the entity whose document holds it), `path` (where it sits in that document, an array of field names and positions; or send `subject`, a presentation envelope you built, instead) and, optionally, `document` (the owner's unsaved draft). `wrap` is the context, outermost first, at most 3 deep: each link names a `template` and `templateVersion`, its `settings`, and the `field` of its content Contract that holds the next link or the presentation (give `contract` and `contractVersion` when the Template supports any Contract). With `annotate`, every annotation inside the presentation names the owner and the owner's own path, and a context link is marked `synthetic: true` with no path.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "resolvePreview",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PreviewResolveRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The resolved document."
          },
          "400": {
            "description": "`validation_failed`: `kind` is not `component`, `node` or `presentation`; for `presentation`, `owner` is not a component or node, `path` holds no presentation in the owner's document, a `wrap[i].template` does not accept the presentation inside it, a `wrap[i].contract` is missing or not one the Template supports, or `wrap` is deeper than 3. `invalid_context`: `ctx` is not a valid context bag. `delivery_target_not_mapped`: `deliveryEnvironmentId` is not one the key's environment publishes to. `ambiguous_delivery_target`: several are mapped; name one."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: a branch access rule stops the key's role viewing this component or page.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No component or page has this id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/taxonomy/groups": {
      "get": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "List category groups",
        "description": "Lists the category groups the key's environment uses, ordered by external id. A group is one dimension of classification, such as `topic` or `region`, and its external id is the first segment of every category path inside it (`topic/news/local`).\n\nCategory groups and categories are the organization's vocabulary, shared by every app and every environment, and managed in Hub. An environment sees only the groups that have been added to it, and this surface lists and acts on those. Send `scope=available` to list the organization's groups this environment does not use yet instead.\n\nEach group carries `metadataFields`: the fields every category in the group must supply as `metadata` in this environment, or an empty list when the group has none here. `sourceRegistrationId` is set when a content provider supplies the group.\n\nA group the organization has given custom fields in Hub also carries `platformMetadataFields`, those fields as `{ key, label, fieldType, settings, isMandatory, isEnumerable, isLocalizable }`. They are the organization's own, the same in every environment and every app, and are separate from `metadataFields`. The member is left out when the group has none, and this API reads it but does not change it.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listCategoryGroups",
        "parameters": [
          {
            "name": "scope",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "An array of `{ id, externalId, name, metadataContractId, sourceRegistrationId, metadataFields, rowVersion, isBound, categoryCount, usageCount }`. `isBound` says whether the environment uses the group; `usageCount` is how many things in the environment still keep it there. `platformMetadataFields` is added only for a group with organization custom fields (see above). Send `rowVersion` back when renaming the group to detect a concurrent change."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "post": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Create a category group",
        "description": "Creates a group in the organization's vocabulary from `externalId` and `name`, and adds it to the key's environment in the same request, with optional `metadataFields` there. `externalId` is lowercase kebab-case, 3 to 64 characters, unique in the organization, and cannot be changed afterwards, since it is part of every category path in the group.\n\n`metadataFields` takes the same field definitions a contract does. When present, every category in the group must supply a `metadata` document in this environment that satisfies them; when absent, categories carry no metadata here.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "createCategoryGroup",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCategoryGroupRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "`201 Created` with the new group, shaped as in the list."
          },
          "400": {
            "description": "`validation_failed`: `errors` names each problem, such as an invalid or duplicate `externalId`, a missing `name`, or an invalid metadata field (reported as `metadataFields[n]…`)."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.taxonomy.manage`. `permission_denied`: the key's role lacks `platform.taxonomy.manage`, which changing the organization's category groups and categories needs.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/taxonomy/groups/{id}": {
      "put": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Rename a category group",
        "description": "Changes the group's display `name`, the only property this route changes — in the organization's vocabulary, so in every environment and app at once. The external id, and so every category path, stays the same.\n\nSend the `rowVersion` you last read to be told when someone else has renamed the group since. Omit it and the last write wins.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "renameCategoryGroup",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCategoryGroupRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated group, shaped as in the list."
          },
          "400": {
            "description": "`validation_failed`: `name` is missing or blank."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `platform.taxonomy.manage`, which changing the organization's category groups and categories needs.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No group has this id in the key's environment."
          },
          "409": {
            "description": "`concurrency_conflict`: the `rowVersion` sent is not the group's current one, so someone else has changed it; `currentRowVersion` is what it is now. `taxonomy_read_only`: the group or category comes from a content provider and changes only when that provider is refreshed. `inherited_read_only`: the item is owned by a realm above the key's own and can only be changed there; `ownerRealmId` names it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Delete a category group",
        "description": "Deletes an empty group from the organization's vocabulary, along with its metadata definition here. Nothing is deleted on your behalf: delete the group's categories first, remove the group from any category field that restricts its values to it, and remove it from every other environment that uses it. The group is not silently removed from this environment when the delete is refused.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "deleteCategoryGroup",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.taxonomy.manage`. `permission_denied`: the key's role lacks `platform.taxonomy.manage`, which changing the organization's category groups and categories needs.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No group has this id in the key's environment."
          },
          "409": {
            "description": "`group_not_empty`: the group still has categories. `group_in_use`: another environment or app still uses the group — a contract's category field names it, or an environment has added it; `usages` lists each usage as `{ sourceKind, sourceId, app, environmentId, environmentName, displayName }`, gathered from every app that classifies with the vocabulary — `app` is `content` or `assets`, and `environmentId`/`environmentName` are set for a usage inside an environment. `taxonomy_read_only`: the group or category comes from a content provider and changes only when that provider is refreshed. `inherited_read_only`: the item is owned by a realm above the key's own and can only be changed there; `ownerRealmId` names it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/taxonomy/groups/{id}/categories": {
      "get": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "List a group's categories",
        "description": "Every category in the group, as a flat list ordered by key. Rebuild the tree from `parentCategoryId`, which is `null` for a top-level category.\n\n`value` is the display label, as `{ \"default\": …, \"locales\": { … } }`. `isSelectable` says whether the category can be used to classify content or chosen in a category field; a non-selectable one is a heading for its children. Send `rowVersion` back when updating the category to detect a concurrent change.\n\nWhen the group has organization custom fields, a category with values for them also carries `platformMetadata`, an object keyed by field key. It is separate from `metadata` (this environment's own values), the same in every environment, and left out when the category has none. This API reads it but does not change it.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listCategoryGroupCategories",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "An array of `{ id, categoryGroupId, parentCategoryId, key, value, isSelectable, metadata, sourceRegistrationId, rowVersion }`, plus `platformMetadata` on a category that has organization custom-field values."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No group has this id in the key's environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/taxonomy/groups/{id}/binding": {
      "post": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Add a category group to the environment",
        "description": "Makes a group from the organization's vocabulary usable in the key's environment: its categories then appear in pickers, validate on save, and can be used in `category=` filters here. A group is never added automatically, so this is how an existing group reaches an environment. Find candidates with `scope=available` on the list.\n\n`metadataFields` is optional and takes the same field definitions a contract does; when present, every category in the group must supply a `metadata` document in this environment that satisfies them. Adding a group that is already here changes nothing and returns it.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "addCategoryGroupToEnvironment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "$ref": "#/components/schemas/BindCategoryGroupRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The group, shaped as in the list, with `isBound` true."
          },
          "400": {
            "description": "`validation_failed`: `errors` names each invalid metadata field (reported as `metadataFields[n]…`)."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.taxonomy.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No group in the organization's vocabulary has this id, or the group comes from a content provider."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Remove a category group from the environment",
        "description": "Stops the key's environment using the group. The group and its categories stay in the organization's vocabulary and in every other environment that uses them; only this environment's use, and its metadata definition for the group, are removed. Nothing is changed on your behalf: it is refused while content here is classified with one of the group's categories, or a category field here names the group.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "removeCategoryGroupFromEnvironment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.taxonomy.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "The key's environment does not use this group."
          },
          "409": {
            "description": "`group_in_use`: something in this environment still uses the group; `usages` lists each usage as `{ sourceKind, sourceId, app, environmentId, environmentName, displayName }`, gathered from every app that classifies with the vocabulary — `app` is `content` or `assets`, and `environmentId`/`environmentName` are set for a usage inside an environment. `inherited_read_only`: the item is owned by a realm above the key's own and can only be changed there; `ownerRealmId` names it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/taxonomy/categories": {
      "post": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Create a category",
        "description": "Creates a category in `categoryGroupId`, under `parentCategoryId` or at the top of the group when that is `null`. `key` is lowercase kebab-case, 3 to 64 characters, and unique among its siblings; the category's path is the group's external id followed by the keys from the top down, such as `topic/news/local`.\n\n`value` is the display label, either a plain string or `{ \"default\": …, \"locales\": { … } }`. Set `isSelectable` to `false` for a category that only groups others. When the group defines metadata fields in this environment, `metadata` is required and must satisfy them; otherwise omit it.\n\nThe category is created in the organization's vocabulary, so it exists in every environment that uses the group.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "createCategory",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCategoryRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "`201 Created` with the new category, shaped as in the group's category list."
          },
          "400": {
            "description": "`validation_failed`: `errors` names each problem, such as an unknown `categoryGroupId` (or one this environment does not use), a `parentCategoryId` not in that group, an invalid or duplicate `key`, or metadata that does not satisfy the group's fields (reported as `metadata.<field>`)."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`quota_exceeded`: the organization has reached its plan's category limit (`platform.max_categories`, counted across every app); `quotaKey` and `limit` say which.\n\n`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `platform.taxonomy.manage`, which changing the organization's category groups and categories needs.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "409": {
            "description": "`taxonomy_read_only`: the group or category comes from a content provider and changes only when that provider is refreshed. `inherited_read_only`: the item is owned by a realm above the key's own and can only be changed there; `ownerRealmId` names it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/taxonomy/categories/{id}": {
      "put": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Update or move a category",
        "description": "Changes a category's `key`, `value`, `isSelectable` or `metadata`, and moves it within its group. Omitted `key`, `value`, `isSelectable` and `metadata` stay as they are, but **`parentCategoryId` is always applied**: omitting it or sending `null` moves the category to the top of its group. Send the current parent to keep it in place. A category cannot move to another group.\n\nSo renaming a category in place is `{ \"key\": \"new-key\", \"parentCategoryId\": \"<its current parent>\" }` — send only `key` and you will also have moved it. Read the category first if you do not already hold its parent. This is the one field that behaves this way; it exists so that a move needs no separate route.\n\nRenaming or moving a category changes its path everywhere at once — every environment, every app, and delivery — with no republish. Content refers to categories by id, so nothing that uses it breaks. Making a category non-selectable is refused while anything in any app still uses it.\n\n`key`, `value`, `isSelectable` and `parentCategoryId` are the organization's vocabulary and need `platform.taxonomy.manage`; `metadata` is this environment's and needs `content.taxonomy.manage`. A request that sends only `metadata` (with the current `parentCategoryId`) is a metadata-only edit and needs only the latter.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "updateCategory",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCategoryRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated category, shaped as in the group's category list."
          },
          "400": {
            "description": "`validation_failed`: `errors` names each problem, such as an invalid `key`, a sibling with the same key at the destination, a parent not in this group, or metadata that does not satisfy the group's fields. `cycle`: the new parent is the category itself or one of its descendants."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `platform.taxonomy.manage`, which changing the organization's category groups and categories needs. `permission_denied`: the key's role lacks `content.taxonomy.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No category has this id in the key's environment."
          },
          "409": {
            "description": "`category_in_use`: `isSelectable: false` was sent for a category that child categories, classification or field values still use; `usages` lists each usage as `{ sourceKind, sourceId, app, environmentId, environmentName, displayName }`, gathered from every app that classifies with the vocabulary — `app` is `content` or `assets`, and `environmentId`/`environmentName` are set for a usage inside an environment. `concurrency_conflict`: the `rowVersion` sent is not the category's current one, so someone else has changed it; `currentRowVersion` is what it is now. `taxonomy_read_only`: the group or category comes from a content provider and changes only when that provider is refreshed. `inherited_read_only`: the item is owned by a realm above the key's own and can only be changed there; `ownerRealmId` names it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Delete a category",
        "description": "Deletes a category nothing uses, from the organization's vocabulary. Nothing is removed on your behalf: delete or move its child categories, remove it from every component, page, contract and asset classified with it in every environment and app, and clear it from every category field value first.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "deleteCategory",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `platform.taxonomy.manage`, which changing the organization's category groups and categories needs.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No category has this id in the key's environment."
          },
          "409": {
            "description": "`category_in_use`: something still uses the category; `usages` lists each usage as `{ sourceKind, sourceId, app, environmentId, environmentName, displayName }`, gathered from every app that classifies with the vocabulary — `app` is `content` or `assets`, and `environmentId`/`environmentName` are set for a usage inside an environment. `sourceKind` is `category`, `component`, `node`, `contract` or `asset`. `taxonomy_read_only`: the group or category comes from a content provider and changes only when that provider is refreshed. `inherited_read_only`: the item is owned by a realm above the key's own and can only be changed there; `ownerRealmId` names it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/taxonomy/resolve": {
      "get": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Resolve a category path or id",
        "description": "Looks a category up by `path` (such as `topic/news/local`) or by `id`, and returns it with its current path. Use it to turn a path you hold into the id every other route takes, or to show an id as a path. When both are sent, `id` wins. A path naming only a group never resolves.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "resolveCategory",
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"category\": { … }, \"path\": \"topic/news/local\" }`, with `category` shaped as in the group's category list."
          },
          "400": {
            "description": "`validation_failed`: neither `path` nor `id` was sent."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No category matches the path or id."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/components/{id}/categories": {
      "get": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Get a component's categories",
        "description": "The categories a component is directly classified with. Ancestors are not listed; they are added only when the component is published. Categories chosen in the component's own category fields are part of its content, not of this list.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getComponentCategories",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"categoryIds\": [\"…\"] }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No component has this id, or the key's role cannot view the folder it is in."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Classify a component",
        "description": "Replaces the component's classification with `categoryIds`. The list is the whole set, so an empty list removes every category. Every id must name a selectable category in the key's environment.\n\nClassification is frozen into the published snapshot when the entity is next published, together with every ancestor of each category, so a delivery `category=` filter on a parent also matches its sub-categories. A change made here reaches delivery only after that publish. What a category *is* (its path and display value) is read live, so renaming or moving a category needs no republish.\n\nClassifying needs edit access to the component's folder, and the key's role is held to the same folder access rules as a person with that role.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "setComponentCategories",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplaceEntityCategoriesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"categoryIds\": [\"…\"] }`, the stored set with duplicates removed."
          },
          "400": {
            "description": "`validation_failed`: one or more ids do not exist or are not selectable (`errors.categoryIds`)."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role can view the component's folder but not edit it.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No component has this id, or the key's role cannot view the folder it is in."
          },
          "409": {
            "description": "`inherited_read_only`: the item is owned by a realm above the key's own and can only be changed there; `ownerRealmId` names it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/experience/nodes/{id}/categories": {
      "get": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Get a page's categories",
        "description": "The categories an Experience node (a page, or any other node in a site) is directly classified with. Ancestors are not listed; they are added only when the node is published.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getNodeCategories",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"categoryIds\": [\"…\"] }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id, or the key's role cannot view its branch."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Classify a page",
        "description": "Replaces an Experience node's classification with `categoryIds`. The list is the whole set, so an empty list removes every category. Every id must name a selectable category in the key's environment.\n\nClassification is frozen into the published snapshot when the entity is next published, together with every ancestor of each category, so a delivery `category=` filter on a parent also matches its sub-categories. A change made here reaches delivery only after that publish. What a category *is* (its path and display value) is read live, so renaming or moving a category needs no republish.\n\nClassifying needs edit access to the node's branch of the site, and the key's role is held to the same access rules as a person with that role.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "setNodeCategories",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplaceEntityCategoriesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"categoryIds\": [\"…\"] }`, the stored set with duplicates removed."
          },
          "400": {
            "description": "`validation_failed`: one or more ids do not exist or are not selectable (`errors.categoryIds`)."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`access_denied`: the key's role can view the node's branch but not edit it.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No node has this id, or the key's role cannot view its branch."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/contracts/{id}/categories": {
      "get": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Get a contract's categories",
        "description": "The categories a contract is classified with. Classifying contracts organizes the contract library for authors; it is not delivered.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getContractCategories",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"categoryIds\": [\"…\"] }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No contract has this id in the key's environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Taxonomy"
        ],
        "summary": "Classify a contract",
        "description": "Replaces the contract's classification with `categoryIds`. The list is the whole set, so an empty list removes every category. Every id must name a selectable category in the key's environment. The change takes effect immediately; contracts are not published.\n\nThe key's role needs `content.contracts.manage`, the same permission that edits the contract itself.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "setContractCategories",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplaceEntityCategoriesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"categoryIds\": [\"…\"] }`, the stored set with duplicates removed."
          },
          "400": {
            "description": "`validation_failed`: one or more ids do not exist or are not selectable (`errors.categoryIds`)."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.contracts.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No contract has this id in the key's environment."
          },
          "409": {
            "description": "`contract_read_only`: the contract comes from a content provider and cannot be changed here. `inherited_read_only`: the item is owned by a realm above the key's own and can only be changed there; `ownerRealmId` names it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/references/dangling": {
      "get": {
        "tags": [
          "Management: Checks and previews"
        ],
        "summary": "List dangling references",
        "description": "Lists every reference in the key's environment whose target does not exist there: a component field or page that points at a component or page this environment lacks, typically because a promotion skipped it. Changes nothing.\n\nEach item names the source (`sourceKind`, `sourceId`, `sourceName`, the `fieldPath` holding the reference, and its folder, contract or site path) and the missing `targetKey`. `presentIn` lists the environments that promote directly into this one and do hold the target. Promoting the target alone does not repair the reference: promote the source again once its target exists here.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listDanglingReferences",
        "responses": {
          "200": {
            "description": "`{ \"items\": [ … ] }`; an empty list means nothing dangles."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/adapters": {
      "get": {
        "tags": [
          "Management: Adapters"
        ],
        "summary": "List adapters",
        "description": "Lists the adapters in the key's environment, ordered by external id. An adapter is a named, reusable field mapping from an input contract to an output contract, with one rule per output field.\n\nEach item carries `usageCount`: how many things use the adapter, counting presentation bindings, streams that project through it, and other adapters whose rules nest it. `realmId` names the realm that owns it; an adapter owned by a realm above the key's own is read-only here.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listAdapters",
        "responses": {
          "200": {
            "description": "An array of `{ id, externalId, name, inputContractId, outputContractId, realmId, usageCount }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "post": {
        "tags": [
          "Management: Adapters"
        ],
        "summary": "Create an adapter",
        "description": "Creates a named adapter from `externalId`, `name`, `inputContractId`, `outputContractId` and `rules`. `externalId` is lowercase kebab-case, 3 to 64 characters, unique in the environment, and cannot be changed later.\n\nEach rule maps one output field: `direct` copies an input field, `computed` builds a value from a small expression tree, `nested` maps a structured value through another adapter (named by id, or inline), and `lookup` reads from another component. The rules are validated against both contracts before anything is saved; send them to `/adapters/validate` first to see the same findings without saving.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "createAdapter",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAdapterRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The created adapter, shaped as `getAdapter` returns it."
          },
          "400": {
            "description": "`validation_failed`: `externalId` is not valid kebab-case of 3 to 64 characters, or `name` is empty."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.adapters.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "409": {
            "description": "`duplicate_external_id`: an adapter with this external id already exists in the environment."
          },
          "422": {
            "description": "`adapter_invalid`: the adapter cannot be saved. `errors` lists each problem as `{ path, code, message }`: `invalid_rules` (the rules are not well-formed), `contract_not_found` (the input or output contract does not exist), `nested_adapter_not_found` (a nested rule names an adapter that does not exist in this environment), and the type-compatibility and coverage problems `/adapters/validate` reports."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/adapters/{id}": {
      "get": {
        "tags": [
          "Management: Adapters"
        ],
        "summary": "Get an adapter",
        "description": "Fetches one adapter with its full `rules`, its `usageCount`, and its `rowVersion`. Send `rowVersion` back on `PATCH` to be told if someone else changed the adapter in the meantime.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getAdapter",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ id, externalId, name, inputContractId, outputContractId, realmId, usageCount, rules, rowVersion, layout }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No adapter has this id in the key's environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "patch": {
        "tags": [
          "Management: Adapters"
        ],
        "summary": "Update an adapter",
        "description": "Changes any of `name`, `inputContractId`, `outputContractId` and `rules`; omitted members keep their current value. `externalId` cannot be changed. The resulting adapter is revalidated as a whole on every update, so changing a contract can fail against rules you did not send. Include `rowVersion` from `getAdapter` to detect a concurrent edit.\n\nA change takes effect at once for every stream that projects through the adapter and every mapping that nests it by name. A page that binds the adapter directly keeps the rules it was published with until it is republished.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "updateAdapter",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAdapterRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated adapter, shaped as `getAdapter` returns it."
          },
          "400": {
            "description": "`validation_failed`: `name` was sent empty."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.adapters.manage`. `inherited_owner_not_permitted`: the item is owned by a realm above the key's own (`ownerRealmId`). A person can edit it from the app and the change is written in the realm that owns it, but a key acts in one realm and never writes through to another: change it with a key bound to the owning realm.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No adapter has this id in the key's environment."
          },
          "409": {
            "description": "`concurrency_conflict`: the adapter changed since `rowVersion` was read; `currentRowVersion` is the current value. Re-read and retry."
          },
          "422": {
            "description": "`adapter_invalid`: the adapter cannot be saved. `errors` lists each problem as `{ path, code, message }`: `invalid_rules` (the rules are not well-formed), `contract_not_found` (the input or output contract does not exist), `nested_adapter_not_found` (a nested rule names an adapter that does not exist in this environment), and the type-compatibility and coverage problems `/adapters/validate` reports."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Management: Adapters"
        ],
        "summary": "Delete an adapter",
        "description": "Deletes an adapter nothing uses. An adapter that a presentation binding, a stream or another adapter's `nested` rule still names cannot be deleted; `getAdapterUsages` lists what uses it.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "deleteAdapter",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.adapters.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No adapter has this id in the key's environment."
          },
          "409": {
            "description": "`adapter_in_use`: something still uses the adapter; `usages` lists each as `{ entityKind, entityId, name, fieldPath }`, where `entityKind` is `stream`, `adapter`, `component` or `node`. Remove those uses first. `inherited_read_only`: the item is owned by a realm above the key's own and can only be changed there; `ownerRealmId` names it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/adapters/{id}/usages/closure": {
      "get": {
        "tags": [
          "Management: Adapters"
        ],
        "summary": "List where an adapter is used",
        "description": "Lists everything that uses the adapter: streams that project through it, adapters whose rules nest it, and the components and pages whose presentation bindings name it. This is the same set that blocks deleting it. Changes nothing.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getAdapterUsages",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"items\": [{ \"kind\", \"id\", \"name\", \"detail\", \"path\" }] }`, where `kind` is `stream`, `adapter`, `component` or `node`, `detail` says how it uses the adapter, and `path` is a component's folder name or a page's path."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No adapter has this id in the key's environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/adapters/validate": {
      "post": {
        "tags": [
          "Management: Adapters"
        ],
        "summary": "Validate an adapter",
        "description": "Checks `rules` against `inputContractId` and `outputContractId` without saving anything. Set `inputContractVersion` to check against a specific version of the input contract rather than its latest.\n\nProblems in the rules come back in a `200` response rather than as an error, so you can show them while editing: `errors` would block a save, `warnings` would not. `coverage` reports whether each output field is mapped, `compatibility` which input fields can feed which output fields, and `suggestions` mappings worth considering.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "validateAdapter",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdapterValidateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ errors, warnings, coverage, compatibility, suggestions }`, each finding in `errors` and `warnings` shaped `{ path, code, message }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "422": {
            "description": "`adapter_invalid`: the request could not be checked at all. `invalid_rules`: the rules are not well-formed. `contract_not_found`: the input or output contract (or that input contract version) does not exist."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/adapters/preview": {
      "post": {
        "tags": [
          "Management: Adapters"
        ],
        "summary": "Preview an adapter",
        "description": "Runs a mapping over one component and returns what it produces, without saving anything. Name the mapping either as a saved adapter (`adapterId`) or inline (`outputContractId` plus `rules`), and the component as `provider` and `key` (the component's id).\n\nThe input is the component's current saved document, not its published version. `contextualValues` overrides fields of that document for this preview, and `locale` and `context` resolve localized and personalized values as delivery would. References inside the input are not expanded. Problems that would not stop delivery, such as a `lookup` that finds nothing, come back as `warnings`.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "previewAdapter",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdapterPreviewRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"content\": { … }, \"warnings\": [ … ] }`: the output document and any warnings."
          },
          "400": {
            "description": "`validation_failed`: neither `adapterId` nor both `outputContractId` and `rules` were sent."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No adapter has `adapterId`, or no component has `key`, in the key's environment."
          },
          "422": {
            "description": "`adapter_invalid` with `invalid_rules`: the inline rules are not well-formed."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/streams": {
      "get": {
        "tags": [
          "Management: Streams"
        ],
        "summary": "List streams",
        "description": "Lists the stream definitions in the key's environment, ordered by external id. A stream is a named listing of published content that delivery serves at `/content/delivery/v1/streams/{externalId}`.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listStreams",
        "responses": {
          "200": {
            "description": "An array of `{ id, externalId, name, sourceContractIds, adapterId, realmId }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "post": {
        "tags": [
          "Management: Streams"
        ],
        "summary": "Create a stream",
        "description": "Creates a stream definition:\n\n- `sourceContractIds`: one to ten contracts. A contract also matches every contract that inherits from it.\n- `adapterId`: an optional adapter every item is mapped through. Every source contract must satisfy the adapter's input contract.\n- `orderByFieldPath` and `orderDescending`: the order, by one top-level field.\n- `resolveDepth`: 0 to 10, how many levels of references each item's content expands.\n- `excludedComponentIds`: up to 100 components to leave out.\n- `declaredFilters`: up to five filters callers may use, each `{ key, type, fieldPath, labelFieldPath, groupExternalId, mode }`. `type` is `string`, `category`, `reference`, `date` or `fullText` (at most one). A `fullText` filter's `mode` is `words` (the default: whole words anywhere in the item) or `prefix`: the start of a word in one field, so \"bel\" finds \"Bella\". A `prefix` filter needs a `fieldPath` naming that top-level field; a `words` filter takes none.\n\nDelivery reads stream definitions live, so a saved stream is served at once, with no publish step. Only the content it lists has to be published.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "createStream",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateStreamRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The created stream, shaped as `getStream` returns it."
          },
          "400": {
            "description": "`validation_failed`: `externalId` is not valid kebab-case of 3 to 64 characters, or `name` is empty."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.streams.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "409": {
            "description": "`duplicate_external_id`: a stream with this external id already exists in the environment."
          },
          "422": {
            "description": "`stream_invalid`: the definition cannot be saved. `errors` lists each problem as `{ target, code, message }`, for example `source_contracts_required`, `too_many_source_contracts`, `contract_not_found`, `adapter_not_found`, `stream_adapter_input_unsatisfied` (a source contract does not satisfy the adapter's input contract), `invalid_field_path`, `invalid_resolve_depth`, `too_many_exclusions`, `too_many_filters`, `duplicate_filter_key`, `unknown_filter_type`, `multiple_fulltext_filters` and `unknown_category_group`."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/streams/{id}": {
      "get": {
        "tags": [
          "Management: Streams"
        ],
        "summary": "Get a stream",
        "description": "Fetches one stream definition, including its `rowVersion`. Send `rowVersion` back on `PUT` to be told if someone else changed the stream in the meantime.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getStream",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ id, externalId, name, sourceContractIds, adapterId, orderByFieldPath, orderDescending, resolveDepth, excludedComponentIds, declaredFilters, realmId, rowVersion }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No stream has this id in the key's environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "put": {
        "tags": [
          "Management: Streams"
        ],
        "summary": "Replace a stream",
        "description": "Replaces a stream's whole definition: send every member `createStream` takes except `externalId`, which cannot be changed. Omitting `excludedComponentIds` or `declaredFilters` clears them. Include `rowVersion` from `getStream` to detect a concurrent edit.\n\nDelivery reads the new definition on its next request, so the change is live at once. Removing or renaming a declared filter breaks callers still sending it.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "updateStream",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateStreamRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The updated stream, shaped as `getStream` returns it."
          },
          "400": {
            "description": "`validation_failed`: `name` is empty."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.streams.manage`. `inherited_owner_not_permitted`: the item is owned by a realm above the key's own (`ownerRealmId`). A person can edit it from the app and the change is written in the realm that owns it, but a key acts in one realm and never writes through to another: change it with a key bound to the owning realm.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No stream has this id in the key's environment."
          },
          "409": {
            "description": "`concurrency_conflict`: the stream changed since `rowVersion` was read; `currentRowVersion` is the current value. Re-read and retry."
          },
          "422": {
            "description": "`stream_invalid`: the definition cannot be saved. `errors` lists each problem as `{ target, code, message }`, for example `source_contracts_required`, `too_many_source_contracts`, `contract_not_found`, `adapter_not_found`, `stream_adapter_input_unsatisfied` (a source contract does not satisfy the adapter's input contract), `invalid_field_path`, `invalid_resolve_depth`, `too_many_exclusions`, `too_many_filters`, `duplicate_filter_key`, `unknown_filter_type`, `multiple_fulltext_filters` and `unknown_category_group`."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      },
      "delete": {
        "tags": [
          "Management: Streams"
        ],
        "summary": "Delete a stream",
        "description": "Deletes a stream definition. Nothing blocks this: callers name a stream by external id, so after deletion delivery answers them `404 unknown_stream`, straight away. The stream's adapter and source contracts are not deleted.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "deleteStream",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`permission_denied`: the key's role lacks `content.streams.manage`.\n\n`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No stream has this id in the key's environment."
          },
          "409": {
            "description": "`inherited_read_only`: the item is owned by a realm above the key's own and can only be changed there; `ownerRealmId` names it."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/streams/{id}/preview": {
      "post": {
        "tags": [
          "Management: Streams"
        ],
        "summary": "Preview a stream",
        "description": "Runs the stream's saved definition against published content and returns its first page, as delivery would. It changes nothing, and it previews the definition as saved, so save an edit before previewing it.\n\nThe body takes `deliveryEnvironmentId` (optional when the key's environment publishes to exactly one), `locale`, `limit` (1 to 100, default 20) and `filters`, an object of declared filter keys to arrays of values.\n\n**Key:** a management key with `content.management.authoring`.",
        "operationId": "previewStream",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StreamPreviewRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "One page shaped as delivery's `queryStream` returns it."
          },
          "400": {
            "description": "`delivery_target_not_mapped`: `deliveryEnvironmentId` is not one the key's environment publishes to. `ambiguous_delivery_target`: several are mapped; name one. `no_delivery_target_mapped`: the key's environment publishes nowhere. `validation_failed`: a filter the stream does not declare, a value over the length limit, or more than one full-text value. `invalid_filter_value`: a `date` value is not `YYYY-MM`. `unknown_category`: no category has this path or id. `category_not_in_group`: the category belongs to a different group from the one the filter is scoped to."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "No stream has this id in the key's environment."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/choice-sources": {
      "get": {
        "tags": [
          "Management: Checks and previews"
        ],
        "summary": "List choice sources",
        "description": "Lists the choice sources activated for the organization. A contract text field can take its allowed values from a choice source, named by `key`, instead of a fixed list. Changes nothing.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listChoiceSources",
        "responses": {
          "200": {
            "description": "`{ \"sources\": [{ \"key\", \"displayName\" }] }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/choice-sources/{pluginKey}/choices": {
      "get": {
        "tags": [
          "Management: Checks and previews"
        ],
        "summary": "Get a choice source's options",
        "description": "Asks one choice source for its current options, live. Changes nothing.\n\nThe list can change at any time. Saving a component does not check a value against it, so a value chosen earlier can later fall off the list.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "getChoiceSourceChoices",
        "parameters": [
          {
            "name": "pluginKey",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ \"choices\": [{ \"value\", \"label\" }] }`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "`unknown_choice_source`: `pluginKey` is not a choice source activated for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          },
          "502": {
            "description": "`choice_source_failed`: the source is unavailable or failed; `message` carries its own explanation. Check the source's configuration."
          }
        }
      }
    },
    "/content/management/v1/library/unused": {
      "get": {
        "tags": [
          "Management: Checks and previews"
        ],
        "summary": "List unused items",
        "description": "Lists the contracts, templates, components and adapters in the key's environment that nothing uses, which are the ones whose delete would succeed right now. Changes nothing.\n\nA component that is published and live counts as used, because delivery serves it. Items owned by a realm above the key's own and contracts supplied by a content provider are never listed, and neither are streams, since callers outside the system name them.\n\n**Key:** a management key with `content.management.read`.",
        "operationId": "listUnusedItems",
        "responses": {
          "200": {
            "description": "`{ \"items\": [ … ] }`, each with its `kind`, `id` and `name`, the folder for a component, and when it was last updated."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/management/v1/proposals": {
      "post": {
        "tags": [
          "Management: Proposals"
        ],
        "summary": "Offer content for review",
        "description": "Offers content for a person to review. A proposal is not content: it is stored on its own, spends no component allowance, and becomes a component only when somebody approves it.\n\nSend `contract` (a contract's external id), an optional `folder` (a slash-separated folder path, matched without regard to capitalisation), a `source`, and `values`.\n\n`values` is a flat object of whatever names your own system uses. You do not need to know the contract's field names: each key is matched to a field by name, ignoring case and punctuation, so `hipScore` finds `hip-score`. A field that holds a value typed in place can be filled from keys named after the field and the part, so `author-name` finds the `name` inside an `author` field. Keys that match nothing are kept and shown to the reviewer rather than dropped, so nothing a sender wrote is lost. Because that matching happens here, this endpoint needs no permission to read your content model.\n\n`source` is `{ kind, ref, label }` and is stored exactly as sent. It is never parsed, resolved, or called back to — it exists so a reviewer can see where something came from. `ref` is your own identifier for the submission, and `(kind, ref)` is unique: **offering the same pair again returns the existing proposal** rather than filing a second one, so retrying after a lost response is safe.\n\nThe destination folder must have a proposal workflow assigned to it, or to one of its ancestors. That is deliberate: without one there would be nobody to review what you send, and content offered from outside the organization must never reach the site without a person.\n\n**Key:** content.management.proposals",
        "operationId": "createProposal",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateProposalRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "`{ id, status, alreadyExisted, match }`. `status` is `pending` until somebody decides. `alreadyExisted` is true when this repeated an earlier offer. `match` is `{ matched, total, unmatchedKeys }` — how many of your keys found a field, how many fields the contract has, and which keys matched nothing. Use it to check a new integration before real submissions start arriving."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `tier_required`: the Content Management API needs the Pro plan or above. `ip_denied`: the caller's address is outside the key's allowed ranges. `app_not_available`: Content is not enabled for the organization."
          },
          "422": {
            "description": "`unknown_contract`: no contract with that external id exists in this environment. `abstract_contract`: that contract is abstract and cannot be instantiated. `unknown_folder`: no folder at that path exists in this environment. `proposal_workflow_not_assigned`: no proposal workflow governs that folder, so there would be nobody to review this."
          },
          "429": {
            "description": "Rate limit or quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/deployment/v1/whoami": {
      "get": {
        "tags": [
          "Deployment: Promotion"
        ],
        "summary": "Describe the calling key",
        "description": "Reports what the deployment key you are calling with is: the organization it belongs to, the promotion edge it is bound to (a source environment and a target), the role it acts as, its own id and name, and its scopes.\n\nA deployment key promotes content along exactly one edge, and nothing else. Call this before acting on a key you did not mint yourself. It works with any valid deployment key whatever its scopes, so a key that can only plan can find out that planning is all it can do.\n\n`edge.active` is false when the edge has been removed from the organization's topology. The key still authenticates, and plan and execute answer `not_a_promotion_target` until the edge is added back.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "getDeploymentKey",
        "responses": {
          "200": {
            "description": "`{ \"organizationId\", \"organizationName\", \"organizationSlug\", \"keyId\", \"keyName\", \"edge\": { \"source\": { \"id\", \"name\" }, \"target\": { \"id\", \"name\" }, \"active\" }, \"roleId\", \"roleName\", \"allowOverwriteDiverged\", \"scopes\": [ … ] }`. `scopes` is sorted."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/deployment/v1/promotions/plan": {
      "post": {
        "tags": [
          "Deployment: Promotion"
        ],
        "summary": "Plan a promotion",
        "description": "Shows what promoting one item from the key's source environment to its target would do, and changes nothing. Send the item as `kind` and `id`: a `component`, `node`, `contract`, `template`, `adapter` or `stream`.\n\nThe response lists every item the promotion would carry, each with a `state`: `new` (the target has no copy), `update` (the target copy is as the last promotion left it) or `diverged` (the target copy has changed since, or was never promoted). Contracts, templates and similar dependencies come along automatically and are marked `implicit`.\n\nThe source and target are the ones the key was created for. The request cannot name either: a `targetEnvironmentId` in the body is ignored.\n\nA key acts in one realm. Each item carries the `realmId` that owns it. When the promotion needs items that a realm above the key's own owns and the target lacks or has behind, they are listed under `blockedByAncestorRealms` (a `realmId` and its `items`, each with a `kind`, `id`, `name` and a `reason` of `missing`, `behind` or `versionMismatch`). A deployment key cannot promote into an ancestor realm: promote those items first with a key bound to that realm.\n\n**Key:** content.deployment.plan",
        "operationId": "planPromotion",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeploymentPlanRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"targetEnvironmentId\", \"items\": [ { \"kind\", \"id\", \"name\", \"state\", \"implicit\", \"include\", \"pinnedVersionNumber\", \"targetHeadVersionNumber\", \"realmId\" } ], \"blockers\": [ … ], \"blockedByAncestorRealms\": [ … ] }`. A non-empty `blockers` means the promotion cannot run until they are resolved."
          },
          "400": {
            "description": "`not_a_promotion_target`: the key's edge is no longer in the organization's topology."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key does not hold `content.deployment.plan`. `tier_required`: the organization's plan is below Pro.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/deployment/v1/promotions/execute": {
      "post": {
        "tags": [
          "Deployment: Promotion"
        ],
        "summary": "Execute a promotion",
        "description": "Promotes one item, and everything it depends on, from the key's source environment to its target.\n\nSend `root` (the same `{ kind, id }` you planned) and `items`, the list of explicit items to write. Items the plan marked `implicit` always come along. The server plans again itself and never trusts an earlier plan, so a change between planning and executing is caught.\n\n**Overwriting.** An item the plan marked `diverged` has been changed in the target since the last promotion. It is promoted only when two things are both true: the item carries `confirmOverwrite: true` in `items`, and the key was created with overwriting allowed. Otherwise the whole request is refused and nothing is written. A pipeline that should never replace edits made in the target needs no extra care: that is how keys are created by default.\n\nThe key is checked as the role it acts as, against the **source** environment, as when a person promotes. Each execute counts against the organization's daily write allowance.\n\n**Key:** content.deployment.execute",
        "operationId": "executePromotion",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeploymentExecuteRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"items\": [ { \"kind\", \"id\", \"promoted\", \"error\" } ] }`, one entry for each item written."
          },
          "400": {
            "description": "`not_a_promotion_target`: the key's edge is no longer in the organization's topology. `promotion_root_required`: the request has no `root`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key does not hold `content.deployment.execute`. `permission_denied`: the role may not promote this item.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "409": {
            "description": "`promotion_conflict`: one or more diverged items were not both confirmed and allowed. The body lists them as `items`. Nothing was written. `promotion_blocked_by_ancestor_realms`: the promotion needs items owned by a realm above the key's own that the target lacks or has behind; they are listed as `blockedByAncestorRealms`. Nothing was written."
          },
          "422": {
            "description": "`promotion_blocked`: the plan has blockers. They are listed as `blockers`."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/sites": {
      "get": {
        "tags": [
          "Delivery: Sites and locales"
        ],
        "summary": "List published sites",
        "description": "Lists the published sites in the key's delivery environment, with the hostnames mapped to each. A key limited to a realm lists only the sites that realm and the realms below it own. A site with locale addressing also carries an `addressing` member, and a site that arranges its own languages carries them as `locales` (the same `code` and `parentCode` entries as `/locales`, default first). A site that follows the organization has no such member.\n\nSend the returned `deliveryEnvironmentId` back as `env=` on later `/sites` and `/locales` requests. With it, and with a key that has no IP allow-list, the response can be cached at the edge: it carries `Cache-Control: public` and an `ETag`, and a matching `If-None-Match` answers `304 Not Modified`. Compare `ETag`s weakly, because a proxy that compresses the response turns a strong one into `W/\"…\"`.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "listSites",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`environment_mismatch`: `env` names a different delivery environment than the key's."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/locales": {
      "get": {
        "tags": [
          "Delivery: Sites and locales"
        ],
        "summary": "Get the locale tree",
        "description": "The organization's locales as a flat, parent-linked list, where `parentCode` is `null` for the root.\n\nUse it to choose the one `locale=` code to send on other requests. The API takes exactly one code and does not fall back from `fr-CA` to `fr` on its own. Cacheable at the edge on the same terms as `/sites`.\n\nSend `site` (a root node id from `/sites`) to get the languages that one site serves. A site can arrange its own languages under the organization's default, so two sites can differ; a site that has not been customized answers the organization's list. It is cached at the edge on the same terms as the organization-wide list, and a change to a site's languages purges it.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "listLocales",
        "parameters": [
          {
            "name": "env",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "site",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`environment_mismatch`: `env` names a different delivery environment than the key's."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`key_realm_mismatch`: the key is limited to one realm and the site named by `site` belongs to a realm outside it. A key with no realm serves every site; one with a realm serves the sites that realm and the realms below it own.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/path/{path}": {
      "get": {
        "tags": [
          "Delivery: Pages"
        ],
        "summary": "Resolve a path",
        "description": "Returns whatever is published at a path. `kind` says what you got:\n\n- `presentation`: a page, with its Template, settings and bound component, plus the page's `title` and `titleSource`, and its `classification` when it has one: the ids of the categories the page was published in, each with all of its ancestors, so a site can say which categories a view was in without a second request. The member is absent for a page with no categories, and it holds ids only (a category's name is read separately, because a rename should not need a republish).\n- `redirect`: follow `targetPath` yourself. Chains are collapsed server-side, and a renamed page's old path answers with a redirect to the new one.\n\nName the site with `site` (a root node id from `/sites`) or `host` (the hostname you serve the page under). On a site with locale addressing the locale comes from the address, so `locale=` is refused. A site that arranges its own languages refuses a `locale` it does not serve with `400 locale_not_in_site`. `resolve` sets how many levels of references are expanded into `content`, and `ctx` carries the visitor context used for personalization.\n\nWith an `X-Ebitex-Preview-Session` header, the page being previewed answers with its draft and `X-Ebitex-Preview: draft`; every other path answers what is published.\n\n`annotate=true` adds a `preview` object to a **draft** answer, naming the entity and fields each part of the page came from — what a server-rendered site emits so the preview script can draw pins and click-to-edit. It is ignored, not refused, on anything answered as published, so one request shape serves both.\n\n**Audiences.** Audiences are never read from `ctx`: a request that sends `audiences` there is refused with `400 audiences_not_in_ctx`. Send the signed audience token from `POST /audience/delivery/v1/resolve` in the `X-Ebitex-Audiences` header instead (the SDKs do). A variant on an audience is delivered only to a token that grants it; with no token, or one that is malformed, tampered with, for another organization or expired, you get the default as for a visitor in no audiences. An `expired` or `invalid` token also sets the `X-Ebitex-Audiences-Status` response header so a client can renew.\n\n**Gated pages.** A page, section or site can be restricted to an audience. Such a page is never returned to a visitor whose token does not grant it. Every response that depends on a token, and every response about a gated page (the page itself or the `403` below), is `Cache-Control: private, no-store`, so keep it out of shared caches.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "resolvePath",
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "site",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "host",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "resolve",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "raw",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "ctx",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "annotate",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`locale_not_addressable`: the site takes its locale from the address. `validation_failed`: an unsupported parameter value. `audiences_not_in_ctx`: `ctx` carries `audiences`. `preview_raw_unsupported`: `raw=true` with a preview session."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`key_realm_mismatch`: the key is limited to one realm and this site belongs to a realm outside it. A key with no realm serves every site; one with a realm serves the sites that realm and the realms below it own. `preview_site_mismatch`: the path is in a different site from the page being previewed. `members_only`: the page is restricted and the request carries no valid claim for its audience (or one without the verification the audience requires). The body is `{ \"error\": \"members_only\", \"gate\": { \"audienceId\", \"access\": \"verified\" | \"open\", \"door\": { \"kind\": \"signIn\" | \"form\" | \"none\", \"formId\"? } }, \"unlock\": { … } }`, where `unlock` is the Experience to show instead (resolved like any page, in the request's locale) or `{ \"kind\": \"builtin\", \"heading\", \"message\", \"doorLabel\" }` when none is chosen. It never contains the withheld page.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "`path_not_found`: nothing is published at this path. `site_not_found`: no site matches `site` or `host`."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/nodes": {
      "get": {
        "tags": [
          "Delivery: Pages"
        ],
        "summary": "Query the page tree",
        "description": "The published page tree, for navigation: a breadcrumb, a section menu, a previous/next pager or a whole-site tree are each one request.\n\n- `from` is the anchor, a node id or a published path; the default is the site root.\n- `include` is repeatable: `ancestors`, `self`, `children`, `siblings` and `descendants` (default `self,descendants`).\n- `maxDepth` bounds `descendants`, counted from the site root.\n\nNodes come back flat and parent-linked, in depth-first order with siblings in their authored order. Each has a `kind`: `presentation` (a page), `redirect` (with `targetNodeId`, and `targetPath` when that target is published), or `structural` (an address with nothing to serve). `title` is never empty: it falls back to the node's own name, and `titleSource` says which answered.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "listNodes",
        "parameters": [
          {
            "name": "site",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "host",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "maxDepth",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`validation_failed`: an unknown `include` value or a negative `maxDepth`. `tree_too_large`: over 5,000 nodes; narrow it with `from`, `include` or `maxDepth`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`key_realm_mismatch`: the key is limited to one realm and this site belongs to a realm outside it. A key with no realm serves every site; one with a realm serves the sites that realm and the realms below it own.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "`site_not_found`: no site matches `site` or `host`. `node_not_found`: `from` names nothing published in that site."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/sitemap": {
      "get": {
        "tags": [
          "Delivery: Pages"
        ],
        "summary": "Get sitemap data",
        "description": "Every page of one site that belongs in a sitemap, with the path it has in each of the site's locales and a `lastmod`. It returns JSON, not XML: render the sitemap with `buildSitemapXml` from `@ebitex/content-sdk/sitemap`, which handles absolute URLs, escaping and `hreflang`.\n\nOnly published pages with a real address are listed. Redirects, structural nodes, and pages an editor excluded from search (or whose ancestor is excluded) are omitted. `lastmod` is when the page's content last changed, not when it was last published. `localeSlots` lists the paths a page has; it does not say whether the page has been translated.\n\nWith an explicit `site` and a valid `env`, the response can be cached at the edge like `/sites`.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "getSitemap",
        "parameters": [
          {
            "name": "site",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "env",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`environment_mismatch`: `env` names a different delivery environment than the key's."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`key_realm_mismatch`: the key is limited to one realm and this site belongs to a realm outside it. A key with no realm serves every site; one with a realm serves the sites that realm and the realms below it own.\n\n`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "`site_not_found`: no site matches `site` or `host`."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/components/{provider}/{key}": {
      "get": {
        "tags": [
          "Delivery: Components"
        ],
        "summary": "Get a published component",
        "description": "Fetches one published component. `provider` is `core`, and `key` is the component's id or its external id, so a site can name a standalone component such as its header by the id it was authored under. The response always reports the id as `key`, and the external id as `externalId` — so a response tells you both names this component answers to.\n\n`include` is repeatable: `paths` adds the component's own published addresses, and `referencePaths` adds addresses to every reference inside its content. `resolve`, `locale`, `raw` and `ctx` work as on `/path`. The external id is the one the component was published under, until it is republished.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "getComponent",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "site",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "resolve",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "raw",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "ctx",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`validation_failed`: an unsupported `include` value. `invalid_context`: `ctx` is not a valid context bag."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "`not_published`: no published component has this id or external id."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/components": {
      "get": {
        "tags": [
          "Delivery: Components"
        ],
        "summary": "List published components",
        "description": "Lists published components of a contract, by its id or external id, including every contract that inherits from it. Results are paged: pass `nextCursor` back as `cursor`, exactly as received; `null` means there are no more.\n\nEach item carries `key` (the component's id), `externalId` (the name it was published under — store this when a selection has to outlive the response, since `getComponent` accepts it as the key) and `title`.\n\n- `category` filters by category path or id, and is repeatable (every value must match). Matching includes sub-categories.\n- `include=content` returns each item's resolved content, shaped like `getComponent`. `resolve`, `raw` and `ctx` are only accepted with it.\n- `include=paths` adds each item's published addresses, and `include=referencePaths` adds addresses to the references inside its content.\n- `orderBy` orders by one top-level field and `direction` by `asc` or `desc`. Date fields order chronologically; items without the field come last either way.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "listComponents",
        "parameters": [
          {
            "name": "contract",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "site",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "resolve",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "raw",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "ctx",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`unknown_contract`: no contract has this id or external id. `unknown_category`: no category has this path or id. `invalid_cursor`: the cursor is malformed or belongs to a different order; restart from the first page. `validation_failed`: an unsupported parameter, or a read option sent without `include=content`."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/streams/{externalId}": {
      "get": {
        "tags": [
          "Delivery: Streams"
        ],
        "summary": "Query a stream",
        "description": "Returns one page of a stream: a named listing an editor defines, with its own sources, order and filters. `externalId` is the stream's external id or id.\n\nItems always carry `content` and `paths`. When the stream maps its items through an Adapter, `content` is the Adapter's output and `contract` names the output contract.\n\n`filter.<key>=` takes values for the filters the stream declares. Repeated values of one key match any of them; different keys must all match. A full-text filter takes one value, with quoted phrases, `OR` and `-word`. A contract filter takes contract keys, and matches items of that contract or any contract inheriting from it — the way to read one kind out of a stream whose sources are a shared parent.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "queryStream",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "site",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ctx",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`validation_failed`: a filter the stream does not declare. `unknown_category`: no category has this path or id. `category_not_in_group`: the category belongs to a different group from the one the filter is scoped to. `unknown_contract`: no contract has this key. `contract_not_in_sources`: the contract exists, but this stream does not cover it. `invalid_cursor`: the cursor is malformed or the stream's order has changed; restart from the first page."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "`unknown_stream`: no stream has this external id or id."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/streams/{externalId}/facets": {
      "get": {
        "tags": [
          "Delivery: Streams"
        ],
        "summary": "Get a stream facet",
        "description": "The distinct values of one declared filter (`field`), and how many items match each. Counts respect every other active filter but not the facet's own, so a facet never narrows itself. Send the same `filter.<key>=` values you send the stream.\n\n`value` is what to send back as the filter. Category values also carry `path` and `label`, and reference and contract values carry `label`. A contract facet's `value` is a contract key and its `label` that contract's name; it lists only the contracts items actually are, so a parent contract you may filter by has no entry of its own. Full-text filters have no facet.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "getStreamFacet",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "field",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`unknown_filter_field`: no `field`, an undeclared one, or the full-text filter. `validation_failed`, `unknown_category`, `category_not_in_group`, `unknown_contract` and `contract_not_in_sources` as for the stream."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "`unknown_stream`: no stream has this external id or id."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/blobs/{id}": {
      "get": {
        "tags": [
          "Delivery: Media"
        ],
        "summary": "Download a published blob",
        "description": "Streams the bytes of a blob that published content references, with its stored content type.\n\nThis is not what a media field's `url` points at. That URL is public and needs no key; use it in pages. This route is for a server that already holds a key and needs the original bytes, such as a build step that transforms images.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "downloadBlob",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/schemas": {
      "get": {
        "tags": [
          "Delivery: Components"
        ],
        "summary": "Get the delivered schema bundle",
        "description": "One JSON Schema (draft 2020-12) document describing what delivery returns for every contract published in the key's environment, keyed `{contractId}@{version}` under `$defs`. It describes the published shape, not the one being edited, and `$ref`s only resolve inside the same document.\n\n`content-sdk-codegen` from `@ebitex/content-sdk` turns it into TypeScript types for your own content model. The bundle describes server-resolved responses, as a request with `ctx` gets them.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "getSchemaBundle",
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/audiences": {
      "get": {
        "tags": [
          "Delivery: Sites and locales"
        ],
        "summary": "List the audiences content uses",
        "description": "The ids of the audiences that published content in the key's delivery environment names: a variant's condition or a page's restriction. Ask Audience about these and no others (`audiences` on `POST /audience/delivery/v1/resolve`), which keeps the signed token small. Ids only, never names or rules. The SDKs read this for you.\n\n**Key:** a delivery key (`content.delivery.read`).",
        "operationId": "listAudiencesInUse",
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/content/delivery/v1/preview/exchange": {
      "post": {
        "tags": [
          "Delivery: Preview"
        ],
        "summary": "Exchange a preview token",
        "description": "Turns the one-time `ebitex-preview-token` that live preview adds to a server-rendered site's URL into a preview session secret. Keep the secret in your own cookie, redirect to the same address without the token, and send the secret as `X-Ebitex-Preview-Session` when resolving paths.\n\nA token is valid for five minutes and can be spent once. A session ends when the editor closes live preview, after 30 minutes without an edit, or 8 hours after it started.\n\n**Key:** a server-side delivery key with `content.delivery.preview` (**Allow draft preview**). A browser-safe key can never hold it.",
        "operationId": "exchangePreviewToken",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PreviewExchangeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "`{ \"session\": \"cps_…\", \"expiresAtUtc\": \"…\" }`"
          },
          "401": {
            "description": "`invalid_preview_token`: the token is spent, expired, unknown, or was minted for another delivery environment.\n\n`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    },
    "/assets/delivery/v1/contracts": {
      "get": {
        "tags": [
          "Delivery: Assets"
        ],
        "summary": "Describe the asset types",
        "description": "Returns the three content types assets are described by: `ebitex-image`, `ebitex-video` and `ebitex-document`. Each lists its fields, and the `file` field carries the JSON Schema of the file object an asset of that type holds.\n\nFields an organization has added to a kind and marked as delivered follow the standard ones, so the list is the organization's own: each is described with its type, whether it is required, and its settings. Internal fields are never listed.\n\nThe Content integration reads this to register the types. Call it directly to learn the exact shape of a `file` before you parse one.\n\n**Key:** an Assets channel key (`assets.delivery.read`), minted for one channel in Assets. What it can see is that channel's.",
        "operationId": "describeAssetContracts",
        "responses": {
          "200": {
            "description": "`{ \"contracts\": [{ \"externalId\": \"ebitex-image\", \"name\": \"Image\", \"fields\": [] }] }`"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or its channel no longer exists."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks `assets.delivery.read`. `app_not_available`: Assets is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "429": {
            "description": "`too_many_failed_authentications`: too many bad keys from this address; `Retry-After` says how long to wait."
          }
        }
      }
    },
    "/assets/delivery/v1/assets": {
      "get": {
        "tags": [
          "Delivery: Assets"
        ],
        "summary": "List and search assets",
        "description": "Lists the assets the key's channel can see, newest first, one page at a time. Each item carries its `key` (the asset's id), its `contractExternalId` (`ebitex-image`, `ebitex-video` or `ebitex-document`), its `name`, a small `thumbnailUrl`, a one-line `subtitle`, and the full `document`, in the same form `GET /assets/{id}` returns. A `document` also carries the asset's delivered custom fields, each as its own member beside `name` and `file`.\n\n`contract` narrows to one type, `search` matches the asset's name anywhere in it without regard to case, and `limit` is 1 to 100 and defaults to 50. `nextCursor` is `null` on the last page; send it back as `cursor` for the next. A `contract` this API does not describe lists nothing.\n\nAn asset can be restricted to an audience. Send the visitor's audience token in the `X-Ebitex-Audiences` header (the content SDK does this for you). Without a token that grants the audience the asset has no link at all: this read answers `403` with `members_only` and the asset's `kind`, and nothing else about it. With one, the asset is returned with links that last five minutes and an `expiresAt` that says when they stop. A list shows a restricted asset the token does not grant as a stub: its `name`, its type, and `\"restricted\": true`, with no file and no thumbnail.\n\nA channel decides what a key sees: its folders, its categories, and whether private assets are included. An asset outside the channel answers `404`, exactly as one that does not exist, so a key can never read by id what it cannot list. Responses are never cached: links to private assets are minted for each response.\n\n**Key:** an Assets channel key (`assets.delivery.read`), minted for one channel in Assets. What it can see is that channel's.",
        "operationId": "listAssets",
        "parameters": [
          {
            "name": "contract",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`validation_failed`: `cursor` is not one this API returned."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or its channel no longer exists."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`scope_denied`: the key lacks `assets.delivery.read`. `app_not_available`: Assets is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "429": {
            "description": "`too_many_failed_authentications`: too many bad keys from this address; `Retry-After` says how long to wait."
          }
        }
      }
    },
    "/assets/delivery/v1/assets/{id}": {
      "get": {
        "tags": [
          "Delivery: Assets"
        ],
        "summary": "Get an asset with every rendition",
        "description": "Returns one asset with all of its renditions, and takes no `locale`. `file` is an object with a `default` rendition and a `locales` map of per-locale overrides, so one response serves every locale; `alt` and `caption` have the same shape. Delivered custom fields appear as members of the same object, named by their keys (for example `photographer`); a field the asset has no value for is left out, and fields marked internal in Assets are never returned.\n\nPick a locale yourself by looking `locales` up along your own fallback order and using `default` when none matches, or use `GET /assets/{id}/rendition` to have the API do it.\n\n`file.url` is a public link for a public asset. For a private asset on a channel that allows them it is a signed link that stops working after one hour, so use it promptly and do not store it.\n\nAn asset can be restricted to an audience. Send the visitor's audience token in the `X-Ebitex-Audiences` header (the content SDK does this for you). Without a token that grants the audience the asset has no link at all: this read answers `403` with `members_only` and the asset's `kind`, and nothing else about it. With one, the asset is returned with links that last five minutes and an `expiresAt` that says when they stop. A list shows a restricted asset the token does not grant as a stub: its `name`, its type, and `\"restricted\": true`, with no file and no thumbnail.\n\nA channel decides what a key sees: its folders, its categories, and whether private assets are included. An asset outside the channel answers `404`, exactly as one that does not exist, so a key can never read by id what it cannot list. Responses are never cached: links to private assets are minted for each response.\n\n**Key:** an Assets channel key (`assets.delivery.read`), minted for one channel in Assets. What it can see is that channel's.",
        "operationId": "getAsset",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "400": {
            "description": "`unknown_parameter`: this read takes no `locale`; use `GET /assets/{id}/rendition` for one locale."
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or its channel no longer exists."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`members_only`: the asset is restricted to an audience the visitor's token does not grant. The body names the asset's `kind` and nothing else.\n\n`scope_denied`: the key lacks `assets.delivery.read`. `app_not_available`: Assets is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "404": {
            "description": "`not_found`: there is no such asset, or it is outside the key's channel."
          },
          "429": {
            "description": "`too_many_failed_authentications`: too many bad keys from this address; `Retry-After` says how long to wait."
          }
        }
      }
    },
    "/assets/delivery/v1/assets/{id}/rendition": {
      "get": {
        "tags": [
          "Delivery: Assets"
        ],
        "summary": "Get an asset for one locale",
        "description": "Returns the same asset as `GET /assets/{id}`, with every localized member already resolved to a plain value for `locale`: `file` is the one rendition, and `alt` and `caption` are strings.\n\n`locale` is a code from the organization's locale tree. The API falls back along that tree from the code you send, for example `de-AT` then `de` then the default rendition. It never matches by prefix. Without `locale`, or when the organization has no locales, you get the default.\n\nAn asset can be restricted to an audience. Send the visitor's audience token in the `X-Ebitex-Audiences` header (the content SDK does this for you). Without a token that grants the audience the asset has no link at all: this read answers `403` with `members_only` and the asset's `kind`, and nothing else about it. With one, the asset is returned with links that last five minutes and an `expiresAt` that says when they stop. A list shows a restricted asset the token does not grant as a stub: its `name`, its type, and `\"restricted\": true`, with no file and no thumbnail.\n\nA channel decides what a key sees: its folders, its categories, and whether private assets are included. An asset outside the channel answers `404`, exactly as one that does not exist, so a key can never read by id what it cannot list. Responses are never cached: links to private assets are minted for each response.\n\n**Key:** an Assets channel key (`assets.delivery.read`), minted for one channel in Assets. What it can see is that channel's.",
        "operationId": "getAssetRendition",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired, or its channel no longer exists."
          },
          "402": {
            "description": "`payment_required`: the organization's subscription needs attention."
          },
          "403": {
            "description": "`members_only`: the asset is restricted to an audience the visitor's token does not grant. The body names the asset's `kind` and nothing else.\n\n`scope_denied`: the key lacks `assets.delivery.read`. `app_not_available`: Assets is not enabled for the organization. `origin_denied` or `ip_denied`: the key was used from an origin or address outside its restrictions."
          },
          "404": {
            "description": "`not_found`: there is no such asset, or it is outside the key's channel."
          },
          "429": {
            "description": "`too_many_failed_authentications`: too many bad keys from this address; `Retry-After` says how long to wait."
          }
        }
      }
    },
    "/content/delivery/v1/preview/blobs/{blobId}": {
      "get": {
        "tags": [
          "Delivery: Preview"
        ],
        "summary": "Download a not-yet-published blob's bytes",
        "description": "Streams a draft blob's bytes with its stored content type. This is the URL a resolved document's media field carries when its blob has no public URL yet (not yet published) — put it straight in `<img src>`, the same as the field's ordinary public URL. Never construct this URL yourself; `t` is a signed, self-verifying token minted only while resolving a document for preview, and expires 30 minutes after that.\n\nNeeds no `Authorization` header, cookie, or API key — the token is the whole credential, since this URL is meant to work in a plain browser image request on a different origin from the Content Delivery API.\n\n**Key:** none — `t` is the credential.",
        "operationId": "downloadPreviewBlob",
        "parameters": [
          {
            "name": "blobId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "t",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "`missing_api_key` or `invalid_api_key`: no key was sent, or it is unknown, revoked or expired."
          },
          "403": {
            "description": "`scope_denied`: the key lacks this route's scope. `origin_denied` or `ip_denied`: a browser-safe key was used from an origin or address outside its restrictions. `app_not_available`: Content is not enabled for the organization."
          },
          "404": {
            "description": "The token is missing, malformed, expired, or names a blob that no longer exists."
          },
          "429": {
            "description": "Quota exceeded; `Retry-After` says how long to wait. `too_many_failed_authentications`: too many bad keys from this address."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AccessRuleRequest": {
        "required": [
          "roleDefinitionId",
          "action"
        ],
        "type": "object",
        "properties": {
          "roleDefinitionId": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string"
          }
        }
      },
      "AccountAttributionInput": {
        "type": "object",
        "properties": {
          "source": {
            "type": [
              "null",
              "string"
            ]
          },
          "medium": {
            "type": [
              "null",
              "string"
            ]
          },
          "campaign": {
            "type": [
              "null",
              "string"
            ]
          },
          "isEmpty": {
            "type": "boolean"
          }
        }
      },
      "AdapterPreviewRequest": {
        "required": [
          "adapterId",
          "outputContractId",
          "rules",
          "provider",
          "key",
          "contextualValues",
          "context",
          "locale"
        ],
        "type": "object",
        "properties": {
          "adapterId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "outputContractId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "rules": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonArray"
              }
            ]
          },
          "provider": {
            "type": "string"
          },
          "key": {
            "type": "string"
          },
          "contextualValues": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          },
          "context": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonElement"
              }
            ]
          },
          "locale": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "AdapterValidateRequest": {
        "required": [
          "inputContractId",
          "outputContractId",
          "rules",
          "inputContractVersion"
        ],
        "type": "object",
        "properties": {
          "inputContractId": {
            "type": "string",
            "format": "uuid"
          },
          "outputContractId": {
            "type": "string",
            "format": "uuid"
          },
          "rules": {
            "$ref": "#/components/schemas/JsonArray"
          },
          "inputContractVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          }
        }
      },
      "AppendInteractionRequest": {
        "required": [
          "kind"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": [
              "null",
              "string"
            ]
          },
          "sourceEventId": {
            "type": [
              "null",
              "string"
            ]
          },
          "occurredAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "data": { },
          "text": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "Attribution": {
        "type": "object",
        "properties": {
          "source": {
            "type": [
              "null",
              "string"
            ]
          },
          "medium": {
            "type": [
              "null",
              "string"
            ]
          },
          "campaign": {
            "type": [
              "null",
              "string"
            ]
          },
          "isEmpty": {
            "type": "boolean"
          }
        }
      },
      "AudienceBehaviour": {
        "required": [
          "id",
          "kind",
          "filter",
          "minCount",
          "days"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "filter": {
            "type": [
              "null",
              "string"
            ]
          },
          "minCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "days": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "scope": {
            "type": [
              "null",
              "string"
            ]
          },
          "minMembers": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          }
        }
      },
      "BindCategoryGroupRequest": {
        "required": [
          "metadataFields"
        ],
        "type": "object",
        "properties": {
          "metadataFields": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/FieldRequest"
            }
          }
        }
      },
      "ChannelPermissionRequest": {
        "required": [
          "channel",
          "status",
          "basis"
        ],
        "type": "object",
        "properties": {
          "channel": {
            "type": [
              "null",
              "string"
            ]
          },
          "status": {
            "type": [
              "null",
              "string"
            ]
          },
          "basis": {
            "type": [
              "null",
              "string"
            ]
          },
          "note": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "CompleteUploadRequest": {
        "type": "object",
        "properties": {
          "parts": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/UploadedPartRequest"
            }
          },
          "name": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "CreateAdapterRequest": {
        "required": [
          "externalId",
          "name",
          "inputContractId",
          "outputContractId",
          "rules"
        ],
        "type": "object",
        "properties": {
          "externalId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "inputContractId": {
            "type": "string",
            "format": "uuid"
          },
          "outputContractId": {
            "type": "string",
            "format": "uuid"
          },
          "rules": {
            "$ref": "#/components/schemas/JsonArray"
          },
          "layout": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          }
        }
      },
      "CreateCategoryGroupRequest": {
        "required": [
          "externalId",
          "name",
          "metadataFields"
        ],
        "type": "object",
        "properties": {
          "externalId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "metadataFields": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/FieldRequest"
            }
          }
        }
      },
      "CreateCategoryRequest": {
        "required": [
          "categoryGroupId",
          "parentCategoryId",
          "key",
          "value",
          "isSelectable",
          "metadata"
        ],
        "type": "object",
        "properties": {
          "categoryGroupId": {
            "type": "string",
            "format": "uuid"
          },
          "parentCategoryId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "key": {
            "type": "string"
          },
          "value": {
            "$ref": "#/components/schemas/LocalizableOfstring"
          },
          "isSelectable": {
            "type": "boolean"
          },
          "metadata": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          }
        }
      },
      "CreateComponentRequest": {
        "required": [
          "name",
          "externalId",
          "contractId",
          "folderId",
          "document"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "externalId": {
            "type": "string"
          },
          "contractId": {
            "type": "string",
            "format": "uuid"
          },
          "folderId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "document": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "madeFor": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CreateContractRequest": {
        "required": [
          "name",
          "externalId",
          "parentContractId",
          "isAbstract",
          "fields"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "externalId": {
            "type": "string"
          },
          "parentContractId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "isAbstract": {
            "type": "boolean"
          },
          "fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FieldRequest"
            }
          },
          "defaultFolderId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "titleFieldPath": {
            "type": [
              "null",
              "string"
            ]
          },
          "referenceTree": { },
          "externalLinks": { },
          "systemParent": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "CreateFolderRequest": {
        "required": [
          "name",
          "parentFolderId"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "parentFolderId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          }
        }
      },
      "CreateIndividualRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": [
              "null",
              "string"
            ]
          },
          "identities": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/IdentityRequest"
            }
          },
          "attributes": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          },
          "channelPermissions": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/ChannelPermissionRequest"
            }
          },
          "displayName": {
            "type": [
              "null",
              "string"
            ]
          },
          "locale": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "CreateNodeRequest": {
        "required": [
          "parentNodeId",
          "name",
          "slug"
        ],
        "type": "object",
        "properties": {
          "parentNodeId": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "$ref": "#/components/schemas/LocalizableOfstring"
          }
        }
      },
      "CreateProposalRequest": {
        "required": [
          "contract",
          "folder",
          "source",
          "values"
        ],
        "type": "object",
        "properties": {
          "contract": {
            "type": "string"
          },
          "folder": {
            "type": [
              "null",
              "string"
            ]
          },
          "source": {
            "$ref": "#/components/schemas/ProposalSourceRequest"
          },
          "values": {
            "type": "object"
          }
        }
      },
      "CreateSiteRequest": {
        "required": [
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          }
        }
      },
      "CreateStreamRequest": {
        "required": [
          "externalId",
          "name",
          "sourceContractIds",
          "adapterId",
          "orderByFieldPath",
          "orderDescending",
          "resolveDepth",
          "excludedComponentIds",
          "declaredFilters"
        ],
        "type": "object",
        "properties": {
          "externalId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "sourceContractIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "adapterId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "orderByFieldPath": {
            "type": [
              "null",
              "string"
            ]
          },
          "orderDescending": {
            "type": "boolean"
          },
          "resolveDepth": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "excludedComponentIds": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "declaredFilters": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/StreamDeclaredFilter"
            }
          }
        }
      },
      "CreateTaskRequest": {
        "type": "object",
        "properties": {
          "title": {
            "type": [
              "null",
              "string"
            ]
          },
          "note": {
            "type": [
              "null",
              "string"
            ]
          },
          "assigneeExternalUserId": {
            "type": [
              "null",
              "string"
            ]
          },
          "assigneeLabel": {
            "type": [
              "null",
              "string"
            ]
          },
          "dueAtUtc": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          }
        }
      },
      "CreateTemplateRequest": {
        "required": [
          "name",
          "externalId",
          "supportedContractIds",
          "settingsFields"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "externalId": {
            "type": "string"
          },
          "supportedContractIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "settingsFields": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/FieldRequest"
            }
          },
          "settingsContractId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          }
        }
      },
      "DeploymentExecuteRequest": {
        "required": [
          "root",
          "items"
        ],
        "type": "object",
        "properties": {
          "root": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/PromotionRootRequest"
              }
            ]
          },
          "items": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/PromotionExecuteItemRequest"
            }
          }
        }
      },
      "DeploymentPlanRequest": {
        "required": [
          "kind",
          "id"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "EnsureFolderRequest": {
        "required": [
          "path"
        ],
        "type": "object",
        "properties": {
          "path": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "EventItem": {
        "required": [
          "kind",
          "subject",
          "value",
          "eventId"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": [
              "null",
              "string"
            ]
          },
          "subject": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/EventSubjectInput"
              }
            ]
          },
          "value": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "eventId": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "EventSubjectInput": {
        "required": [
          "categories"
        ],
        "type": "object",
        "properties": {
          "categories": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        }
      },
      "FieldRequest": {
        "required": [
          "name",
          "externalId",
          "fieldTypeKey",
          "isMandatory",
          "isEnumerable",
          "isLocalizable",
          "isContextual",
          "isPersonalizable",
          "settings"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "externalId": {
            "type": "string"
          },
          "fieldTypeKey": {
            "type": "string"
          },
          "isMandatory": {
            "type": "boolean"
          },
          "isEnumerable": {
            "type": "boolean"
          },
          "isLocalizable": {
            "type": "boolean"
          },
          "isContextual": {
            "type": "boolean"
          },
          "isPersonalizable": {
            "type": "boolean"
          },
          "settings": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonNode"
              }
            ]
          },
          "defaultValue": { }
        }
      },
      "HandoffRequest": {
        "required": [
          "code",
          "visitor",
          "consent"
        ],
        "type": "object",
        "properties": {
          "code": {
            "type": [
              "null",
              "string"
            ]
          },
          "visitor": {
            "type": [
              "null",
              "string"
            ]
          },
          "consent": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdentifyConsentInput"
              }
            ]
          }
        }
      },
      "IdentifyAccountRequest": {
        "required": [
          "externalId"
        ],
        "type": "object",
        "properties": {
          "externalId": {
            "type": [
              "null",
              "string"
            ]
          },
          "name": {
            "type": [
              "null",
              "string"
            ]
          },
          "role": {
            "type": [
              "null",
              "string"
            ]
          },
          "attributes": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          },
          "attribution": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/AccountAttributionInput"
              }
            ]
          }
        }
      },
      "IdentifyConsentInput": {
        "required": [
          "cmp",
          "gpc",
          "dnt",
          "cmpString"
        ],
        "type": "object",
        "properties": {
          "cmp": {
            "type": [
              "null",
              "string"
            ]
          },
          "gpc": {
            "type": "boolean"
          },
          "dnt": {
            "type": "boolean"
          },
          "cmpString": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "IdentifyInteractionRequest": {
        "required": [
          "kind"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": [
              "null",
              "string"
            ]
          },
          "sourceEventId": {
            "type": [
              "null",
              "string"
            ]
          },
          "occurredAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "data": { }
        }
      },
      "IdentifyRequest": {
        "required": [
          "identity"
        ],
        "type": "object",
        "properties": {
          "identity": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdentityRequest"
              }
            ]
          },
          "visitor": {
            "type": [
              "null",
              "string"
            ]
          },
          "consent": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdentifyConsentInput"
              }
            ]
          },
          "name": {
            "type": [
              "null",
              "string"
            ]
          },
          "attributes": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          },
          "interaction": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdentifyInteractionRequest"
              }
            ]
          },
          "source": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdentifySourceRequest"
              }
            ]
          },
          "account": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdentifyAccountRequest"
              }
            ]
          },
          "verification": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdentifyVerificationRequest"
              }
            ]
          },
          "attribution": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Attribution"
              }
            ]
          },
          "locale": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "IdentifySourceRequest": {
        "required": [
          "label"
        ],
        "type": "object",
        "properties": {
          "label": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "IdentifyVerificationMessage": {
        "required": [
          "subject",
          "text"
        ],
        "type": "object",
        "properties": {
          "subject": {
            "type": [
              "null",
              "string"
            ]
          },
          "text": {
            "type": [
              "null",
              "string"
            ]
          },
          "html": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "IdentifyVerificationRequest": {
        "type": "object",
        "properties": {
          "audienceId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "returnUrl": {
            "type": [
              "null",
              "string"
            ]
          },
          "message": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdentifyVerificationMessage"
              }
            ]
          }
        }
      },
      "IdentityRequest": {
        "required": [
          "type",
          "value"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": [
              "null",
              "string"
            ]
          },
          "value": {
            "type": [
              "null",
              "string"
            ]
          },
          "system": {
            "type": [
              "null",
              "string"
            ]
          },
          "verified": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/VerifiedRequest"
              }
            ]
          },
          "mergeOnConflict": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "JsonArray": {
        "type": "array"
      },
      "JsonElement": { },
      "JsonNode": { },
      "JsonObject": {
        "type": "object"
      },
      "LocalizableOfstring": { },
      "LocalizedText": {
        "required": [
          "default"
        ],
        "type": "object",
        "properties": {
          "default": {
            "type": "string"
          },
          "locales": {
            "type": [
              "null",
              "object"
            ],
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "MoveComponentsRequest": {
        "required": [
          "componentIds",
          "folderId"
        ],
        "type": "object",
        "properties": {
          "componentIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "folderId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          }
        }
      },
      "MoveNodeRequest": {
        "required": [
          "newParentNodeId"
        ],
        "type": "object",
        "properties": {
          "newParentNodeId": {
            "type": "string",
            "format": "uuid"
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          }
        }
      },
      "MoveStageRequest": {
        "required": [
          "transitionId"
        ],
        "type": "object",
        "properties": {
          "transitionId": {
            "type": "string",
            "format": "uuid"
          },
          "comment": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "OpenAccountStageRequest": {
        "type": "object",
        "properties": {
          "openedAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          }
        }
      },
      "OpenStageRequest": {
        "type": "object",
        "properties": {
          "facts": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          }
        }
      },
      "PreviewExchangeRequest": {
        "required": [
          "token"
        ],
        "type": "object",
        "properties": {
          "token": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "PreviewOwner": {
        "required": [
          "kind",
          "id"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "PreviewResolveRequest": {
        "required": [
          "kind",
          "id",
          "document",
          "locale",
          "ctx"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "document": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          },
          "locale": {
            "type": [
              "null",
              "string"
            ]
          },
          "ctx": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          },
          "resolveDepth": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32",
            "default": 0
          },
          "annotate": {
            "type": "boolean",
            "default": false
          },
          "overrides": {
            "type": [
              "null",
              "object"
            ],
            "additionalProperties": {
              "$ref": "#/components/schemas/JsonObject"
            }
          },
          "deliveryEnvironmentId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "owner": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/PreviewOwner"
              }
            ]
          },
          "path": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonArray"
              }
            ]
          },
          "wrap": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/PreviewWrapLink"
            }
          },
          "subject": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          }
        }
      },
      "PreviewWrapLink": {
        "required": [
          "template",
          "templateVersion",
          "settings",
          "field"
        ],
        "type": "object",
        "properties": {
          "template": {
            "type": "string",
            "format": "uuid"
          },
          "templateVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "settings": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          },
          "field": {
            "type": "string"
          },
          "contract": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "contractVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          }
        }
      },
      "PromotionExecuteItemRequest": {
        "required": [
          "kind",
          "id",
          "confirmOverwrite"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "confirmOverwrite": {
            "type": [
              "null",
              "boolean"
            ]
          }
        }
      },
      "PromotionRootRequest": {
        "required": [
          "kind",
          "id"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "ProposalSourceRequest": {
        "required": [
          "kind",
          "ref",
          "label"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "ref": {
            "type": "string"
          },
          "label": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "PublicConsentRequest": {
        "required": [
          "cmp",
          "gpc",
          "dnt",
          "cmpString"
        ],
        "type": "object",
        "properties": {
          "cmp": {
            "type": [
              "null",
              "string"
            ]
          },
          "gpc": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "dnt": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "cmpString": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "RecordEventRequest": {
        "required": [
          "visitor",
          "consent",
          "kind",
          "subject",
          "value",
          "eventId",
          "events"
        ],
        "type": "object",
        "properties": {
          "visitor": {
            "type": [
              "null",
              "string"
            ]
          },
          "consent": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdentifyConsentInput"
              }
            ]
          },
          "kind": {
            "type": [
              "null",
              "string"
            ]
          },
          "subject": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/EventSubjectInput"
              }
            ]
          },
          "value": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "eventId": {
            "type": [
              "null",
              "string"
            ]
          },
          "events": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/EventItem"
            }
          }
        }
      },
      "RecordInteractionRequest": {
        "required": [
          "visitor",
          "name",
          "properties",
          "interactionId",
          "consent"
        ],
        "type": "object",
        "properties": {
          "visitor": {
            "type": [
              "null",
              "string"
            ]
          },
          "name": {
            "type": [
              "null",
              "string"
            ]
          },
          "properties": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          },
          "interactionId": {
            "type": [
              "null",
              "string"
            ]
          },
          "consent": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdentifyConsentInput"
              }
            ]
          }
        }
      },
      "ReorderNodesRequest": {
        "required": [
          "parentNodeId",
          "orderedNodeIds"
        ],
        "type": "object",
        "properties": {
          "parentNodeId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "orderedNodeIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        }
      },
      "ReplaceAccessRulesRequest": {
        "required": [
          "rules"
        ],
        "type": "object",
        "properties": {
          "rules": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccessRuleRequest"
            }
          }
        }
      },
      "ReplaceAllowedContractsRequest": {
        "required": [
          "contractIds"
        ],
        "type": "object",
        "properties": {
          "contractIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        }
      },
      "ReplaceAllowedTemplatesRequest": {
        "required": [
          "templateIds"
        ],
        "type": "object",
        "properties": {
          "templateIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        }
      },
      "ReplaceEntityCategoriesRequest": {
        "required": [
          "categoryIds"
        ],
        "type": "object",
        "properties": {
          "categoryIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        }
      },
      "ResolveAudiencesRequest": {
        "required": [
          "ctx",
          "visitor",
          "consent"
        ],
        "type": "object",
        "properties": {
          "ctx": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonElement"
              }
            ]
          },
          "visitor": {
            "type": [
              "null",
              "string"
            ]
          },
          "consent": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdentifyConsentInput"
              }
            ]
          },
          "audiences": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string",
              "format": "uuid"
            }
          }
        }
      },
      "ResolveReviewRequest": {
        "type": "object",
        "properties": {
          "resolution": {
            "type": [
              "null",
              "string"
            ]
          },
          "survivorId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          }
        }
      },
      "SaveAudienceRequest": {
        "required": [
          "externalId",
          "name",
          "predicate",
          "rowVersion"
        ],
        "type": "object",
        "properties": {
          "externalId": {
            "type": [
              "null",
              "string"
            ]
          },
          "name": {
            "type": [
              "null",
              "string"
            ]
          },
          "predicate": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonElement"
              }
            ]
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          },
          "behaviours": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/AudienceBehaviour"
            }
          },
          "scorePoints": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          }
        }
      },
      "SetChannelPermissionRequest": {
        "required": [
          "status",
          "basis"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": [
              "null",
              "string"
            ]
          },
          "basis": {
            "type": [
              "null",
              "string"
            ]
          },
          "note": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "SetMemberRequest": {
        "required": [
          "role"
        ],
        "type": "object",
        "properties": {
          "role": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "SetPayloadRequest": {
        "required": [
          "kind",
          "presentation",
          "targetNodeId"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "presentation": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          },
          "targetNodeId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          }
        }
      },
      "SetVisibilityRequest": {
        "required": [
          "visibility"
        ],
        "type": "object",
        "properties": {
          "visibility": {
            "type": "string"
          }
        }
      },
      "StartSignInRequest": {
        "required": [
          "email",
          "audienceId",
          "visitor",
          "returnUrl",
          "consent"
        ],
        "type": "object",
        "properties": {
          "email": {
            "type": [
              "null",
              "string"
            ]
          },
          "audienceId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "visitor": {
            "type": [
              "null",
              "string"
            ]
          },
          "returnUrl": {
            "type": [
              "null",
              "string"
            ]
          },
          "consent": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/IdentifyConsentInput"
              }
            ]
          }
        }
      },
      "StartUploadRequest": {
        "required": [
          "kind",
          "sizeBytes",
          "filename",
          "contentType"
        ],
        "type": "object",
        "properties": {
          "kind": {
            "type": "string"
          },
          "sizeBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int64"
          },
          "filename": {
            "type": "string"
          },
          "contentType": {
            "type": [
              "null",
              "string"
            ]
          },
          "visibility": {
            "type": [
              "null",
              "string"
            ]
          },
          "folderId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "assetId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "locale": {
            "type": [
              "null",
              "string"
            ]
          },
          "openInBrowser": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "metadata": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          }
        }
      },
      "StreamDeclaredFilter": {
        "required": [
          "key",
          "type"
        ],
        "type": "object",
        "properties": {
          "key": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "fieldPath": {
            "type": [
              "null",
              "string"
            ]
          },
          "labelFieldPath": {
            "type": [
              "null",
              "string"
            ]
          },
          "groupExternalId": {
            "type": [
              "null",
              "string"
            ]
          },
          "mode": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "StreamPreviewRequest": {
        "required": [
          "deliveryEnvironmentId",
          "locale",
          "limit",
          "filters"
        ],
        "type": "object",
        "properties": {
          "deliveryEnvironmentId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "locale": {
            "type": [
              "null",
              "string"
            ]
          },
          "limit": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "filters": {
            "type": [
              "null",
              "object"
            ],
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "UpdateAdapterRequest": {
        "required": [
          "name",
          "inputContractId",
          "outputContractId",
          "rules",
          "rowVersion"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": [
              "null",
              "string"
            ]
          },
          "inputContractId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "outputContractId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "rules": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonArray"
              }
            ]
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          },
          "layout": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          }
        }
      },
      "UpdateAssetRequest": {
        "required": [
          "name",
          "folderId",
          "title",
          "alt",
          "caption"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "folderId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "title": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/LocalizedText"
              }
            ]
          },
          "alt": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/LocalizedText"
              }
            ]
          },
          "caption": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/LocalizedText"
              }
            ]
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          },
          "openInBrowser": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "metadata": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          }
        }
      },
      "UpdateCategoryGroupRequest": {
        "required": [
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          }
        }
      },
      "UpdateCategoryRequest": {
        "required": [
          "key",
          "value",
          "isSelectable",
          "metadata",
          "parentCategoryId",
          "rowVersion"
        ],
        "type": "object",
        "properties": {
          "key": {
            "type": [
              "null",
              "string"
            ]
          },
          "value": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/LocalizableOfstring"
              }
            ]
          },
          "isSelectable": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "metadata": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          },
          "parentCategoryId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          }
        }
      },
      "UpdateComponentRequest": {
        "required": [
          "name",
          "document",
          "folderId"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "document": {
            "$ref": "#/components/schemas/JsonObject"
          },
          "folderId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "repinToLatest": {
            "type": "boolean",
            "default": false
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          },
          "madeFor": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        }
      },
      "UpdateContractRequest": {
        "required": [
          "name",
          "externalId",
          "parentContractId",
          "isAbstract",
          "fields"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "externalId": {
            "type": "string"
          },
          "parentContractId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "isAbstract": {
            "type": "boolean"
          },
          "fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FieldRequest"
            }
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          },
          "defaultFolderId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "titleFieldPath": {
            "type": [
              "null",
              "string"
            ]
          },
          "referenceTree": { },
          "externalLinks": { },
          "systemParent": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "UpdateFolderRequest": {
        "required": [
          "name",
          "parentFolderId"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "parentFolderId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          }
        }
      },
      "UpdateIndividualRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": [
              "null",
              "string"
            ]
          },
          "attributes": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          },
          "displayName": {
            "type": [
              "null",
              "string"
            ]
          },
          "locale": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "UpdateNodeRequest": {
        "required": [
          "name",
          "slug"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "slug": {
            "$ref": "#/components/schemas/LocalizableOfstring"
          },
          "excludeFromSitemap": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          },
          "madeFor": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          }
        }
      },
      "UpdateStageFactsRequest": {
        "required": [
          "rowVersion"
        ],
        "type": "object",
        "properties": {
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "uint32"
          },
          "facts": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          }
        }
      },
      "UpdateStreamRequest": {
        "required": [
          "name",
          "sourceContractIds",
          "adapterId",
          "orderByFieldPath",
          "orderDescending",
          "resolveDepth",
          "excludedComponentIds",
          "declaredFilters",
          "rowVersion"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "sourceContractIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "adapterId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "orderByFieldPath": {
            "type": [
              "null",
              "string"
            ]
          },
          "orderDescending": {
            "type": "boolean"
          },
          "resolveDepth": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "excludedComponentIds": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "declaredFilters": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/StreamDeclaredFilter"
            }
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          }
        }
      },
      "UpdateTemplateRequest": {
        "required": [
          "name",
          "externalId",
          "supportedContractIds"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "externalId": {
            "type": "string"
          },
          "supportedContractIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "settingsContractId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "rowVersion": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "uint32"
          }
        }
      },
      "UploadedPartRequest": {
        "required": [
          "partNumber",
          "eTag"
        ],
        "type": "object",
        "properties": {
          "partNumber": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "eTag": {
            "type": "string"
          }
        }
      },
      "UpsertAccountRequest": {
        "required": [
          "name",
          "attributes"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": [
              "null",
              "string"
            ]
          },
          "attributes": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/JsonObject"
              }
            ]
          },
          "attribution": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/AccountAttributionInput"
              }
            ]
          }
        }
      },
      "VerifiedRequest": {
        "required": [
          "method"
        ],
        "type": "object",
        "properties": {
          "method": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "VisitorForgetBody": {
        "required": [
          "visitor"
        ],
        "type": "object",
        "properties": {
          "visitor": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "VisitorSessionRequest": {
        "required": [
          "visitor"
        ],
        "type": "object",
        "properties": {
          "visitor": {
            "type": [
              "null",
              "string"
            ]
          },
          "consent": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/PublicConsentRequest"
              }
            ]
          }
        }
      }
    },
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "description": "An ebitex API key, sent as `Authorization: Bearer <key>`. Each API takes its own kind of key: a delivery key, an Assets channel key, a management key, a deployment key, an Audience management key, or a Forms SDK key.",
        "scheme": "bearer"
      }
    }
  },
  "security": [
    {
      "apiKey": [ ]
    }
  ],
  "tags": [
    {
      "name": "Delivery: Sites and locales",
      "description": "The published sites a delivery key can read, and the organization's locales. Rendering a site? Use `@ebitex/content-sdk` (JavaScript, TypeScript) or `Ebitex.Content.Delivery` (.NET) instead of calling these endpoints by hand."
    },
    {
      "name": "Delivery: Pages",
      "description": "Resolve a path to its page, and read the page tree for navigation and sitemaps."
    },
    {
      "name": "Delivery: Components",
      "description": "Read published components directly, and the schema of what delivery returns."
    },
    {
      "name": "Delivery: Streams",
      "description": "Query authored streams: a named, filterable listing an editor defines."
    },
    {
      "name": "Delivery: Media",
      "description": "Raw bytes of published media, for server-side use."
    },
    {
      "name": "Delivery: Preview",
      "description": "Draft preview for a server-rendered site."
    },
    {
      "name": "Delivery: Assets",
      "description": "Read the images, videos and documents a channel key may see, with their localized renditions."
    },
    {
      "name": "Management: Assets",
      "description": "Put images into the Assets library from a script or a pipeline: find what is already there by the hash of the file, upload what is not, and make it public."
    },
    {
      "name": "Delivery: Data",
      "description": "Read the models and records of one Data partition: a record by key with its references followed, and filtered, sorted, faceted lists."
    },
    {
      "name": "Forms SDK: Forms",
      "description": "Read your organization's forms, to render them in your own application."
    },
    {
      "name": "Management: Key",
      "description": "What the key you are calling with holds, and where it sits."
    },
    {
      "name": "Management: Bundles and publishing",
      "description": "Move content in and out as transfer bundles, and publish it."
    },
    {
      "name": "Management: Contracts",
      "description": "Content types: the fields a component carries."
    },
    {
      "name": "Management: Templates",
      "description": "Presentations of a contract, and their settings."
    },
    {
      "name": "Management: Components",
      "description": "The content itself."
    },
    {
      "name": "Management: Folders",
      "description": "The folder tree components live in, and who may edit each branch."
    },
    {
      "name": "Management: Experience",
      "description": "Sites and the pages beneath them."
    },
    {
      "name": "Management: Taxonomy",
      "description": "Category groups, categories, and classification."
    },
    {
      "name": "Management: Adapters",
      "description": "Reusable field mappings from one contract to another."
    },
    {
      "name": "Management: Streams",
      "description": "Stream definitions: the listings delivery serves."
    },
    {
      "name": "Management: Proposals",
      "description": "Offer content from outside the organization, for a person to review."
    },
    {
      "name": "Management: Checks and previews",
      "description": "Validation, previews and reports that change nothing."
    },
    {
      "name": "Deployment: Promotion",
      "description": "Promote content along the one edge a deployment key is bound to: plan it, then execute it."
    },
    {
      "name": "Audience: People",
      "description": "The people an organization knows: identities, permissions and interactions, and the one call that identifies a visitor."
    },
    {
      "name": "Audience: Lifecycle",
      "description": "Where each person stands in the five lifecycle stages, the passes you can move, and the review queue of duplicates and tasks."
    },
    {
      "name": "Audience: Audiences",
      "description": "The named rules that decide who sees personalized content: define them from a script, and read which ones a person is in."
    },
    {
      "name": "Audience: Accounts",
      "description": "Optional groups of people (a company, a household, a school), found by your own external id: create or update them, and add or remove their people."
    },
    {
      "name": "Delivery: Audiences",
      "description": "Ask which audiences a visitor is in, from the presentation app, so personalized content can be requested with the answer."
    },
    {
      "name": "Delivery: Visitors",
      "description": "Recognise a device with its consent, and forget it, from a site that has no form."
    }
  ]
}