{
  "schema": "aaes/capability-pack/1",
  "provider": "googleworkspace",
  "version": "1.0.0",
  "description": "Read Google Workspace users, groups, and group memberships, and send Gmail messages as a named user. Sending requires user consent or domain-wide delegation to impersonate that user.",
  "notes": "GROUP_KEY and USER_ID in the endpoint paths are placeholders: the deployment must substitute the concrete group and user each capability is bound to before enabling the pack, and the pack must not be enabled with the literals in place — the review does not rely on Google rejecting literal placeholder identifiers as the control, and allowlisting admin.googleapis.com or gmail.googleapis.com does not make an unsubstituted pack safe.\n\nThe egress allowlist hosts are real shared Google API hosts, not placeholders: admin.googleapis.com serves the Admin SDK Directory API and gmail.googleapis.com serves the Gmail API, so they stay real and the allowlist does not move with the tenant.\n\nThis pack registers only endpoints the official Google Workspace documentation publishes: Admin SDK Directory users.list, groups.list and members.list, and Gmail users.messages.send (the metadata URI, not the media-upload URI). users.list requires either the customer or domain query parameter and can retrieve deleted users via showDeleted — review which scoping the deployment binds.\n\nDocumented exclusions: this pack does not register user creation, update, patch, deletion, undelete or makeAdmin (users.insert, users.update, users.patch, users.delete, users.undelete, users.makeAdmin), user alias and photo operations, group creation, update or deletion (groups.insert, groups.update, groups.patch, groups.delete), group membership writes (members.insert, members.update, members.patch, members.delete), organizational-unit, role, role-assignment, schema, token and device operations, the Reports and Alert Center APIs, and every other Gmail operation including message delete, batchDelete, modify, batchModify, import, insert, trash and settings/delegate management.\n\nApproval classification: the three directory reads sit at the minimum R0 tier of read, with users and memberships at the pii class because names, email addresses and member identities are personal data. gmail.messages.send carries the send action at the minimum R3 tier because a sent message cannot be recalled after delivery, and send 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.\n\nThe send capability's credential contract needs an honest delegation distinction, like the microsoft365 pack's Teams handling: with a user-consent (delegated) OAuth 2.0 flow, the token acts as the signed-in user and USER_ID must be that user (or the special value 'me'); with a service account, Gmail has no application-level access at all — a service account reaches a user's mailbox only through domain-wide delegation, which a Google Workspace super administrator grants in the Admin console by authorizing the service account's numeric client ID with explicit OAuth scopes, after which the token process impersonates a named user (the JWT 'sub' claim) and acts with that user's permissions, never with elevated domain-wide privileges.\n\nThe pack-level reference is deliberately flow-neutral; the deployment chooses the flow, binds USER_ID to the impersonated or signed-in user accordingly, and consents the minimum scopes — admin.directory.user.readonly and admin.directory.group.readonly for the reads, gmail.send (the least-privileged send scope) for the send. Domain-wide delegation is tenant-grade power: a mis-scoped DWD grant lets the service account impersonate any user in the domain within those scopes, so the authorized scope list and the service account's key custody are part of this pack's review.\n\nThis package has completed the documentation review recorded in review.state: the dated method, scope and delegation sources are listed in review.sources, and promotion requires a reviewer to complete that named-source check — library readiness does not require demonstrating a particular deployment's connectivity or executor operation.",
  "credential": {
    "secret_ref_id": "secret-ref-google-workspace-access-token",
    "custody": "customer_vault",
    "inject": "header",
    "inject_name": "Authorization",
    "note": "The value stored under this reference is the complete Authorization header value (scheme included): the scheme 'Bearer', one separating space, then an OAuth 2.0 access token for Google APIs — 'Bearer <token>'.\n\nThe token is minted and renewed by an external token process, never by the executor: either a user-consent (delegated) OAuth 2.0 flow acting as one signed-in user, or a service account using the JWT-bearer grant against https://oauth2.googleapis.com/token — with domain-wide delegation and a 'sub' claim naming the impersonated user when the token must act as a user, which the Gmail send capability always requires because a service account has no mailbox access of its own.\n\nGoogle access tokens are short-lived (the token response's expires_in, typically about one hour), so renewal ownership sits with the external process, which obtains a replacement access token before expiry and stores the new complete header value; the executor injects the stored value verbatim and performs no token lifecycle itself.\n\nThe stored value is never the service-account private key, the OAuth client secret or a refresh token — the service-account JSON key stays with the external token process, and losing it is a revocation event, not a vault rotation.\n\nRevocation: distinguish grant revocation from token invalidation. A user-consent grant can be revoked by the user or restricted by the Workspace admin, and removing a domain-wide delegation grant in the Admin console's Domain-wide delegation page stops the service account from minting further impersonated tokens — both cut off future token acquisition.\n\nDisabling or deleting a service-account key likewise stops future JWT signing and future token minting, but it is not revocation of access tokens already minted with that key: Google access tokens remain valid until their own expiry (the token response's expires_in, typically about one hour), subject to Google's revocation-propagation limits.\n\nPlan any emergency revocation around that residual validity window. An expired value fails closed.\n\nLeast privilege: consent only the scopes these capabilities need — admin.directory.user.readonly, admin.directory.group.readonly (which also covers members.list), and gmail.send — and weigh before granting that gmail.send under domain-wide delegation lets the service account send as any authorized user in the domain. customer_vault is the only generally-supported production custody model here — identity_federation and customer_broker are refused at startup and inline is lab-only.\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": "googleworkspace.users.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "admin.googleapis.com"
      ],
      "endpoint": "https://admin.googleapis.com/admin/directory/v1/users",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-google-workspace-access-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "google-workspace-users",
      "description": "List the Workspace account's users: names, email addresses, phone numbers and organizational details are personal data, so the read is classified as pii (personal data). The registered path covers the collection read only — single-user reads (GET /admin/directory/v1/users/{userKey}) are not registered by this pack. The request must carry either the customer or domain query parameter, and showDeleted=true would extend the result to deleted users; which scoping the deployment binds is part of the review.",
      "requires_review": true
    },
    {
      "capability_id": "googleworkspace.groups.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "admin.googleapis.com"
      ],
      "endpoint": "https://admin.googleapis.com/admin/directory/v1/groups",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-google-workspace-access-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "google-workspace-groups",
      "description": "List the Workspace account's groups: names, email addresses and descriptions, which reveal org structure. The registered path covers the collection read only — single-group reads (GET /admin/directory/v1/groups/{groupKey}) are not registered by this pack. The request must carry either the customer or domain query parameter unless userKey is used to list one user's groups.",
      "requires_review": true
    },
    {
      "capability_id": "googleworkspace.groups.members.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "admin.googleapis.com"
      ],
      "endpoint": "https://admin.googleapis.com/admin/directory/v1/groups/GROUP_KEY/members",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-google-workspace-access-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "google-workspace-users",
      "description": "List the members of one group; the response carries member email addresses and ids, which are personal data. GROUP_KEY (the group's email address, alias or unique id) is a placeholder the deployment binds to a concrete group before enabling. The registered path covers the collection read only — single-member reads (GET.../members/{memberKey}) and the hasMember check are not registered by this pack. includeDerivedMembership=true would extend the result to indirect memberships; whether the deployment sets it is part of the review.",
      "requires_review": true
    },
    {
      "capability_id": "googleworkspace.gmail.messages.send",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "gmail.googleapis.com"
      ],
      "endpoint": "https://gmail.googleapis.com/gmail/v1/users/USER_ID/messages/send",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-google-workspace-access-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "send"
      ],
      "resource_id": "google-workspace-mailbox",
      "description": "Send a message as one user, to the recipients named in the To, Cc and Bcc headers of the supplied message. A sent message cannot be recalled after delivery, and send is one of the designated actions requiring human approval, which is why this capability carries the minimum R3 tier; recipients are personal data and the body may carry more, so it registers at the pii class.\n\nThe registered path is the metadata send URI; the media-upload URI (upload/gmail/v1/...) is not registered.\n\nUSER_ID (the user's email address, or 'me' for the authenticated user) is a placeholder the deployment binds before enabling — with a user-consent token it must be the consenting user, and with a service account it must be the impersonated user named in the JWT 'sub' claim under a domain-wide delegation grant, as the pack notes describe; the two flows are not interchangeable and the deployment's choice is part of the review.",
      "requires_review": true
    }
  ],
  "resources": [
    {
      "resource_id": "google-workspace-users",
      "kind": "api",
      "locator": "https://admin.googleapis.com/admin/directory/v1/users",
      "data_class": "pii",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    },
    {
      "resource_id": "google-workspace-groups",
      "kind": "api",
      "locator": "https://admin.googleapis.com/admin/directory/v1/groups",
      "data_class": "internal",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    },
    {
      "resource_id": "google-workspace-mailbox",
      "kind": "mailbox",
      "locator": "https://gmail.googleapis.com/gmail/v1/users/USER_ID",
      "data_class": "pii",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    }
  ],
  "review": {
    "state": "library",
    "date": "2026-09-22",
    "sources": [
      "https://developers.google.com/workspace/admin/directory/reference/rest/v1/users/list (retrieved 2026-09-21)",
      "https://developers.google.com/workspace/admin/directory/reference/rest/v1/groups/list (retrieved 2026-09-21)",
      "https://developers.google.com/workspace/admin/directory/reference/rest/v1/members/list (retrieved 2026-09-21)",
      "https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages/send (retrieved 2026-09-21)",
      "https://developers.google.com/identity/protocols/oauth2/service-account (retrieved 2026-09-21)",
      "https://developers.google.com/identity/protocols/oauth2 (retrieved 2026-09-21)",
      "https://support.google.com/a/answer/162106 (retrieved 2026-09-21)"
    ],
    "reviewer_id": "aaes-eng"
  }
}
