Google Cloud Monitoring
Send Cloud Monitoring alerts to a BatonDeck board. An alert that opens files an incident ticket; the same alert closing annotates that ticket rather than filing a second one.
What Cloud Monitoring can and cannot send
This is the part that trips people up, so it is first.
| Custom headers | No. Webhook channels do not support them. |
| Signing | No. Cloud Monitoring cannot compute a signature. |
| HTTP Basic auth | Yes — this is what you use. |
| Query-string token | Supported by Google, not accepted by us. |
We do not accept the query-string token because a credential in a URL ends up in access logs, proxy logs and referrer headers. Basic auth puts it in a header instead, which is why that is the only mode this source offers.
1. Create the integration
From the web app: On-call → gear → New → Google Cloud Monitoring. The browser generates the key and shows it once — copy it then, it cannot be retrieved afterwards.
Or over MCP:
create_integration {
projectId, name: "gcp-prod", source: "gcp", boardId: "B-…",
auth: { mode: "basic", apiKey: "<a long random value you generate>" },
routing: { mode: "oncall", onCallAgent: "claude-oncall" }
}The response carries endpointPath — /i/ing_…. Your full URL is the on-call function's base URL
followed by that path.
2. Create the notification channel
In Cloud Console: Monitoring → Alerting → Edit notification channels → Webhooks → Add new.
- Endpoint URL —
https://<function-host>/i/ing_… - Authentication — choose Basic auth
- Username — anything (
batondeckis fine; it is ignored) - Password — the key from step 1
Or with gcloud:
gcloud beta monitoring channels create \
--display-name="BatonDeck on-call" \
--type=webhook_basicauth \
--channel-labels=url=https://<function-host>/i/ing_… \
--user-labels=owner=batondeckThe username is ignored on purpose. The password is the whole secret, and pinning a username would add a second thing to get wrong for no security gain.
3. Attach it to an alerting policy
Any policy will do. Add the channel under Notifications, and turn on Notify on incident closure so the resolve reaches us — without it the ticket never learns the alert recovered.
gcloud alpha monitoring policies update POLICY_ID \
--add-notification-channels=projects/PROJECT/notificationChannels/CHANNEL_IDWhat lands on the ticket
| Alert field | Where it goes |
|---|---|
incident.incident_id | the correlation fingerprint |
incident.state | open → firing, closed → resolved |
incident.policy_name | ticket title — your own name for the alert (falls back to condition name, then summary) |
incident.summary | kept as an annotation; Google auto-generates it and it reads identically for every incident on a policy |
incident.url | a link on the ticket |
incident.severity | ticket priority (defaults to warning when absent) |
resource.*, metric.*, policy_user_labels | labels, prefixed resource. / metric. / user. |
threshold_value, observed_value, documentation | annotations |
Correlation is automatic. Cloud Monitoring reuses one incident_id for the open and the close,
so the close finds the ticket the open created with nothing to configure. A flapping alert appends to
the same ticket instead of filing dozens.
Checking it works
Send a test from the integration's page (Send test event) — that proves the board path but not your credential, because a test event is synthesized inside BatonDeck and skips signature verification entirely. To prove the credential, fire a real alert (a policy on a metric you can move) and watch the delivery log.
If deliveries are refused:
| Delivery log says | Cause |
|---|---|
basic_missing | the channel is not using Basic auth, or is sending nothing |
basic_malformed | the header is not Basic base64(user:pass) |
api_key_mismatch | the password does not match the key you created |
A 404 for the whole endpoint means the integration id in the URL is wrong or the integration was
disabled — the two are deliberately indistinguishable from outside.
