Guide to Gemini Webhooks: Features and Best Practices
Gemini webhooks are HTTP callbacks that the Gemini API uses to notify your application when long-running async work completes — a Batch job processing thousands of prompts finishes, an Interactions session needs human-in-the-loop input, a video generation job produces a result. They are the primary way to react to Gemini activity without polling the API.
This guide covers how Gemini webhooks work, the features Google provides, the limitations and pain points worth knowing about before you build, and the best practices that keep production integrations reliable.
How Gemini webhooks work
Gemini webhooks follow the Standard Webhooks specification, which means the same webhook-id, webhook-timestamp, and webhook-signature headers you might recognise from other Standard Webhooks adopters. Google's distinctive choice is that there are two configuration models with two different signing schemes: static webhooks (project-level, signed with HMAC-SHA256) and dynamic webhooks (per-job, signed with RS256 JWTs verified via Google's JWKS endpoint).
Each delivery is an HTTP POST with a JSON body. Your endpoint must return a 2xx status to acknowledge receipt; anything else is treated as a failure and retried with exponential backoff for up to 24 hours.
| Feature | Details |
|---|---|
| Webhook configuration | API-only via WebhookService (/v1/webhooks/) or the google-genai SDK; no AI Studio UI at launch |
| Configuration models | Static (project-level, HMAC) or dynamic (per-job, JWT) |
| Verification spec | Standard Webhooks headers; HMAC-SHA256 (static) or RS256 JWT (dynamic) |
| Headers | webhook-id, webhook-timestamp, webhook-signature |
| Replay protection | Five-minute timestamp tolerance recommended |
| Acknowledgement | Any 2xx response; respond "within a few seconds" |
| Retry logic | At-least-once delivery, exponential backoff, up to 24 hours |
| Auto-disable | Not documented |
| Ordering | Not guaranteed |
| Payload shape | Thin — data carries resource pointers, often a gs:// URI |
| SDK support | google-genai Python SDK; standardwebhooks libraries for verification |
Google's design choices are deliberate. Adopting Standard Webhooks means tooling works across providers. Thin payloads with gs:// pointers keep deliveries small and let Google deliver multi-gigabyte Batch results without inflating webhook bodies. The dual signing model lets static endpoints share a secret across all events while dynamic endpoints get the security guarantees of asymmetric cryptography. Each of these choices has a flip side, which we cover in the limitations section below.
Gemini webhook features
Standard Webhooks compliance
Gemini implements the Standard Webhooks specification, which means the headers, the canonical signed-payload string, the signature format, and the timestamp tolerance all follow a published spec rather than a Google-specific scheme. Tooling that targets Standard Webhooks — including the standardwebhooks package in Python and Node, and Hookdeck's verification — works without Gemini-specific configuration.
Three event categories
Gemini emits webhook events across three product surfaces:
- Batch API —
batch.succeeded,batch.failed,batch.cancelled,batch.expired. The succeeded payload includes anoutput_file_uripointing at a Google Cloud Storage object containing the JSONL result file. - Interactions API —
interaction.requires_action(fired when an in-progress agent conversation hits a pending function call and needs your application to step in),interaction.completed,interaction.failed,interaction.cancelled. - Video generation —
video.generated, withfile_idandvideo_uriin the payload.
Each endpoint subscribes per-event-type via the subscribed_events array.
Static and dynamic webhook configuration
This is Gemini's most distinctive feature. Static webhooks are registered once via client.webhooks.create(...) and apply to all matching events in the project. They're best for global integrations — a Slack notifier, a database sync, an audit logger. Dynamic webhooks are configured per-job inside an individual API call:
file_batch_job = client.batches.create(
model="gemini-3-flash-preview",
src="files/uploaded_file_id",
config={
"display_name": "My Setup",
"webhook_config": {
"uris": ["https://my-api.com/gemini-webhook-dynamic"],
"user_metadata": {"job_group": "nightly-eval", "priority": "high"}
}
}
)
Dynamic webhooks let you route specific jobs to dedicated workers or fan-out queues. The user_metadata field is echoed back in the webhook payload, which is genuinely useful as a routing primitive — a multi-tenant agent platform can tag each job with its tenant ID and skip a database lookup when the event arrives.
Built-in secret rotation grace period
When you rotate a static webhook's signing secret, Gemini supports a revocation_behavior parameter that controls when the previous secret stops being valid. REVOKE_PREVIOUS_SECRETS_AFTER_H24 keeps the old secret valid for 24 hours, giving you a safe overlap window to deploy the new secret across all environments. Immediate revocation is also available for incident response. This is more developer-friendly than several competitors that require you to manage overlap manually.
Thin-payload pattern with GCS hydration
Every delivery returns the event type, the version, the timestamp, and a data object containing pointers to the affected resource — typically including a gs:// URI for Batch and video results. To get the actual result, your handler reads the file from Google Cloud Storage. This avoids stale-data races on retries and keeps every webhook delivery small enough to verify and queue cheaply, but it does mean your handler needs Google Cloud credentials in addition to a Gemini API key.
Security considerations
Verifying webhook signatures
Every webhook delivery includes the three Standard Webhooks headers. Verification is non-optional in production: without it, your handler is an unauthenticated public endpoint that does work on receipt of any HTTP POST.
Static webhooks are signed with HMAC-SHA256 over the canonical webhook-id.webhook-timestamp.body string. Dynamic webhooks are signed with RS256 JWTs that must be verified against Google's JWKS endpoint at https://generativelanguage.googleapis.com/.well-known/jwks.json. The two schemes share the same headers and the same idempotency key (webhook-id), but the cryptographic verification is different.
For both, the standardwebhooks library handles HMAC verification, and standard JWT libraries (PyJWT, jsonwebtoken) handle the RS256 path. For a code-level walk-through of both verification flows, see our guide to securing and verifying Gemini webhooks with Hookdeck.
Store webhook secrets securely
Treat the static webhook signing secret like a database credential. Store it in environment variables or a secrets manager — never in source control, never hardcoded. The secret is shown once at endpoint creation and is not retrievable afterward, so capture it then or you'll have to delete and recreate the endpoint to get a new one.
For dynamic webhooks there's no static secret to store — verification uses Google's public keys from the JWKS endpoint. Cache the JWKS response with a sensible TTL to avoid hitting Google's identity service on every webhook delivery.
Gemini webhook limitations and pain points
Two signing models means two verification paths
The Problem: Static webhooks use HMAC-SHA256 with a shared secret. Dynamic webhooks use RS256 JWTs verified against a JWKS endpoint. Any handler that wants to support both has to implement and maintain both verification flows.
Why It Happens: Google designed each model for a different use case — static for shared global integrations, dynamic for per-job routing with stronger asymmetric guarantees.
Workarounds:
- Pick one model and stick to it if your use case fits.
- If you need both, factor verification behind a single interface so handler code doesn't care which flow ran.
How Hookdeck Can Help: Event Gateway verifies both static-webhook HMACs and dynamic-webhook JWTs at the edge. Your application handler only ever sees pre-verified events and doesn't need to know which configuration model produced them.
Thin payloads require GCS reads
The Problem: Webhook payloads carry resource pointers, often a gs:// URI for Batch results and video files. Your handler has to authenticate to Google Cloud Storage, fetch the file, and parse it before it can act on the result.
Why It Happens: Google deliberately ships thin payloads to avoid inflating webhook bodies with multi-gigabyte Batch outputs.
Workarounds:
- Defer the GCS read to an async worker so the synchronous handler can return 2xx fast.
- Cache results by
idif your handler is likely to see the same resource multiple times. - Make sure your handler's service account has read access to the relevant buckets.
How Hookdeck Can Help: Event Gateway queues every event durably so your worker can fetch the GCS object at its own pace. The 24-hour retry window stops being a constraint because Hookdeck verifies and acknowledges at the edge.
At-least-once delivery means duplicates
The Problem: Google explicitly notes that duplicate copies of the same event can be delivered under congestion. Your handler will receive duplicates, and acting on them more than once can mean duplicate notifications, duplicate database writes, or duplicate downstream API calls.
Why It Happens: Gemini prioritises eventual delivery over exactly-once semantics. The webhook-id header is your idempotency key.
Workarounds:
- Track processed
webhook-ids in a fast key-value store with a TTL longer than the 24-hour retry window - Make every side effect idempotent at the destination — use unique constraints, upserts, or natural keys instead of inserts.
- Don't rely on processing order to detect duplicates; events can arrive out of sequence.
How Hookdeck Can Help: Event Gateway dedupes events at the edge using configurable rules and only forwards unique events downstream. For deeper background, see our guide to webhook idempotency.
No ordering guarantees
The Problem: Gemini makes no guarantees about the order in which events arrive. An interaction.completed may land before its preceding interaction.requires_action. State machines that assume sequential delivery will get into impossible states.
Why It Happens: Gemini's delivery infrastructure is parallelised; ordering would require serialising deliveries per resource, which would limit throughput.
Workarounds:
- Use the
timestampfield on the event envelope to sort. Don't rely on receipt order. - Treat each event as a transition trigger that re-fetches the current state from the API rather than as the authoritative current state itself.
- Build state machines that tolerate out-of-order transitions — for example, treat
failedandcancelledas terminal regardless of what other events have arrived.
How Hookdeck Can Help: Event Gateway's queueing and replay let you re-process events in any order you choose during recovery. For deeper context on why ordering is hard and how to handle it, see our guide to webhook ordering.
Five-minute timestamp tolerance
The Problem: Webhook payloads with a webhook-timestamp more than five minutes off real time should be rejected as a replay-protection measure. If your handler is slow, queued behind a backlog, or briefly down, deliveries can age out before you process them.
Why It Happens: The freshness window is what makes signature verification resistant to replay attacks — without it, a captured signed payload could be re-delivered indefinitely.
Workarounds:
- Acknowledge fast (return 2xx within milliseconds) and process asynchronously off a queue.
- Monitor end-to-end latency from
webhook-timestampto processing and alert on regressions. - Avoid processing-time work in the synchronous webhook handler; defer everything except verification and enqueue.
How Hookdeck Can Help: Event Gateway verifies the signature once at the edge and acknowledges Gemini immediately. Your handler then consumes from Hookdeck's queue at whatever rate suits — the freshness window doesn't apply to Hookdeck's downstream forwarding because the verification has already happened.
No documented auto-disable
The Problem: Google's docs do not specify auto-disable behaviour for endpoints that fail consistently. In practice this means a misconfigured endpoint can fail silently within the 24-hour window with no automatic intervention.
Why It Happens: Gemini may rely on the 24-hour retry ceiling as the implicit bound rather than a failure-count threshold.
Workarounds:
- Monitor endpoint health and alerting yourself.
- Build runbooks for spotting silent endpoint failures.
How Hookdeck Can Help: Event Gateway's observability surfaces failed deliveries in real time with detailed error context, so you don't have to wait for a downstream symptom to discover an endpoint is broken.
No first-party CLI for local development
The Problem: Gemini doesn't ship a CLI for forwarding webhooks to a local handler. The cookbook quickstart walks through ngrok, which gives you a public URL but no replay history, no durable inspection, and no signature verification at the edge.
Why It Happens: Google hasn't shipped a CLI for this use case at launch.
Workarounds:
- Use ngrok for the public URL.
- Save raw payloads to disk and re-POST them manually with curl.
How Hookdeck Can Help: The Hookdeck CLI gives you a stable public HTTPS URL, full request inspection, and one-click replay — filling the gap Google leaves. CLI users activate at materially higher rates than non-CLI users; the development feedback loop is genuinely the difference between an integration that ships and one that stalls. See our guide to testing and replaying Gemini webhooks locally.
Configuration is API-only
The Problem: At launch, there's no UI in Google AI Studio for managing webhook endpoints. Configuration, secret rotation, deletion, and status checks all happen via the WebhookService REST API or the google-genai SDK.
Why It Happens: Google appears to have prioritised API surface over a console UI for the initial launch.
Workarounds:
- Build a thin internal admin tool over the
WebhookServiceAPI. - Treat webhook configuration as code — script it, version it, and apply it from CI.
How Hookdeck Can Help: Hookdeck's dashboard provides full UI for managing webhook sources, destinations, transformations, and retries — covering the operational surface Google's API leaves bare.
Best practices
Always verify webhook signatures
Use the standardwebhooks library for static webhooks and a JWT library with JWKS support for dynamic webhooks. Never trust an unsigned payload, never skip the freshness check, and never use a non-constant-time string comparison if you're verifying HMAC manually. Standard Webhooks supports overlapping signatures during secret rotation — your manual verification needs to handle that.
Return 2xx status codes quickly
Acknowledge within milliseconds and defer work to an async queue. The five-minute timestamp tolerance plus exponential backoff mean slow handlers compound problems — deliveries age out, retries pile up, and recovery slows.
Subscribe only to required events
Each endpoint subscribes per-event-type. Don't blanket-subscribe to every event family — it inflates your processing volume and forces your handler to filter events it never needed.
Dedupe on webhook-id
Google explicitly says duplicates can occur. Track processed webhook-ids with a TTL longer than the 24-hour retry window. The webhook-id is unique per event but reused across retries, which is exactly what makes it usable as an idempotency key.
Use user_metadata for routing
If you're using dynamic webhooks, attach routing context to the job in user_metadata rather than encoding it in URL path or query parameters. The metadata is echoed back in the webhook payload, which avoids a database lookup at processing time and keeps your handler stateless.
Plan secret rotation with the 24-hour grace
When rotating static webhook secrets, prefer REVOKE_PREVIOUS_SECRETS_AFTER_H24 over immediate revocation. Deploy the new secret to your handlers within the grace window, validate that traffic is signing correctly, and let the old secret expire automatically.
Process events asynchronously
Verify, enqueue, and acknowledge synchronously; do everything else off a queue. This keeps your handler under the freshness window, makes retries safe, and lets you scale workers independently of webhook ingress. For background, see our guide on why to implement asynchronous processing for webhooks.
Implement reconciliation jobs
Webhooks are a notification mechanism, not a system of record. Periodically reconcile against the Gemini API to catch any events that were dropped or processed incorrectly. The thin-payload pattern actually helps here — your reconciliation job is already shaped like the rest of your handler, since both pull full state from the API and GCS.
Validate payloads
Even after signature verification, validate the parsed event against an expected schema. Google adds new event types over time, and a handler that assumes every payload has fields it expects will break the first time a new event family ships.
Conclusion
Gemini webhooks are how long-running async work (Batch, Interactions, video generation) becomes addressable from the rest of your stack. Google ships them on top of the Standard Webhooks specification, with a distinctive dual signing model (HMAC for static, RS256 JWT for dynamic) that gives you flexibility in exchange for a more complex verification surface. The operational responsibilities of running them in production are non-trivial: dual-model verification, deduplication, ordering tolerance, the 24-hour retry window, GCS hydration, and the local-development feedback loop are all problems your team will own unless you offload them.
For teams that want to focus on what their AI workloads do rather than on the infrastructure that delivers their events, Hookdeck handles verification (both HMAC and JWT), deduplication, queueing, retries, replay, and observability at the edge — so your application only ever sees verified, deduplicated, durably-queued events.
Get started with Hookdeck Event Gateway for free and start handling Gemini webhooks reliably in minutes.