SSE streaming vs push notifications in A2A: hold a connection or register a webhook
A2A streaming sends task events over an open SSE connection. Push notifications POST them to a client's webhook. How they differ, and when to poll.
SSE streaming is for a client that stays connected and wants each task update the moment it happens, delivered over one long-lived HTTP response. Push notifications are for a client that cannot or should not hold a connection open: it registers a webhook URL, and the agent sends each update there as an HTTP POST.
Both deliver the same events. In A2A 1.0 a stream event and a webhook body are the same object, a StreamResponse holding a task, a message, a status update or an artifact update. The difference is who keeps the channel open and who has to be reachable. With streaming, the client holds a connection to the agent. With push, the agent calls the client.
Status as of September 26, 2026. Streaming and push notifications are both optional capabilities in A2A 1.0, declared in the Agent Card as capabilities.streaming and capabilities.pushNotifications. A2A is a Linux Foundation project; its latest release, v1.0.1, shipped on May 28, 2026. Server-Sent Events is defined in section 9.2 of the WHATWG HTML Living Standard. Polling with GetTask works with every agent and needs no capability.
What SSE streaming is for
A client starts a stream with SendStreamingMessage, or attaches to an existing task with SubscribeToTask. Over the JSON-RPC and HTTP+JSON bindings, the agent answers 200 with Content-Type: text/event-stream. Over gRPC, it uses a server-streaming RPC.
The specification fixes the order of a task stream. The first event is the Task, then zero or more status and artifact updates, in the order they were generated. For SubscribeToTask, that first Task is the task’s current state, so nothing is lost between reading a task and subscribing to it. An agent can serve several streams for one task, all receiving the same events, and closing one does not affect the task or the other streams.
An illustrative stream over the JSON-RPC binding, where each event’s data is a complete JSON-RPC response:
data: {"jsonrpc": "2.0", "id": 4, "result": {"task": {"id": "task-9e1f", "contextId": "ctx-2b77", "status": {"state": "TASK_STATE_WORKING", "timestamp": "2026-09-26T15:00:00.000Z"}}}}
data: {"jsonrpc": "2.0", "id": 4, "result": {"artifactUpdate": {"taskId": "task-9e1f", "contextId": "ctx-2b77", "artifact": {"artifactId": "quote", "parts": [{"text": "40 pallets, Toronto to Chicago: 2,480.00 CAD"}]}, "lastChunk": true}}}
data: {"jsonrpc": "2.0", "id": 4, "result": {"statusUpdate": {"taskId": "task-9e1f", "contextId": "ctx-2b77", "status": {"state": "TASK_STATE_COMPLETED", "timestamp": "2026-09-26T15:00:04.000Z"}}}}
The stream closes when the task reaches a terminal state. The specification is not fully consistent here: its operation sections say terminal state, while its HTTP+JSON streaming section says terminal or interrupted. Handle both, and expect the stream to end when a task stops at TASK_STATE_INPUT_REQUIRED.
Two SSE details matter in practice. The HTML standard’s EventSource API reconnects with a Last-Event-ID header, but A2A defines no use of SSE event IDs, so to resume you call SubscribeToTask. And EventSource cannot send a request body or custom headers, so browser clients need an HTTP client that reads the response stream.
What push notifications are for
A client registers a push notification configuration, either inside SendMessage through configuration.taskPushNotificationConfig or later with CreateTaskPushNotificationConfig. The configuration holds a url, an optional token, and optional authentication with a scheme and credentials. It lasts until the task completes or the client deletes it. Illustrative registration for an existing task over JSON-RPC:
{
"jsonrpc": "2.0",
"id": 5,
"method": "CreateTaskPushNotificationConfig",
"params": {
"taskId": "task-9e1f",
"url": "https://buyer.example.com/a2a/notifications",
"token": "illustrative-task-token",
"authentication": { "scheme": "Bearer", "credentials": "illustrative-single-purpose-secret" }
}
}
When the task changes, the agent POSTs a StreamResponse to the URL with Content-Type: application/a2a+json and an Authorization header built from the scheme and credentials. Webhook calls always use plain HTTP and the JSON of the HTTP+JSON binding, whichever binding the client used.
The specification splits the duties. The agent must attempt delivery at least once, may retry with exponential backoff, should time out after roughly 10 to 30 seconds, may stop after repeated failures, and should validate webhook URLs against server-side request forgery. The client must answer with a 2xx status, validate the credentials and check that the task ID is one it expects, and should process notifications idempotently, because duplicates can arrive.
Side by side
| SSE streaming | Push notifications | |
|---|---|---|
| Purpose | Deliver task events live to a connected client | Deliver task events to a client that is not connected |
| Layer | Response format of A2A streaming operations | Webhook calls from agent to client, defined by A2A |
| Who talks to whom | Client opens the connection; agent writes events to it | Agent opens a new HTTP request to the client for each event |
| Transport | text/event-stream over the JSON-RPC and HTTP+JSON bindings; server-streaming RPC over gRPC |
HTTPS POST with application/a2a+json, regardless of binding |
| Discovery | capabilities.streaming in the Agent Card |
capabilities.pushNotifications in the Agent Card; client supplies the URL |
| Auth | The client’s normal credentials on the request that opens the stream | Credentials the client supplied, sent by the agent in Authorization |
| State | One open response per stream; task outlives it; resubscribe with SubscribeToTask |
Configuration lasts until the task ends or is deleted; at-least-once delivery |
| Governance and status | Optional in A2A 1.0 (Linux Foundation); SSE from the WHATWG HTML standard | Optional in A2A 1.0 (Linux Foundation) |
When to use each
Use streaming when the client is waiting: an interactive app showing progress, an orchestrator that needs a sub-task’s result before its next step, or any task that finishes in seconds or minutes. It needs no public endpoint on the client side and no webhook security.
Use push notifications when the task may take hours or days, when the client runs as short-lived functions that cannot hold connections, or when many tasks are in flight at once. The client must run a reachable HTTPS endpoint and treat it like any webhook receiver.
Poll with GetTask when the agent supports neither capability, when the client sits behind a firewall that blocks both long responses and inbound calls, or as a reconciliation step after either of the others.
Using both
They combine well. A client can stream while it is connected and register a webhook so it hears about the task after it disconnects. The A2A streaming guide describes the typical client action: verify the notification, then call GetTask for the complete, current task. The same habit helps after a dropped stream.
Push also matters for in-task authorization. If a task enters TASK_STATE_AUTH_REQUIRED and the agent receives credentials out of band, it may continue without a new message from the client. Section 7.6.2 tells clients to subscribe, register a webhook or poll so they do not miss that change.
Common misconceptions
“Push notifications are faster than streaming.” Both deliver the same events as they occur. Push adds a new HTTP request per event and retries; streaming reuses one open response.
“Streaming works for any agent.” Only for agents that declare capabilities.streaming. Others must return UnsupportedOperationError (-32004). Push configuration calls on agents without push support must return PushNotificationNotSupportedError (-32003).
“A webhook can replace GetTask.” Delivery is at least once, may stop after repeated failures, and can duplicate. GetTask remains the source of truth.
“Only the webhook receiver has security work.” The agent fetches client-supplied URLs, which makes it a potential SSRF path into its own network. The specification tells agents to reject private, loopback and link-local addresses and to use allowlists where appropriate. See A2A vs webhooks for the receiver’s side.
Questions
- Can a client use streaming and push notifications for the same task?
- Yes. The specification lets an agent serve several streams per task, and push configurations work independently of how the task was started. A client can watch a stream while connected and rely on a webhook for updates after it disconnects.
- What happens if a stream drops mid-task?
- The task keeps running; its lifecycle is independent of any stream. Call SubscribeToTask to open a new stream, which begins with the task's current state, or call GetTask to read it once.
Sources
- A2A Protocol Specification (sections 3.1, 3.2.2, 3.3.4, 3.5, 4.3, 9.4, 11.7 and 13.2) (accessed )
- A2A protocol definition (a2a.proto): StreamResponse, TaskPushNotificationConfig, AuthenticationInfo (accessed )
- Streaming and asynchronous operations (A2A documentation) (accessed )
- A2A releases on GitHub (v1.0.1, May 28, 2026) (accessed )
- HTML Living Standard, section 9.2: Server-sent events (accessed )
- OWASP Server-Side Request Forgery Prevention Cheat Sheet (accessed )