Collaboration spaces Module

Vaks PM · Integration guide · Microsoft Teams · Slack · Google Chat · July 2026

Three providers under the same roof. Admin → Integrations → Collaboration spaces offers Microsoft Teams, Slack and Google Chat as self-service providers — same trigger (a project becoming active), same idempotence, same per-project panel, each configured independently in its own card. This guide covers all three end to end, plus the inbound path for spaces an external tool manages itself. Teams needs the most (identity prerequisites, SharePoint); Slack is the lightest.
What you will end up with (Microsoft Teams). A Microsoft Teams team created automatically when a project becomes active, its channels set up, and its membership kept in step with the project. Optionally, the team's SharePoint documents become readable context for AI agents and project members alike.
Set up single sign-on or SCIM first — this depends on it. Creating a team requires an owner, and Vaks PM can only designate one from a member whose account is linked to an Entra identity. That link is populated by SSO just-in-time creation or by SCIM. If no project member has an Entra identity, provisioning fails outright, with local accounts silently skipped. Get OIDC or SCIM working before you rely on this — and make sure the external identifier maps to the Entra object ID.

What collaboration-space provisioning does — and what it does not

How Collaboration spaces works

Two independent paths lead to the same place. Outbound: Vaks itself creates the space through whichever provider you configured (Teams, Slack, Google Chat). Inbound: a tool that manages its own space (a Slack channel your team already owns, a Teams team provisioned outside Vaks, a Google space set up by another system) declares the link back. Both converge on the same record and the same panel on the project.

Project becomes ACTIVE server-side transition, either path can follow Project closes/archives Team archived (Teams only) OUTBOUND · VAKS CREATES THE SPACE Microsoft Teams Slack Google Chat Create the space + invite members matched by project members' email INBOUND · AN EXTERNAL TOOL DECLARES IT External tool creates its own space Slack channel · Google space · Teams outside Vaks PUT …/collab-spaces/:provider org key scoped collab:link, nothing more ProjectCollabSpace row created provider · url · source: provisioned or external Shown in the project's panel
Outbound (Vaks provisions) and inbound (an external tool declares) both end up as the same ProjectCollabSpace record, shown in the project's Collaboration spaces panel regardless of which path created it.

Providers are not identical in what they support: archive on close today only applies to Microsoft Teams (Slack channels and Google Chat spaces are created but not automatically archived when a project closes); public/private visibility is a Teams and Slack concept that Google Chat spaces don't have. An externally declared space is never touched by these lifecycle rules — only Vaks-provisioned ones are.

Prerequisites

Microsoft Teams is the identity-heaviest of the three — the table below is specific to it. Slack and Google Chat are lighter (no Entra/SSO dependency at all) and each has its own short prerequisites list in its setup section further down.

SideWhat you need
IdentitySSO or SCIM live, mapping the external identifier to the Entra object ID, so project members carry an Entra identity. This is the hard prerequisite — see the warning above.
Microsoft EntraA dedicated app registration — separate from your SSO app — with Graph application permissions and admin consent. Only a tenant administrator can grant those.
Vaks PMorg:manage to configure it. A licence that includes Teams provisioning.

Step 1 — The Entra app registration

Create a new app registration in Microsoft Entra admin center → Identity → Applications → App registrations → New registration, single tenant, no redirect URI. It authenticates as itself with the client-credentials flow — no user is involved.

Under API permissions → Add a permission → Microsoft Graph → Application permissions, add:

Team.Create
TeamMember.ReadWrite.All
Channel.Create
TeamSettings.ReadWrite.All

Optionally add User.Read.All to help resolve identities. Then press Grant admin consent and confirm the status column reads Granted for each — application permissions do nothing until consented.

Collect the Directory (tenant) ID and Application (client) ID from Overview, and create a client secret under Certificates & secrets, copying its value at once.

The SharePoint-for-agents add-on needs one or two more permissions. If you plan to enable it (step 4), add Sites.Read.All to this same app for the simple model, or use the two-app model described there. You can add it now or come back later.
Secretless alternative. Instead of a client secret, this application can authenticate with a certificate (private_key_jwt) — see the detailed procedure in the SSO OIDC guide, which applies identically here (generate the key/certificate pair, upload the .cer under Certificates & secrets → Certificates, copy the thumbprint).

Step 2 — In Vaks PM

