Skip to main content
Bluejay connects to your Google Cloud project with Workload Identity Federation (WIF). Bluejay presents a short-lived token, Google exchanges it for a credential that impersonates one dedicated service account in your project, and that service account only has the read and run roles you grant it.
  • 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.
This is the recommended setup for the Google Agents integration. Syncing agents, running simulations, and everything else on the Dialogflow CX and Conversational Engine (CES) pages works the same once it is in place.

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 bluejay-wif.sh, it does not run anything yet, so you can open the file and read it first.
Then run it with the tenant id from step 1 and your project id. Add --ces if you also run Conversational Engine (CES) agents.
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.
The tenant id must match Bluejay character for character. It is baked into the provider’s condition, so a typo produces a provider that silently rejects every exchange, and Test connection in the next step reports unreachable.
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.
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 whose tenant_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:
New credentials stop immediately. A credential Bluejay already holds keeps working until it expires, which is within an hour. To remove everything the setup created, delete the service account and the pool (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.
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/ (or https://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.
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 --ces if you run CES agents and skipped it before.
  • USER_PROJECT_DENIED on voice simulations. The service account needs roles/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 (or ces.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.
A JSON key is a long-lived credential that anyone holding the file can use, from anywhere, until it is rotated. It has no tenant pinning and no expiry by default, and revoking it means finding and deleting every copy. Use keyless access for every new integration, and migrate existing key-based ones: run the setup above, confirm Test connection is green, then delete the JSON key from the service account’s Keys tab in the Google Cloud console.

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.