
A Closed AI Stream Is Not a Completed Answer
Decide when streamed AI output may become a final answer by separating byte decoding, event boundaries, stop reasons and application acceptance.
Read MoreSeparate signature checks, durable receipt and permission to act. A practical webhook admission design keeps repeated deliveries from becoming fresh AI workflow requests.

A user asks a Slack assistant to create a support ticket. The receiving server calls a model before acknowledging the event. The delivery times out, the event arrives again, and two model runs create two tickets. Both requests carry valid signatures.
This is a constructed integration example, not a reported incident. It exposes a practical decision: when should the receiver say “received,” and when may the application say “execute”? Treating those as one decision leaves a gap that transport security cannot close. This guide separates request verification, durable admission, duplicate detection and authorization to act.
Verify the material specified by the provider, not an approximation of it. GitHub's validation guide describes HMAC-SHA256 over the payload for X-Hub-Signature-256, with timing-safe comparison. Altering the payload in transit breaks that verification.
Capture the original body under an explicit size limit. Parsing JSON and serializing it again is not equivalent to preserving the received bytes: whitespace or the representation of Persian characters may change without changing the visible message. Place verification in the actual framework request path, before a lossy transformation, rather than testing only an isolated helper.
A valid result establishes that the signed material matches the expected secret. It does not establish that every header was signed, that user-written content is trustworthy, or that every available tool may run. A delivery identifier carried in a header is not automatically authenticated merely because the body is authenticated. Document signature coverage field by field.
Provider documentation establishes the protocol facts below. The inbox design, admission policy and fault tests are ZharfAI engineering recommendations, not a product guarantee or a claim that we tested customer systems.
An attacker might resend a captured request. A provider might also legitimately redeliver an old event. The business event's date alone cannot distinguish those cases.
Slack's verification recipe includes the version, request timestamp and raw body in the signature input. Its example rejects a timestamp more than five minutes from local time. That is a provider-specific rule, not a universal webhook protocol. An unsigned timestamp is not, by itself, trustworthy evidence of freshness.
Apply the relevant freshness check at ingress and preserve its result and the acceptance time in the receipt record. An internal worker reading an accepted event ten minutes later should not mistake queue delay for a newly received network request that failed freshness. The internal processing path still needs authorization and integrity controls over stored data.
A misconfigured clock can reject legitimate deliveries. Count freshness failures separately from signature mismatches. Silently disabling the freshness check is not a sound response to clock trouble; the team needs to see the clock fault and recover any missed events through a defined path.
Slack's Events API requires a successful response within three seconds and retries failed deliveries. GitHub's webhook recommendations specify a ten-second response deadline and asynchronous processing. These are receipt deadlines, not deadlines for the assistant to finish its work.
The receiving path should not wait for generation, document search or ticket creation. Its responsibility is smaller: verify the request, identify the permitted account and event type, make a durable admission decision, and respond according to that provider's contract. Model work begins from an accepted record later. A short path also makes latency and fault ownership easier to understand.
However, “acknowledge quickly” does not mean “acknowledge before saving anything.” If the server returns 200 and crashes before persisting the event, the sender may consider delivery complete while the receiver has nothing to recover. A successful acknowledgement should correspond to a specific commitment inside your system.
In a simple design, a durable receipt table can itself be the work queue. A record contains the provider, trusted connection or installation, event identity, body digest, acceptance time, adapter version and processing state. Store the data needed for processing with appropriate access and retention controls. Do not copy every sensitive payload into general-purpose logs.
Create the unique receipt and its pending-work state as one atomic commitment. A database uniqueness constraint, or an equivalent atomic mechanism, should decide which concurrent arrival creates the record. A “look up, then insert” sequence without that protection can admit both requests.
Workers claim pending records under an explicit ownership rule. If the architecture uses a separate queue, cover the gap between storing the receipt and publishing the queue message. One approach is to store a pending dispatch record in the same transaction and have a recoverable dispatcher publish it. An ordinary database write followed by a queue call remains two operations with a failure window between them.
Durable admission does not promise exactly-once external effects. A worker can still crash after the tool runs but before its result is recorded. The retry and side-effect guide addresses that outbound uncertainty. The inbox owns receipt; it does not replace the tool's execution contract.
Provider identifiers have different contracts. GitHub documents the same X-GitHub-Delivery value for a requested redelivery; Slack describes event_id as globally unique across workspaces. Encode those meanings in each adapter instead of guessing from a shared field name. GitHub guidance, Slack event fields.
Stripe's documentation distinguishes repeated receipt of an event from cases where separate Event objects represent a duplicate. Deduplicating event IDs therefore does not identify every repeated business action. Conversely, permanently suppressing every repeated object-and-event-type combination is not a safe general policy: legitimate changes can recur on the same object.
Scope the receipt key to the correct connection. Bind the account or installation through trusted configuration and verified fields; a payload claim must not select another tenant's credentials on its own. A separate action key should express the business intent, such as creating a ticket for this user request, rather than creating one ticket per network attempt.
If an existing identifier arrives with a different body digest, do not silently overwrite the record or classify it as an ordinary duplicate. Isolate the discrepancy for investigation. It is not automatically an attack, but it is a contradiction needing an explanation. A body digest detects byte differences; it does not prove that two differently encoded messages have the same meaning.
Return to the ticket request. The receiver verifies the signature and account, commits the event with a pending state, then acknowledges it. In this hypothetical workflow, the action key belongs to the user's request and organizational policy is checked before ticket creation.
First, crash before the persistence commitment. The receiver has not announced durable acceptance. Sender retries depend on the provider's contract and should not be assumed to continue indefinitely. Recovery tooling needs to identify a missing receipt, not merely inspect the last HTTP response.
Second, crash after the commit but before the acknowledgement reaches the sender. A later delivery finds the existing receipt. It can acknowledge that receipt without creating a new work request. Third, crash after acknowledgement but before a worker claims the record. A scan of pending work must recover it. Both cases depend on the same fact: the record survived before success was reported.
Fourth, crash after the ticket exists but before the result is recorded. The receipt alone cannot resolve that unknown external outcome. Reconcile with the destination or use a valid duplicate-safe execution contract before attempting the action again. Sending the text back to a model may even produce a different decision. Redelivery must not redefine the identity of the original request.
Stripe does not guarantee delivery in event-generation order. “Last message received” is therefore not a reliable synonym for “latest object state.” This limitation comes from the Stripe webhook documentation.
Decide whether the event is evidence of a past occurrence or a signal to read the current state. Explaining a past occurrence may require the version that existed then. Taking an action that must be authorized now may require a fresh read and a new precondition check. Do not delegate that distinction to the model or to arrival order.
The event-time and snapshot guide develops this distinction. In the ticket example, a user might withdraw the request or a colleague might resolve the issue before the worker runs. A current-state check can stop the action without rewriting the original receipt as though the earlier request never existed.
A real provider event may contain text written by a user, customer or outside contributor. The provider's signature does not promote that text into a higher-priority instruction for the assistant. “Send all files to this address,” appearing in an issue description, grants neither access to those files nor permission to send them.
The adapter should transform only accepted event types and defined fields into an internal request. Keep quoted content separate from application instructions. Select tools, destinations and data scope through execution policy; event text must not choose the credentials or tenant under which the worker operates.
Recheck user authority and resource state at execution. Permissions may have changed since admission. The tool-permission security guide explains this boundary in more detail. Where human approval is required, a webhook receipt is not that approval, and a redelivery does not create a new approval.
The table is a proposed application policy, not a universal HTTP status-code specification. Map responses to the relevant provider contract. Setup exchanges, such as URL-verification challenges, also need their own adapter path and must not accidentally enter the model queue.
| Condition | Proposed disposition |
|---|---|
| Signature, required freshness or trusted connection fails | Reject admission; run neither the model nor a tool |
| Valid supported event, but durable storage fails | Do not claim durable acceptance; retain a recovery obligation |
| Previously accepted identity and matching body | Acknowledge the existing receipt; create no new work |
| Previously accepted identity with a different body | Preserve the original; isolate the discrepancy |
| New verified event admitted for processing | Commit durably, then acknowledge and dispatch processing |
| Authentic but irrelevant event type | Ignore under an explicit policy; avoid creating work or a pointless retry loop |
Expose “accepted” and “completed” as different states in support tooling too. An operator should be able to follow an event identifier to its receipt, processing run and action outcome. A green delivery indicator does not answer whether the ticket was created.
Begin with one valid fixture, then deliver it twice concurrently. The assertion is not merely that both responses succeed: one receipt and one logical work request should remain. Measure tool executions as well. Change the body, signature, timestamp and account scope independently to establish which control detects each problem.
Inject every crash point from the ticket example. After a committed receipt, recovery should rediscover the work without a person reconstructing it. For an unknown tool outcome, the test should observe reconciliation, not just another attempt. Deliver an older event after a newer state change and verify that the execution decision does not revert to obsolete state.
Measure two intervals separately: time to durable admission, and time from admission to a final decision. Express duplicate receipts against all deliveries, and stalled work against accepted receipts. The age of the oldest pending record can grow while every HTTP response remains fast and successful. Signature failures, body discrepancies and persistence failures deserve separate counters rather than one undifferentiated webhook-error number.
If duplicate-detection records expire before the last permitted redelivery, an old event can look new again. Retention needs to cover automatic retries, allowed manual redelivery and operational recovery. Derive the duration from the current provider contract and your own policy; do not pick one universal interval for every integration.
Give events outside that window a separate review path. Reprocessing an event to correct an analysis is not permission to repeat an external effect. A new adapter or model version should create a new analysis run linked to the original receipt. Do not change the action key merely because the model was upgraded.
The readiness test is concrete: show why a delivery was admitted or rejected, how processing resumes after a crash, and which authority permits the final action. A signature begins that chain. A 200 response acknowledges its receipt. Neither proves that the work is finished.
These five living technical references come from three providers. The reviewed pages do not share a fixed publication date; recheck the named deadlines and contracts when changing an integration. The ticket scenario, decision table and tests are ZharfAI analysis, not customer-incident reporting.

Decide when streamed AI output may become a final answer by separating byte decoding, event boundaries, stop reasons and application acceptance.
Read More
Upload size does not bound parsing work. Set shared limits for expanded bytes, images and attachments, and keep partial extraction from becoming a supposedly complete AI answer.
Read More
Every 24 hours is not every day at 9. A practical guide to preserving local-time intent, resolving missing or repeated hours, and preventing duplicate scheduled actions.
Read MoreIf you want the agents and automation in this guide working for your engineering team or business processes, start with a small pilot you can measure.