Webhooks
TaskSocial’s webhook experiment sends task events to a configured receiver. A receiver should verify each request, respond promptly, and tolerate retries. This guide describes the behavior implemented in the current experimental sender and receiver. It does not guarantee exactly-once delivery or universal event ordering.Events
The sender currently creates these event types:task.createdtask.updated
id uniquely identifies the event and should be used for duplicate detection. The type identifies the event kind, while data contains the task payload. In the current implementation, the task’s version increases when the task is updated. The IDs above are illustrative values.
Receive a webhook
Configure an endpoint that acceptsPOST requests with JSON bodies. Return a successful 2xx response after the receiver has accepted the event. The sender treats any 2xx response as a successful delivery.
The current local experiment uses:
Verify the signature
The sender signs the timestamp and exact serialized request body using HMAC-SHA256. It sends these headers:X-Webhook-Timestamp: Unix timestamp in seconds.X-Webhook-Signature:sha256=<hex digest>.Content-Type:application/json.
401 for invalid signatures or expired timestamps.
Example verification function in Node.js
This example shows the signature verification logic, not a complete HTTP route:application/json.
Timestamp validation limits how long a signed request remains acceptable, but it does not replace duplicate detection or prevent every possible replay.
Retries and timeouts
The current worker behavior is:- Each outbound request has a five-second timeout.
- The sender makes up to four attempts in total.
- The configured retry delays are 5, 15, and 30 seconds after failed attempts.
- The worker polls for due deliveries every five seconds, so actual retry timing may be later than the configured delay.
- A final unsuccessful delivery is marked
failed. - A successful
2xxresponse is markeddelivered.
Handle duplicate events
Persist processed event IDs and make event handling idempotent. In this experiment, the receiver stores event IDs in MongoDB and returns HTTP200 with duplicate: true when it receives an event ID that has already been recorded.
Do not rely only on in-memory state because it disappears when the receiver restarts. The current experiment uses a unique database index for event IDs, but checking whether an event was processed and recording its result are separate operations.
Concurrent duplicate requests and failures between state changes can still expose race conditions. This implementation should not be treated as an atomic, exactly-once processing system.
Handle stale or out-of-order updates
Network delays and retries can cause events to arrive in a different order from the order in which tasks changed. A receiver can compare the incoming taskversion with the version already stored and ignore an event whose version is not newer. This helps prevent an older task state from replacing a newer one.
The experiment demonstrated this approach with a version-ordering test. It does not establish a universal ordering guarantee for every concurrent request or failure scenario. Consumers should design their event handling to tolerate duplicate and out-of-order delivery.
Troubleshooting
Current limitations
This is an experimental implementation. Keep these limitations in mind when integrating with it:- Task persistence, event creation, and delivery creation are separate database operations. A task can be saved even if queuing its webhook fails, and an event can be created without its delivery record if the subsequent write fails.
- Receiver task-state updates and processed-event records are also separate operations, so they do not form one atomic transaction.
- The worker’s processing timeout helps recover deliveries left in
processing, but multiple backend instances do not currently use an atomic claim to guarantee that only one worker handles a delivery. - The receiver URL is hardcoded to a local address in the current experiment.
- Delivery retries and version checks reduce common failure modes; they do not provide exactly-once delivery or a blanket guarantee of event ordering.