{
  "schema": "aaes/capability-pack/1",
  "provider": "notion",
  "version": "1.0.0",
  "description": "Search Notion pages and data sources, query data-source rows, create pages, update pages, and trash blocks. Page updates can trash a page or irreversibly erase its block children.",
  "notes": "The endpoint host 'notion-api.invalid' is a reserved non-production placeholder (an RFC 2606 reserved TLD): substitute the real Notion API host before enabling, in every endpoint, in the resource locator and in every egress allowlist entry — the reviewed documentation names the base URL as https://api.notion.com with the /v1 path prefix. The allowlist matches exactly, so endpoints and allowlist must be substituted together, and substitution is a required deployment step. PAGE_ID, BLOCK_ID and DATA_SOURCE_ID in the endpoint paths are placeholders the deployment binds before enabling.\n\nNotion-Version pinning: the reviewed documentation requires a Notion-Version request header on every call, and the operation and authorization pages retrieved on 2026-09-21 show '2026-03-11' as the current documented version — that is the version this pack's registered contracts (search, page create and update, data source query, block delete) were reviewed against, so the deployment must send 'Notion-Version: 2026-03-11' on every call; adopting any newer version is a re-review trigger, not a configuration detail.\n\nThe registered endpoint fixes the URL; the pinned version header completes the reviewed contract.\n\nDocumented exclusions: this pack does not register public sharing of a page to the web ('Share to web' / publish).\n\nSharing a page publicly is a share effect — disclosed information cannot be made private again once the other party has it — so it would register as share at the minimum R3 tier, but no publish or page-sharing endpoint page could be retrieved during the documented review (publish-page and update-page-settings returned 404, and the retrieved PATCH page documentation shows public_url only as a response field): rather than guess an endpoint, this pack registers no sharing operation at all.\n\nFurther documented exclusions: user and bot management, comments, file uploads, data source schema updates (updating a data source alters the schema of a whole database, a extensive change needing its own review), block content append and update, and the OAuth token exchange for public connections.\n\nApproval classification: notion.page.update registers update and delete at R3 because the reviewed documentation shows the same request body can set in_trash (trashing the page) and erase_content, which the documentation states deletes all of the page's block children and 'cannot be reversed using the API' — an unrestricted request body defeats a lower tier, and intended limits need a separate enforceable control; a deployment needing property-only updates at R2 must remove this capability rather than rely on caller intent.",
  "credential": {
    "secret_ref_id": "secret-ref-notion-integration-token",
    "custody": "customer_vault",
    "inject": "header",
    "inject_name": "Authorization",
    "note": "A Notion connection (integration) token for the customer's workspace, held in the client's own secret manager. The retrieved documentation names three bearer-token kinds — an installation access token from an internal connection, an OAuth access token from a public connection, and a personal access token; this pack registers the internal-connection token, issued in Notion's developer portal when a workspace admin creates the connection. The stored material must be the complete Authorization header value: the scheme 'Bearer', one separating space, then the token — 'Bearer <token>'.\n\nThe token's reach is limited to the pages and data sources a workspace member has explicitly shared with the connection, and to the capabilities (read, update, insert content) chosen at creation — choose the minimum: the retrieved delete-block and patch-page documentation both state the call fails with 403 unless the connection holds update content capabilities, so a read-mostly deployment should withhold update capability and accept that the write capabilities in this pack will fail closed.\n\nLifecycle: the retrieved authorization guide describes the internal-connection installation access token as static — the connection sends the same token in every API request — and does not document token expiry, nor whether regenerating or disabling a connection invalidates already-issued tokens; that documentation gap stands after retrieval of the authorization guide on 2026-09-21, so the deployment must confirm revocation behaviour in the developer portal before enabling and must treat disabling issuance as not necessarily invalidating already-issued tokens until confirmed; a revoked or rejected value fails closed.\n\nRotation ownership: the external deployment process that creates the connection in the developer portal also owns ongoing rotation and replacement of the stored value — AAES creates nothing and the injector performs no token lifecycle; name the owning team in the deployment's custody configuration before enabling.\n\nExecutor hygiene: the value must live only in executor memory for the lifetime of a call, must never be written to logs, traces or diagnostics, and must not appear in crash dumps; a deployment that cannot meet that bar must not enable the pack."
  },
  "capabilities": [
    {
      "capability_id": "notion.search",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "notion-api.invalid"
      ],
      "endpoint": "https://notion-api.invalid/v1/search",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-notion-integration-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "notion-workspace",
      "description": "Search pages and data sources shared with the connection: a POST-with-query-body endpoint registered read-only at the read floor. Body shape: an optional query string, a filter object selecting object type (page or data_source), a sort direction, and the documented pagination fields start_cursor and page_size. The operation reference page (POST /v1/search) was retrieved on 2026-09-21 and confirms this path and request-body contract.",
      "requires_review": true
    },
    {
      "capability_id": "notion.data_source.query",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "notion-api.invalid"
      ],
      "endpoint": "https://notion-api.invalid/v1/data_sources/DATA_SOURCE_ID/query",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-notion-integration-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "notion-workspace",
      "description": "Query one data source: a POST-with-query-body endpoint registered read-only at the read floor; the operation reference page (query a data source) was retrieved on 2026-09-21 and confirms this exact path and the complete request contract. Body shape: filter and sort clauses (including compound and/or filter groups) plus the documented pagination fields start_cursor and page_size, with the filter_properties query parameter narrowing returned schema properties.\n\nData source rows frequently describe people (contacts, employees, customers), so the read is classified as pii (personal data); declare internal only after the owner classifies the base. DATA_SOURCE_ID is a placeholder the deployment binds before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "notion.page.create",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R2",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "notion-api.invalid"
      ],
      "endpoint": "https://notion-api.invalid/v1/pages",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-notion-integration-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "create"
      ],
      "resource_id": "notion-workspace",
      "description": "Create one page under a parent page or data source; a created page can later be trashed, so create sits at the minimum R2 tier for reversible writes.\n\nThe registration does not restrict the body, so the caller chooses the parent and properties; the operation reference page (POST /v1/pages) was retrieved on 2026-09-21 and confirms the path and that the body carries parent, properties, optional children and an optional template — a selected template is applied asynchronously by Notion after the API request finishes, and the integration must hold Insert Content capabilities on the parent or the call fails with 403.",
      "requires_review": true
    },
    {
      "capability_id": "notion.page.update",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "notion-api.invalid"
      ],
      "endpoint": "https://notion-api.invalid/v1/pages/PAGE_ID",
      "method": "PATCH",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-notion-integration-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "update",
        "delete"
      ],
      "resource_id": "notion-workspace",
      "description": "Update one page. The retrieved documentation shows the request body can update properties, icon, cover and lock state, but can ALSO set in_trash:true (trashing the page) and erase_content:true — which the documentation states deletes all of the page's block children and 'cannot be reversed using the API'. The registration does not restrict the request body, so the capability's worst-case effect is irreversible content erasure: it declares update and delete at the minimum R3 tier — delete is one of the designated actions requiring human approval.\n\nOn supported AAES-mediated execution paths, irreversible actions require approval by an authorized person; this does not prevent calls through bypass or observation-only paths. A deployment needing property-only updates at R2 must remove this capability and register a body-restricted surface elsewhere; intended limits need a separate enforceable control. The connection must hold update content capabilities on the page or the call fails with 403. PAGE_ID is a placeholder the deployment binds before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "notion.block.delete",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "notion-api.invalid"
      ],
      "endpoint": "https://notion-api.invalid/v1/blocks/BLOCK_ID",
      "method": "DELETE",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-notion-integration-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "delete"
      ],
      "resource_id": "notion-workspace",
      "description": "Trash one block, including page blocks: the reviewed documentation states the call sets the block to in_trash:true and that it can be restored via the update APIs. Restore availability does not lower the floor: delete is one of the designated actions requiring human approval and registers at R3. The connection must hold update content capabilities on the block or the call fails with 403. BLOCK_ID is a placeholder the deployment binds before enabling.",
      "requires_review": true
    }
  ],
  "resources": [
    {
      "resource_id": "notion-workspace",
      "kind": "site",
      "locator": "https://notion-api.invalid/v1",
      "data_class": "pii",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    }
  ],
  "review": {
    "state": "library",
    "date": "2026-09-23",
    "sources": [
      "https://developers.notion.com/reference/intro (retrieved 2026-09-21)",
      "https://developers.notion.com/reference/patch-page (retrieved 2026-09-21)",
      "https://developers.notion.com/reference/delete-a-block (retrieved 2026-09-21)",
      "https://developers.notion.com/reference/post-search (retrieved 2026-09-21)",
      "https://developers.notion.com/reference/post-page (retrieved 2026-09-21)",
      "https://developers.notion.com/reference/query-a-data-source (retrieved 2026-09-21)",
      "https://developers.notion.com/docs/authorization (retrieved 2026-09-21)"
    ],
    "reviewer_id": "aaes-eng"
  }
}