Open Admin → Integrations → Collaboration spaces, choose Add a provider → Microsoft Teams, and configure it:

FieldWhat to put
Entra tenant IDThe tenant GUID from step 1.
Client ID (app registration)The application GUID.
Client secret or certificateThe secret value, or the thumbprint + private key if using certificate authentication (see above). Both are encrypted at rest, never shown again — leave blank when editing to keep what is stored. A private key on file takes precedence over the secret.
Team name patternHow teams are named, e.g. [PROJECT] - {{project.name}}. Variables: {{project.name}}, {{project.key}}, {{client.name}}, {{project.id}}.
Channels to createA comma-separated list, e.g. Planning,Dev,Client. General is created by Teams automatically.
Team visibilityPrivate or public. It cannot be changed after the team is created.
Who to inviteWhether membership follows the project's directly assigned members, the members of teams assigned to the project, or both.
Remove on leaving the projectOff by default (additive). On, someone removed from the project is removed from the team — never the owner.
Archive on closeOn by default. Archives the team when the project completes or is archived.
EnabledThe switch that lets a space be created on project activation.

Step 3 — Test, then provision

Press Test on the provider card to confirm the credentials are accepted by Graph.

The test is deliberately shallow. It confirms the token is accepted, but does not verify the Teams permissions — a missing Team.Create passes the test and only fails at the first real provisioning. So after a green test, provision one project and watch it succeed before trusting the setup.

From here provisioning is automatic: activate a standard project and its team is created. Each project's Collaboration spaces panel also has manual buttons — Provision now, Re-sync, Archive — and shows the last few provisioning log entries with counts of members added and skipped.

Team creation is asynchronous. Microsoft creates the team in the background, so channels and members appear a moment later, and an immediate membership add can briefly fail while Entra propagates — the system retries. A member with no Entra identity is skipped and counted, not an error.

Step 4 — SharePoint documents for agents and members (optional)

Each provisioned team has a SharePoint site behind it. This add-on lets AI agents working on the project — and project members who have access to the team — read those documents as live context: nothing is copied, and it is read-only. Members browse them under the SharePoint tab of the project's documents area; agents reach them through their working context.

It requires additional Graph permission on the app, in one of two models:

