- No downloadable secret. Nothing exists that can leak, so nothing needs rotating.
- Least privilege. Read and run roles only, never admin.
- Revoke with one IAM change. Remove one binding and Bluejay cannot get new credentials.
What you need
- Access to the Google Cloud project that holds your agents, as Owner (or Workload Identity Pool Admin + Service Account Admin + Project IAM Admin).
- The project’s project id (for example
acme-voice-prod). The script looks up the project number for you. - Your Bluejay tenant id. It is shown in Bluejay under Settings > Integrations > Google Agents, at the top of the keyless section, with a copy button.
Set it up
1
Copy your Bluejay tenant id
In Bluejay, click Settings in the bottom left, then Integrations, then the Google Agents tab. Copy the value under Your Bluejay tenant id. Keep this tab open, you will come back to it.
2
Open Cloud Shell
Open Cloud Shell with the Google account that has access to your project. Nothing to install: Cloud Shell already has
gcloud and is signed in as you.3
Paste and run the setup script
Paste this whole block into Cloud Shell. It only writes a file called Then run it with the tenant id from step 1 and your project id. Add It creates everything in a few seconds, then waits until Google is actually enforcing the new roles (usually 1 to 3 minutes; it prints dots while it waits), and ends by printing four values. That wait covers the roles only. The token exchange itself is tested by Test connection in the next step.
bluejay-wif.sh, it does not run anything yet, so you can open the file and read it first.--ces if you also run Conversational Engine (CES) agents.4
Paste the four values into Bluejay
Back in the Google Agents tab, paste the four printed values into Service account email, Workload identity provider, Project ID, and Project number. Click Save, then Test connection.Connected means Bluejay minted a token, Google accepted the exchange, the impersonation worked, and a read call (listing your Dialogflow CX agents) came back. That one check covers the tenant pin, the audience, and the roles at once, and it is the first time the token exchange is exercised. The script already waited out the slowest part, role propagation, so the usual reason for a red first click is gone.
5
Sync your agents
Click Sync Agents from Google. Your Dialogflow CX agents (and CES agents, if you passed
--ces) appear in Bluejay. From here, follow the Dialogflow CX or CES page to run simulations.What the script created
Everything lives in your project. Bluejay owns nothing here.
Bluejay does not ask for
roles/dialogflow.admin. Admin could create, modify, and delete agents and flows, and none of that is needed to test or observe them.
Bluejay stores four pieces of non-secret metadata for your integration: the provider path, the service account email, and the project id and number. It holds no private key, no key file, and no long-lived credential for your project. If a setup flow ever asks you for one, that flow is wrong.
Prefer Terraform?
Prefer Terraform?
Same result, as code. Copy these three files into your infrastructure repository and apply them against your own state and review process. Bluejay never runs Terraform for you.Then, with the tenant id from step 1:The plan should create one pool, one provider, one service account, one
workloadIdentityUser binding, the Service Usage Consumer binding, and one project binding per role. Nothing else, and no keys. Paste the three outputs plus your project id into Bluejay exactly as in step 4 above. Enabling storage or bigquery grants project-wide read; prefer resource-level bindings on the specific bucket or dataset Bluejay needs.Security model
The tenant condition is the boundary. Bluejay’s issuer signs tokens for every one of its customers. The provider in your project only accepts tokens whosetenant_id claim equals your tenant id and whose environment is production, and Google evaluates that before issuing any credential. Another Bluejay tenant presenting a perfectly valid Bluejay token cannot exchange it against your provider.
Everything is in your project. Your pool, your provider, your service account, your bindings. There is no shared Bluejay-owned IAM surface across customers, so a mistake in someone else’s project cannot reach yours.
The audience is the provider itself. Tokens carry aud = //iam.googleapis.com/projects/<number>/locations/global/workloadIdentityPools/bluejay/providers/bluejay, and the provider only allows that audience, so a token minted for one provider is useless against another.
Tokens are short-lived (minutes) and minted on demand.
Audit it yourself. Turn on IAM and STS Data Access audit logs in your project. Every exchange and impersonation is then recorded on your side, attributed to the federated principal, independently of anything Bluejay reports.
Revoking access
Remove the impersonation binding. Bluejay can still mint tokens, but nothing in your project will accept them:gcloud iam workload-identity-pools delete bluejay --location=global), or terraform destroy if you used the module. To restore access later, re-run the setup script.
Troubleshooting
Test connection distinguishes two different failures.Result says unreachable
Result says unreachable
Google refused the token exchange before any API call, so this is a trust problem, not a permissions problem. In order:
- Tenant id mismatch. The most common cause. Compare the value you passed to the script (or put in
terraform.tfvars) with Your Bluejay tenant id in the Google Agents tab. Re-run the script with the right value; it updates the provider in place. - Provider path edited by hand. It must be exactly
projects/<number>/locations/global/workloadIdentityPools/bluejay/providers/bluejay. If you copied it from the Google Cloud console rather than the script output, drop the leading//iam.googleapis.com/(orhttps://iam.googleapis.com/). - Wrong project number. The path embeds the numeric project number, not the project id. Paste the script output verbatim.
- Impersonation binding missing. Re-run the script (or
terraform apply); it is safe to repeat. - APIs not enabled.
gcloud services enable iamcredentials.googleapis.com sts.googleapis.com --project=PROJECT_ID. - Set up by hand or with Terraform a moment ago. Google applies IAM changes over a few minutes (the Cloud Shell script waits for this, Terraform does not). Click Test connection again in a minute.
Result says no permission
Result says no permission
The exchange worked and Bluejay is authenticated as the service account, so trust is fine. The service account cannot perform the read Bluejay attempted. Check:
- A role is missing. Re-run the script; it re-applies every role. Add
--cesif you run CES agents and skipped it before. USER_PROJECT_DENIEDon voice simulations. The service account needsroles/serviceusage.serviceUsageConsumer. The script grants it; if you built the setup by hand, add it. Text and chat simulations can keep working while voice fails this way, so a green Test connection does not rule it out.- The agent API is not enabled.
gcloud services enable dialogflow.googleapis.com(orces.googleapis.com)--project=PROJECT_ID. - A narrowed grant is too narrow. If you replaced a project-level storage or bigquery role with a resource-level one, confirm it covers the bucket or dataset Bluejay reads.
Alternative: service-account key
Bluejay still accepts a service-account JSON key in the Google Agents panel, as a legacy fallback for teams whose policy or tooling cannot yet accommodate federation. That setup is documented on the Dialogflow CX and CES pages.Up Next
Dialogflow CX Simulations
Sync, test, edit, version, and promote your DFCX agents.
CES Simulations
Run Voice and Chat simulations against your CES agents.