A webhook that may fire twice or not at all
RolesAgent Systems EngineeringCustomer AI Deployment
The question
A test run outlasts any connection your planner can hold open, so you want a webhook. Compare a webhook with polling and with streaming for this case. Then list the receiver's duties, given that the agent promises only to try each delivery at least once, and that the config operations return the stored webhook credentials to anyone who can call them.
Answer it first, aloud or on paper. Then open the answer.
The answer
Show short answer
Register the config inline in SendMessage with returnImmediately: true, so that no event escapes before the config exists, and close the connection. Delivery is at least once, so a notification can arrive twice or, after an outage, not at all: the receiver answers 2xx, checks the task id, processes idempotently, and reads the task with GetTask before it acts. Put the secret your receiver checks in authentication, because token has no standard header, and restrict who can call the four config operations, because a read returns the credentials in clear.
Show full answer
Three ways to wait
| Mechanism | Holds a connection | Learns of a change | Cost when the client restarts |
|---|---|---|---|
polling with GetTask | no | on the next poll | none, the task is stored |
SendStreamingMessage or SubscribeToTask | yes | at once | missed frames; subscribe again for a snapshot |
| webhook | no | at once, when delivery succeeds | missed deliveries; read the task |
A webhook suits work that outlasts the caller. It moves the connection problem to the receiver and adds a second trust boundary.
What arrives
Each notification is one HTTP POST whose body is one StreamResponse in the HTTP+JSON form, with no JSON-RPC envelope. In the recorded run the first body was a statusUpdate with TASK_STATE_WORKING, under Content-Type: application/a2a+json, Authorization: Bearer <secret> and X-A2A-Notification-Token. The SDKs differ on the content type, on the token header, and on timeouts of 5 s or 30 s, and none of them retries. So accept both content types, read either token header, and handle every StreamResponse member, because the reference SDK also posts the first Task.
The receiver's duties
- Answer with a 2xx status to confirm receipt.
- Check that the task id matches a task you expect.
- Process idempotently. A status update is safe to apply twice, keyed by task id and timestamp. An
artifactUpdatewithappend: trueis not, and no delivery id exists. - Treat each notification as a signal and read the task with
GetTaskbefore you act. - Check the
Authorizationvalue against the credential you put inauthentication.
The agent's duties, and where they fail
The agent must put the configured credentials in the request headers and should validate the URL against server-side request forgery, rejecting private ranges such as 10.0.0.0/8 and 192.168.0.0/16, loopback and link-local addresses. The reference SDK's validator does that only when the host turns it on. A config must stay until the task completes or a client deletes it, and a delete must be idempotent.
The trade
Inline registration costs one field and closes the gap between the first event and the config. The secret in authentication costs a per-run value and buys a check the receiver can make. The read-back of credentials in clear is the price of the four config operations, so scope them to the task's owner.
Follow-ups
L3SeniorAn
artifactUpdatewithappend: truearrives twice. Why can you not apply it twice?Show expected answer
it duplicates parts, and there is no delivery id to deduplicate on, so read the task instead.
L3SeniorThe receiver was down for a minute. What did it miss?
Show expected answer
possibly everything, because no SDK retries; read the task when the receiver returns.
L4StaffWhy must the agent reject a webhook URL inside a private range?
Show expected answer
the agent calls that URL for every later event of the task, so an outside caller could aim the agent's requests at an internal service.
L4StaffWho may call
ListTaskPushNotificationConfigs, and why?Show expected answer
only the task's owner, because the reply returns
credentialsandtokenunchanged.
Wrong answers
Show the wrong answers that interviewers hear
“Register the webhook after the task starts with the create operation.”
Events before the config exists are lost. Inline registration leaves no gap.
“Authenticate on the token header.”
The specification gives
tokenno transport, and the SDKs use two header names. Put the secret inauthenticationand checkAuthorization.“Apply each notification as it arrives.”
Duplicates and lost deliveries both happen. The stored task is the source of truth.
Taught in
The push notification section records the eight deliveries and the SDK table, and the security requirements section lists the webhook rules.