Then enable it in two places — org-wide under Integrations → Collaboration spaces (on the SharePoint card: Enable SharePoint context, plus the identity model and, for the selected model, the reader app's client ID and secret), and per project (SharePoint document context, which also requires the project to have agents enabled).

Access is verified, not assumed. When a team is provisioned, Vaks PM resolves its site and drive and records whether the grant is in place. If it is not, the project's Collaboration spaces panel has a Verify access button to re-check. Agents and members can only read once the grant shows as granted and the drive is resolved.

Step 1 — The Slack app

Slack has no identity prerequisite — it needs a bot token and nothing else. Go to api.slack.com/appsCreate New AppFrom scratch, name it (e.g. Vaks PM) and pick the workspace it should live in.

Under OAuth & Permissions → Scopes → Bot Token Scopes, add:

channels:manage
groups:write
users:read.email

channels:manage creates and manages public channels, groups:write does the same for private ones, and users:read.email is how a project member's email is resolved to a Slack account for invites. Add whichever of the first two match the channel visibility you plan to use — both if you are not sure yet.

Scroll up to OAuth Tokens and press Install to Workspace (a workspace admin must approve if you are not one). Copy the Bot User OAuth Token — it starts with xoxb-.

Added a scope after installing? Slack does not apply new scopes retroactively. Reinstall the app from the same OAuth & Permissions page and the token stays the same, but only takes the new scope into account from that point on.

Step 2 — In Vaks PM

Open Admin → Integrations → Collaboration spaces, choose Add a provider → Slack, and configure it:

FieldWhat to put
Bot tokenThe xoxb-… value from step 1. Encrypted at rest, never shown again — leave blank when editing to keep the stored token.
Channel name prefixPrepended to the slugified project name, e.g. proj-#proj-website-revamp.
Channel visibilityPrivate or public. Needs the matching scope from step 1.
Invite membersOn by default. Invites project members to the channel, matched by email — best-effort, members without a matching Slack account are silently skipped.
EnabledThe switch that lets a channel be created on project activation.

Press Test to confirm the token is accepted, then activate a standard project to see its channel created — or, for a project that is already active, use the Provision now tool right on the Slack card (paste the project id) instead of cycling its status.

Step 1 — Google Cloud & Workspace

Google Chat is the heaviest of the three to set up, because the Chat API has no app-only mode: creating a space and adding members always requires a real Workspace user, so a service account has to impersonate one via domain-wide delegation. Expect a Google Cloud admin and a Workspace admin to be involved.

  1. Enable the API. In a Google Cloud project (create one if needed), go to APIs & Services → Library, search Google Chat API, and enable it.
  2. Configure the Chat app — do not skip this. Enabling the API is not enough: open the Configuration tab of the Google Chat API (console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat) and fill in an app name, an avatar URL (any https image) and a description. Since Vaks only makes REST calls (never an interactive bot), leave interactive features off (otherwise Google demands a connection URL you don't have). Make the app visible to your domain. Without this, every Chat call returns "PERMISSION_DENIED — the resource doesn't exist", even with perfect delegation — it's the most common trap.
  3. Create a service account. IAM & Admin → Service Accounts → Create Service Account. Note its email (…@….iam.gserviceaccount.com). Two authentication modes follow: service account key (download a JSON key via Keys → Add Key → JSON) or Workload Identity, keyless (nothing to download — see the dedicated section next, recommended if your organization blocks service account key creation).
  4. Turn on domain-wide delegation. Still on the service account's page, check Enable Google Workspace Domain-wide Delegation and note the Client ID (a long number, not the email).
  5. Authorize the scopes, in the Workspace admin console. In admin.google.com (a Workspace super admin is required here) → Security → API controls → Domain-wide delegation → Add new: paste the numeric Client ID, and these scopes:
    https://www.googleapis.com/auth/chat.spaces,https://www.googleapis.com/auth/chat.memberships
  6. Pick a user to impersonate. Any real, licensed Workspace mailbox works — it is the identity the service account borrows to act as a human. An operations or automation account is a common choice; it just cannot be the service account itself.
Delegation can take a while to propagate. Usually it is near-instant; Google's own documentation allows for up to 24 hours in rare cases. If the test below fails right after granting the scopes with an authorization error, wait a few minutes and retry before assuming the configuration is wrong.

Keyless — Workload Identity Federation (common to all Google integrations)

Many security-conscious organizations enforce the Google org policy iam.disableServiceAccountKeyCreation, which forbids downloading a service account key. There, the key method above is a wall. Vaks PM then offers a second authentication mode, Workload Identity Federation (WIF), which needs no downloaded key — the path a serious Google customer will accept.

How it works: Vaks signs an OIDC token with its own key (Google only knows its public half, pasted into the pool config), Google exchanges it for a federated token, then signs the domain-wide delegation assertion server-side. The key Vaks holds grants no access on its own: access is governed by the WIF config and an IAM grant, both revocable by you at any time.

API prerequisite. WIF uses signJwt: also enable the IAM Service Account Credentials API (iamcredentials.googleapis.com) in the project, in addition to the API of the integration concerned (Chat or Drive). It's a common miss — the test fails until it's on.
The pool is created once; the provider, once per integration. Vaks emits a different issuer for each Google integration, and a Google OIDC provider can be bound to one issuer only. So you create the pool once for the whole organization (--workload-identity-pool=vaks stays the same everywhere), then one provider per integration you run in WIF — each with its own issuer and JWKS.
IntegrationIssuer emitted by VaksSuggested provider nameWhere to generate the key
Google Chat…/federation/gchatvaks-gchatGoogle Chat card (Collaboration spaces)
Google Drive — shared drive provisioning…/federation/gdrive-provisioningvaks-gdrive-provGoogle Drive card (Collaboration spaces)
Google Drive — document reading…/federation/gdrivevaks-gdrive-readGoogle Drive card (Document sources)

This separation is deliberate: each integration has its own key, so you can rotate or revoke it without breaking the others. If you only run one integration in WIF, you obviously only have one provider to create.

Once per organization

  1. Target the project (the ID, as text — not the number):
    gcloud config set project PROJECT_ID
  2. Create the identity pool — once only, reused by every integration:
    gcloud iam workload-identity-pools create vaks --location=global --display-name="Vaks PM"

Once per integration

Repeat this block for each row of the table above that you enable. Every command on this page uses the same placeholders:

PlaceholderWhat to put in its place
PROJECT_IDThe Google Cloud project's text ID (e.g. my-prod-project).
PROJECT_NUMBERThe project number (digits only, e.g. 3110105436) — shown on the Cloud console home page. It is not the PROJECT_ID: the two commands below do not expect the same thing.
SA_EMAILThe service account email (…@….iam.gserviceaccount.com).
PROVIDER_NAMEThe provider name for this integration (the "Suggested provider name" column above).
<ISSUER>The issuer shown on the card after generating the key — copy-paste it, never retype it.
  1. Keyless service account. The service account from Step 1 works — just don't create any JSON key. Domain-wide delegation (Client ID + scopes in Workspace admin) stays the one for the integration concerned.
  2. Generate the signing key in Vaks. In that integration's card, switch Authentication to Workload Identity (keyless), then Generate signing key. Vaks shows the issuer, the kid and a public JWKS — the private key stays encrypted in the database, never exposed.

    Create the file in Cloud Shell. Between the two EOF markers, paste the exact contents of the card's "Public JWKS" field — the whole {"keys":[ … ]} block, verbatim, unchanged. This is not something to invent or copy from an example: it is your public key, the one carrying the kid shown on the card. (Quoting the first EOF stops the shell interpreting the content.)

    cat > jwks-PROVIDER_NAME.json <<'EOF'
    [[ PASTE HERE THE PUBLIC JWKS SHOWN ON THE CARD ]]
    EOF

    A JWKS holds public key material only — there is nothing secret to protect in this file. The kid it carries must be the one shown on the card: that is what lets you verify later that Google holds the right version.

    Prefer an editor? cloudshell edit jwks-PROVIDER_NAME.json. You can check the file is valid JSON with python3 -m json.tool jwks-PROVIDER_NAME.json.

  3. Create the OIDC provider (inline JWKS, flag --jwk-json-path — not --jwks-json-file in recent gcloud):
    gcloud iam workload-identity-pools providers create-oidc PROVIDER_NAME \
      --location=global --workload-identity-pool=vaks \
      --issuer-uri="<ISSUER>" --jwk-json-path=jwks-PROVIDER_NAME.json \
      --attribute-mapping="google.subject=assertion.sub"
  4. Let the pool impersonate the service account. This binding lives at the pool level, not the provider level: it identifies the service account by its subject, regardless of which provider the token came through. So if you reuse the same service account across integrations, one binding is enough — there is nothing to replay. Only redo it if the integration uses a different service account.
    gcloud iam service-accounts add-iam-policy-binding SA_EMAIL \
      --role=roles/iam.serviceAccountTokenCreator \
      --member="principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/vaks/subject/SA_EMAIL"

    Note: SA_EMAIL appears in two places — the target service account (right after add-iam-policy-binding) and the member's subject/. Replace both with the same address.

  5. Fill in the audience in the card: the full resource name of the provider you just created — it ends with PROVIDER_NAME, not with the pool name.
    //iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/vaks/providers/PROVIDER_NAME
If you regenerate the key, you MUST re-upload the JWKS. Every generation produces a new kid. As long as the JWKS held by Google carries the old one, Google cannot find the token's key and falls back to OIDC discovery — hence the misleading Parsing error for OIDC discovery document, which looks like a URL problem when it is really a stale key. Copy the JWKS shown on the card, then:
gcloud iam workload-identity-pools providers update-oidc PROVIDER_NAME \
  --location=global --workload-identity-pool=vaks \
  --jwk-json-path=jwks-PROVIDER_NAME.json
Then check the stored kid matches the card's:
gcloud iam workload-identity-pools providers describe PROVIDER_NAME \
  --location=global --workload-identity-pool=vaks --format="value(oidc.jwksJson)"
Reuse the same service account across integrations? Yes — that's the simplest case, and then there is no IAM binding and no account to recreate. Only one thing to adjust: the domain-wide delegation must list the union of the scopes. In admin.google.comSecurity → API controls → Domain-wide delegation, edit the existing row for your Client ID (don't add a second one) and put every needed scope in it, e.g. for Chat + Drive:
https://www.googleapis.com/auth/chat.spaces,https://www.googleapis.com/auth/chat.memberships,https://www.googleapis.com/auth/drive
A dedicated service account per integration remains cleaner for least privilege (each identity carries only its own scopes) — but it then needs its own IAM binding.
The most common mistake when adding a second integration. Reusing the first one's provider (and therefore its audience): the STS exchange fails, because the iss of the emitted token doesn't match the issuer declared on that provider. One provider per issuer — always. gcloud is explicit about it: ALREADY_EXISTS: Requested entity already exists if you try to recreate a provider with the same name.
Air-gap. The JWKS is pasted into the provider config (the --jwk-json-path option): Google never calls back to your server to fetch it. WIF therefore works even in an isolated deployment.

Step 2 — In Vaks PM

Open Admin → Integrations → Collaboration spaces, choose Add a provider → Google Chat, and configure it. First pick the Authentication mode: Service account key (the private-key field appears) or Workload Identity (keyless) (the audience field and the key-generation panel appear instead):

FieldWhat to put
Service account emailThe …@….iam.gserviceaccount.com address from step 1.
Service account private keyPaste the whole downloaded JSON file, its raw private_key value, or an already-clean PEM — all three are accepted and normalized. Encrypted at rest, never shown again — leave blank when editing to keep the stored key.
Impersonated Workspace userThe email from step 1's last point.
Space name prefixPrepended to the project name, e.g. proj-proj-Website revamp. Unlike Slack, Google Chat space names are not slugified.
Invite membersOn by default. Invites project members to the space, matched by email — best-effort, members without a Chat account are silently skipped.
EnabledThe switch that lets a space be created on project activation.

Press Test — it exchanges the service-account assertion for a token and does a small, read-only Chat API call, confirming the whole delegation chain works before anything is created. Then activate a standard project to see its space appear.

Delete a space, or unlink

Two actions live on each tile of a project's Collaboration spaces section, and they must not be confused:

ActionEffectAvailable on
ArchiveA real, reversible remote action: the team/channel becomes read-only, content is kept.Microsoft Teams, Slack
Delete spaceDestructive and permanent on Google's side. Chat: the space and its messages are gone (spaces.delete) — requires adding the chat.delete scope to the domain-wide delegation. Shared drive: the drive and the files it holds are deleted. Neither has an archive — this is the only remote cleanup.Google Chat, Google Drive
UnlinkLocal-only cleanup: removes the Vaks link with no remote call. The remote space keeps running. Use it when the space/team/drive was already deleted on the provider side and Archive/Re-sync fail with 404. The project goes back to "Provision now".All four providers
Where do you grant the chat.delete scope? In the same place as the two initial Chat scopes: admin.google.comSecurity → API controls → Domain-wide delegation. Edit the existing row for your Client ID (don't create a new entry) and replace the scope list with all three:
https://www.googleapis.com/auth/chat.spaces,https://www.googleapis.com/auth/chat.memberships,https://www.googleapis.com/auth/chat.delete
Workload Identity needs no other change for deletion — it's a delegation scope, not an IAM role. Until chat.delete is in the list, only deletion fails; creation keeps working.

Step 1 — Google Cloud & Workspace

Google Drive provisioning creates one shared drive per project — the Google equivalent of the SharePoint site that comes with a Teams team. As with Google Chat, a service account alone isn't enough: creating a shared drive requires a real Workspace user, so domain-wide impersonation is mandatory, whichever authentication mode you pick.

Two identities, deliberately separate. This provisioning writes (the drive scope), whereas reading documents stays on drive.readonly. You can use the same service account for both, or dedicate one to each role — recommended if you want a hard guarantee that the read path cannot modify anything. For the read side, see Document sources.
  1. Enable the API. In your Google Cloud project, APIs & Services → LibraryGoogle Drive API → enable.
  2. Create a service account. IAM & Admin → Service Accounts. Note its email (…@….iam.gserviceaccount.com). Two authentication modes follow: service account key (Keys → Add Key → JSON) or Workload Identity, keyless.
  3. Enable domain-wide delegation. On the service account page, tick Enable Google Workspace Domain-wide Delegation and note the Client ID (the long number, not the email).
  4. Authorise the scope, in the Workspace admin console. admin.google.comSecurity → API controls → Domain-wide delegation → Add new: paste the numeric Client ID, and this scope:
    https://www.googleapis.com/auth/drive
    This scope covers creating the drive and managing its members. It subsumes reading: a service account dedicated solely to reading documents only needs drive.readonly.
  5. Pick a user to impersonate. Any real, licensed Workspace mailbox that is allowed to create shared drives. An operations account is a good fit.
The Workspace setting that blocks everything. If shared drive creation is turned off for the impersonated user's organisational unit (Admin → Apps → Google Workspace → Drive and Docs → Sharing settings), creation fails no matter how good the delegation is. Check this first if the test passes but provisioning fails.

Keyless mode (Workload Identity) for this integration

The principle and the pool are common to every Google integration — see Keyless — Workload Identity Federation for creating the pool (do that once). What follows is the block specific to Drive provisioning: it needs its own provider, because its issuer differs from Google Chat's.

  1. In the Google Drive card (Step 2 below), switch Authentication to Workload Identity (keyless), then Generate signing key. The issuer shown ends with /federation/gdrive-provisioning; copy the JWKS into jwks-gdrive-prov.json.
  2. Create the dedicated provider, inside the existing vaks pool:
    gcloud iam workload-identity-pools providers create-oidc vaks-gdrive-prov \
      --location=global --workload-identity-pool=vaks \
      --issuer-uri="https://YOUR-TENANT/federation/gdrive-provisioning" \
      --jwk-json-path=jwks-gdrive-prov.json \
      --attribute-mapping="google.subject=assertion.sub"
  3. The impersonation grant lives on the pool: if the service account is already bound (for Google Chat, say), there is nothing to redo. Otherwise replay the add-iam-policy-binding from the shared section.
  4. Paste this audience into the card's WIF pool audience field:
    //iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/vaks/providers/vaks-gdrive-prov

Step 2 — In Vaks PM

Open Admin → Integrations → Collaboration spaces, then Add a provider → Google Drive. Pick the Authentication mode first: the form adapts (private key, or pool audience + key generation panel).

FieldWhat to put in
Service account emailThe …@….iam.gserviceaccount.com address from step 1.
Service account private key (key mode)The whole JSON file, just its private_key value, or a clean PEM — all three forms are accepted. Encrypted at rest, never shown again; leave blank when editing to keep the stored key.
WIF pool audience (keyless mode)The full resource name of the WIF provider, as //iam.googleapis.com/projects/…/providers/….
Impersonated Workspace userThe email from the last point of step 1. Required in both modes.
Drive name prefixPrepended to the project name, e.g. Project Project Website revamp.
Role for added membersViewer (default, least privilege — enough for document context) or Contributor if the team needs to drop files in.
Add project membersOn by default. Adds project members and members of assigned teams, matched by email — best-effort, failures are logged without blocking creation.
EnabledThe switch that lets a drive be created when a project goes active.

Click Test — the assertion is exchanged for a token and a small read-only Drive call is made, which validates the whole delegation chain before anything is created. Then activate a standard project to watch its drive appear. Re-provisioning a project that already has one creates nothing: it only re-syncs the members.

What this changes for document reading. As soon as a project has its shared drive, the Google Drive document source reads that drive for that project, instead of the organisation-wide root folder. The connector's root folder then only acts as a fallback, for projects without a dedicated drive — exactly the SharePoint behaviour, where each project reads its own team's drive.

Membership sync

Membership is kept current in three ways: when project members or managers change, when the composition of an assigned team changes, and by a safety-net job that periodically reconciles every provisioned active project. Sync is add-only unless Remove on leaving is on, and it never removes the owner. Only active members with an Entra identity are added; everyone else is skipped and counted in the log.

Externally declared spaces

Everything above is Vaks creating the space (Microsoft Teams). The other direction exists too: a tool that creates the space itself — a Slack channel, a Google space, or a Teams team managed outside Vaks — declares the link back so it shows on the project. This path needs no Teams provisioning license.

It relies on an organization key restricted to the collab:link scope alone — deliberately narrow: the key can read and change nothing else on the project. Create it from Admin → Integrations → Collaboration spaces: add the Declared spaces API provider, which mints a key scoped to collab:link (the secret is shown only once).

Method & routePurpose
PUT /api/v1/projects/{projectId}/collab-spaces/{provider}Declare or update a space. Idempotent on (project, provider). Refuses (409) to overwrite a Vaks-provisioned space.
GET /api/v1/projects/{projectId}/collab-spacesList the spaces linked to a project.
DELETE /api/v1/projects/{projectId}/collab-spaces/{provider}Remove a declared link (external links only).

{provider} is a free-form label (slack, google, teams…). The PUT body:

{
  "url": "https://…",         // required, https only, ≤ 2048 chars
  "externalId": "C0123ABCD",  // optional — the space's id in the tool
  "name": "Acme project"      // optional — display label
}
When to declare? Subscribe a webhook to the project.activated event: your tool then knows when to create the space, and declares it back with the PUT above.

Troubleshooting

Provisioning is deliberately silent — it never breaks a project update — so the project's Collaboration spaces panel and its log are where you look. Server messages come back in French regardless of interface language, reproduced verbatim below.

SymptomCause & fix
Provisioning fails, every member skippedNo member has an Entra identity, so no owner can be designated. SSO or SCIM must be live and mapping to the Entra object ID. This is the number-one cause.
Test is green but the first provision failsThe test does not check Teams permissions. Confirm all four Graph permissions are added and consented.
Le provisioning Microsoft Teams requiert une licence active.The licence does not include Teams provisioning. Import one under Admin → Organization → License.
Configuration Graph incomplète (tenantId / clientId).A required field is empty. Complete the provider card.
Échec d'obtention du token Graph: …Tenant ID, client ID or secret is wrong, or the secret expired. The trailing text is Microsoft's own reason.
Provisioning indisponible sur un projet externeExpected — external-ticket containers never get a team.
Members briefly fail to add after creationEntra propagation delay after asynchronous team creation. The system retries; it resolves on its own.
Agents cannot read SharePointCheck, in order: the org toggle, the project toggle, the project has agents enabled, the team is provisioned, and the grant shows as granted (use Verify access).
invalid_auth / not_authed (Slack)The bot token is wrong, revoked, or the app was uninstalled from the workspace. Reinstall and paste the new token.
missing_scope (Slack)A required Bot Token Scope is missing. Add it under OAuth & Permissions and reinstall the app — adding the scope alone is not enough.
name_taken (Slack)A channel with that exact name already exists. Vaks PM does not guess its ID reliably, so this surfaces as a provisioning failure rather than silently adopting the existing channel.
Test fails with an authorization/consent error (Google Chat)Domain-wide delegation is missing, has the wrong scopes, or has not propagated yet (rarely up to 24h). Double-check the Client ID and the two scopes in the Workspace admin console, then retry.
Clé privée invalide (Google Chat)The pasted value is not recognized as a PKCS#8 private key. Vaks PM accepts the whole downloaded JSON, the raw private_key field, or a clean PEM — but it must ultimately contain a -----BEGIN PRIVATE KEY----- block.
PERMISSION_DENIED / "the resource doesn't exist" (Google Chat)In order of frequency: (1) the Chat app is not configured — enabling the API isn't enough, you must fill the Configuration tab (name/avatar/description); (2) the Google Chat service is off for the org; (3) the impersonated user is not a real, active, licensed Workspace mailbox; (4) delegation hasn't propagated yet (retry after a few minutes).
Parsing error for OIDC discovery document: Unexpected end of stream : expected '{' (WIF)The provider has no JWKS attached. With an inline JWKS, Google never fetches a discovery document: seeing this error means it is trying to read <issuer>/.well-known/openid-configuration — a route Vaks does not serve, hence the non-JSON reply. Typical causes: the provider was created without --jwk-json-path, or jwks-<provider>.json was empty/missing when create-oidc ran. Check with providers describe … --format="yaml(oidc)" that jwksJson is populated, then fix it with providers update-oidc … --jwk-json-path=jwks-<provider>.json.
Google STS: federated token missing (WIF)The pool audience doesn't match, or the provider's attribute mapping is wrong. Check the Workload Identity pool audience (the exact //iam.googleapis.com/…/providers/…) and the declared issuer.
Google signJwt: signedJwt missing (WIF)The roles/iam.serviceAccountTokenCreator grant is missing on the service account for the pool principal, or the IAM Service Account Credentials API isn't enabled.
Space deletion refused (Google Chat)The chat.delete scope is not in the domain-wide delegation. Add it to the authorized scopes in Workspace admin — creation doesn't need it, only deletion.

Related: document sources, the full guide to SharePoint and Google Drive as read context · SCIM and OIDC, prerequisites for the Entra identity link · AI connector for the agents that read SharePoint context · all integrations.