{
  "schema": "aaes/capability-pack/1",
  "provider": "looker",
  "version": "1.0.0",
  "description": "Read the Looker API user's profile and dashboard and Look listings; run queries, create folders, and delete Looks and dashboards. Query results can include any data the credential's user can access.",
  "notes": "The endpoint host 'looker-instance.invalid' is a reserved non-production placeholder (an RFC 2606 reserved TLD): substitute the customer's Looker instance host before enabling, in every endpoint, in the resource locator and in every egress allowlist entry. The allowlist matches exactly, so endpoints and allowlist must be substituted together, and substitution is a required deployment step. LOOK_ID, DASHBOARD_ID and RESULT_FORMAT in the endpoint paths are placeholders the deployment binds before enabling.\n\nThe retrieved authentication documentation confirms the API path prefix with the example GET /api/4.0/user on the instance host. The Looker 4.0 endpoint-by-endpoint reference has since been retrieved live at cloud.google.com/looker/docs/reference/looker-api/latest/methods/ (method pages for Query/run_inline_query, Folder/create_folder, Dashboard/all_dashboards, Dashboard/delete_dashboard, Look/all_looks, Look/delete_look, ApiAuth/login and ApiAuth/logout), and every operation path in this pack is verified against it; the instance's own API Explorer (which every Looker instance serves) remains a useful deployment check.\n\nDocumented exclusions: this pack does not register user administration (creating or deleting users, or creating or deleting their API credentials), sudo or user impersonation (login_as_user), scheduled-plan creation and one-shot delivery (sending dashboards or Looks to email, Slack, S3 or SFTP — send is one of the designated actions requiring human approval and a delivered copy cannot be recalled after delivery), LookML project git operations and deploys, database connection management, content access-grant APIs, and the OAuth CORS authentication mode for frontend clients — each a deliberate exclusion requiring its own review, not an approximated capability.\n\nApproval classification: query execution is registered read at the read floor because its governed effect is read-only over business data — it modifies none of the content or configuration this pack manages — while provider-side side effects (query history, server-side caches, and any derived-table activity a query triggers in the database) are real but outside what this registration claims to enumerate; and a query can return any data the credential's user can see — the deployment must raise the data class (and policy the tier) wherever the exposed models carry regulated data, since an unrestricted query body selects arbitrary models and explores and the registration does not restrict it.",
  "credential": {
    "secret_ref_id": "secret-ref-looker-access-token",
    "custody": "customer_vault",
    "inject": "header",
    "inject_name": "Authorization",
    "note": "Looker issues API credentials per Looker user as two values — a client ID and a client secret — created by an admin on the Users page in the Admin section (Looker original) or by the user on their Account page once API management is enabled (Looker Google Cloud core). The credential pair is used only to obtain a token: neither the client ID nor the client secret is what the executor injects.\n\nAn external deployment process holds the pair in the client's own secret manager under a separate reference, exchanges them at the instance's login endpoint (POST /api/4.0/login, according to the reviewed authentication documentation) for a short-lived OAuth 2.0 access token, and stores under this reference the complete Authorization header value the reviewed documentation shows: the scheme 'token', one separating space, then the access token — 'token <access_token>'.\n\nThe external process renews the stored value before expiry; the injector injects the stored value verbatim and performs no login itself.\n\nToken lifetime: the reviewed documentation names the login and logout (DELETE /api/4.0/logout) endpoints and states an access token can be used on multiple API requests until it expires or is invalidated by calling logout, but the api-auth page states NO numeric token lifetime, while the retrieved login reference documents an expires_in seconds-to-expiry field in the token response — the external renewal process must therefore renew proactively using expires_in (or a deployment-chosen interval) and treat a 401 as fail-closed: re-login, never retry with the dead value.\n\nThe retrieved documentation also does not state that deleting or rotating the underlying client credentials invalidates already-issued access tokens — so the deployment must treat issued tokens as live until expiry or explicit logout (disabling issuance is not revocation), while an expired or logged-out value fails closed. Deleting the bound Looker user account invalidates that user's API credentials.\n\nLeast privilege, according to the reviewed guidance: never use credentials bound to a Looker admin account in production — create a minimal-privilege service account, grant only the permissions these capabilities need, and bind the credentials to it; API calls execute with exactly that user's permissions.\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": "looker.user.me.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "looker-instance.invalid"
      ],
      "endpoint": "https://looker-instance.invalid/api/4.0/user",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-looker-access-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "looker-instance",
      "description": "Read the profile of the user the API credentials are bound to: name, email and permissions, which are personal data. This is the one path the reviewed documentation shows verbatim (GET /api/4.0/user).\n\nThe host is a reserved placeholder: substitute the customer's instance host before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "looker.dashboards.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "looker-instance.invalid"
      ],
      "endpoint": "https://looker-instance.invalid/api/4.0/dashboards",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-looker-access-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "looker-instance",
      "description": "List dashboards as a collection read. Dashboard titles and folder structure are internal content metadata. Path verified against the retrieved Looker 4.0 endpoint reference (cloud.google.com/looker/docs/reference/looker-api/latest/methods/); the instance's own API Explorer remains a useful deployment check.",
      "requires_review": true
    },
    {
      "capability_id": "looker.looks.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "looker-instance.invalid"
      ],
      "endpoint": "https://looker-instance.invalid/api/4.0/looks",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-looker-access-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "looker-instance",
      "description": "List Looks as a collection read. Path verified against the retrieved Looker 4.0 endpoint reference (cloud.google.com/looker/docs/reference/looker-api/latest/methods/); the instance's own API Explorer remains a useful deployment check.",
      "requires_review": true
    },
    {
      "capability_id": "looker.query.run",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "looker-instance.invalid"
      ],
      "endpoint": "https://looker-instance.invalid/api/4.0/queries/run/RESULT_FORMAT",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-looker-access-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "looker-instance",
      "description": "Run an inline query and return results. The POST body is a WriteQuery selecting model, view/explore, fields, filters, sorts, and row limit; RESULT_FORMAT selects the rendering format, for example json. The capability is read at the read floor: it changes no content or configuration managed by this pack. Provider query history, caches, and any database derived-table activity are side effects outside this registration's enumeration.\n\nThe body is unrestricted, so it can query any model or explore available to the credential's user. Internal is a minimum data classification; raise it for models exposing personal or financial data. The path was verified against the Looker 4.0 reference at cloud.google.com/looker/docs/reference/looker-api/latest/methods/; confirm it in the instance's API Explorer before deployment.",
      "requires_review": true
    },
    {
      "capability_id": "looker.folder.create",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R2",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "looker-instance.invalid"
      ],
      "endpoint": "https://looker-instance.invalid/api/4.0/folders",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-looker-access-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "create"
      ],
      "resource_id": "looker-instance",
      "description": "Create one content folder. A created folder can later be deleted, so create sits at the minimum R2 tier for reversible writes. Path verified against the retrieved Looker 4.0 endpoint reference (cloud.google.com/looker/docs/reference/looker-api/latest/methods/); the instance's own API Explorer remains a useful deployment check.",
      "requires_review": true
    },
    {
      "capability_id": "looker.look.delete",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "looker-instance.invalid"
      ],
      "endpoint": "https://looker-instance.invalid/api/4.0/looks/LOOK_ID",
      "method": "DELETE",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-looker-access-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "delete"
      ],
      "resource_id": "looker-instance",
      "description": "Delete one Look. 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; even if the instance offers a trash or recovery flow, this pack makes no general-reversibility claim and policy can raise the tier above the minimum R3 tier. LOOK_ID is a placeholder the deployment binds before enabling. Path verified against the retrieved Looker 4.0 endpoint reference (cloud.google.com/looker/docs/reference/looker-api/latest/methods/); the instance's own API Explorer remains a useful deployment check.",
      "requires_review": true
    },
    {
      "capability_id": "looker.dashboard.delete",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "looker-instance.invalid"
      ],
      "endpoint": "https://looker-instance.invalid/api/4.0/dashboards/DASHBOARD_ID",
      "method": "DELETE",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-looker-access-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "delete"
      ],
      "resource_id": "looker-instance",
      "description": "Delete one dashboard, including its tiles: a extensive content removal registered as 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. DASHBOARD_ID is a placeholder the deployment binds before enabling. Path verified against the retrieved Looker 4.0 endpoint reference (cloud.google.com/looker/docs/reference/looker-api/latest/methods/); the instance's own API Explorer remains a useful deployment check.",
      "requires_review": true
    }
  ],
  "resources": [
    {
      "resource_id": "looker-instance",
      "kind": "api",
      "locator": "https://looker-instance.invalid/api/4.0",
      "data_class": "pii",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    }
  ],
  "review": {
    "state": "library",
    "date": "2026-09-23",
    "sources": [
      "https://cloud.google.com/looker/docs/api-auth (retrieved 2026-09-21)",
      "https://cloud.google.com/looker/docs/api-overview (retrieved 2026-09-21)",
      "https://cloud.google.com/looker/docs/reference/looker-api/latest/methods/Query/run_inline_query (retrieved 2026-09-21)",
      "https://cloud.google.com/looker/docs/reference/looker-api/latest/methods/Folder/create_folder (retrieved 2026-09-21)",
      "https://cloud.google.com/looker/docs/reference/looker-api/latest/methods/Dashboard/all_dashboards (retrieved 2026-09-21)",
      "https://cloud.google.com/looker/docs/reference/looker-api/latest/methods/Dashboard/delete_dashboard (retrieved 2026-09-21)",
      "https://cloud.google.com/looker/docs/reference/looker-api/latest/methods/Look/all_looks (retrieved 2026-09-21)",
      "https://cloud.google.com/looker/docs/reference/looker-api/latest/methods/Look/delete_look (retrieved 2026-09-21)",
      "https://cloud.google.com/looker/docs/reference/looker-api/latest/methods/ApiAuth/login (retrieved 2026-09-21)",
      "https://cloud.google.com/looker/docs/reference/looker-api/latest/methods/ApiAuth/logout (retrieved 2026-09-21)"
    ],
    "reviewer_id": "aaes-eng"
  }
}
