A webhook integration starts with a simple idea: when something changes in one system, notify another system so it can respond. The difficult part is deciding what happens between receiving the notification and completing the work. A request can arrive twice, an action can finish after its caller times out, or a newer change can overtake an older job.

Design webhooks and API integrations around explicit state. Separate the incoming delivery, the event it describes, and the work your application chooses to perform. This article uses a hypothetical documentation workflow: a publishing event schedules a page check and creates a screenshot for review. The webhook workflow guide introduces the vocabulary; the sections below turn it into a practical operating plan.

Write down the event contract

Before building a receiver, describe which event should trigger work and why. A broad “content changed” subscription might include drafts, edits, deletions, and republishing. If your goal is to inspect public pages, those events may need different responses. Define the accepted event types and the conditions under which each is relevant.

Give the receiver a small internal record rather than passing an unexamined payload through the entire system. That record might include a provider name, delivery identifier, event category, content identifier, reported revision, receipt time, and workflow name. Keep a documented mapping from the provider's fields to your fields.

Plan for a valid event you do not use. It should produce a deliberate ignored outcome, with a brief reason, rather than a confusing failure. This makes future changes to subscriptions easier to review and keeps an unexpected action from starting an unintended workflow.

Follow the provider's actual delivery rules

The GitHub webhook best practices recommend subscribing only to needed events, using a webhook secret and HTTPS, and checking the event type and action. GitHub specifies a successful response within ten seconds and explains that a requested redelivery retains the original X-GitHub-Delivery value. Those are concrete provider rules, not assumptions to apply automatically to every webhook system.

Create a short integration record containing the provider's acknowledgment deadline, authentication method, delivery identifier, retry behavior, and recovery mechanism. Revisit it when the integration changes. Where behavior is undocumented, make the uncertainty visible and test it in a controlled environment instead of treating a convenient guess as a guarantee.

For the documentation example, decide whether the event contains enough information to select a page or whether a separate authorized API read is needed. A notification can be a prompt to retrieve current state; it does not always need to carry the complete record your worker will use.

Separate acceptance from completion

Give “accepted” a precise meaning in your implementation. One defensible contract is that the delivery has passed validation and its required work has been durably recorded. The receiver can then acknowledge without waiting for browser startup, page rendering, screenshot capture, and image storage.

This design creates two outcomes to monitor. Delivery acceptance tells you whether the event entered your system. Job completion tells you whether the page check finished. A successful receiver response should not be displayed to an operator as proof that the screenshot exists.

If durable storage is unavailable, decide whether to reject the delivery or use another documented recovery path. Recording an event only in a process's temporary memory and reporting it accepted leaves a gap if that process stops. The right implementation depends on your infrastructure, but its meaning should be understandable without knowing the framework.

Handle duplicates at the right boundary

Connect repeated deliveries to one job

Suppose the same publishing event reaches the receiver twice. Ideally, both receipts connect to the same intended page-check job. Keep a delivery record and an explicit relationship to the job rather than using arrival time as identity. If two requests arrive together, the decision to create the job must be coordinated so both cannot independently win.

Give outputs their own identity

Now consider a different problem: the job sends its screenshot to storage, then loses its connection before recording completion. Retrying the whole job may create another file. Preventing duplicate admission alone does not prevent every duplicate side effect. Give important outputs stable identities, and define whether another attempt replaces, reuses, or versions them.

For a review screenshot, a filename derived from the content revision and capture profile can be easier to manage than a random name on every attempt. Preserve attempt records separately so reproducible output naming does not erase the history of what happened.

Decide what ordering means

Imagine two publishing events: revision seventeen followed by revision eighteen. The second job might finish first because its page loads faster. If both write to “latest screenshot,” the older job could overwrite the newer result unless your workflow checks the revision before updating that pointer.

Choose the outcome the product actually needs. A historical audit may require one artifact per revision. A current-status dashboard may require only the newest confirmed revision. A review queue may need both, with the superseded item marked clearly. These are different policies; queue order alone should not silently choose between them.

When events omit a trustworthy revision, consider reading current state before making an irreversible decision. Record that the result represents the state observed at processing time, rather than claiming it reconstructs the precise state at notification time.

Keep authority and payload data separate

The receiver should decide which actions it is allowed to schedule. A page URL or operation name inside a payload should not become unrestricted instructions for a browser runner. Match incoming information to the destinations, workflow types, and accounts your integration is meant to use.

For the example, associate a known content identifier with an approved documentation host. Reject or quarantine data that cannot be mapped safely. Keep credentials in the receiver's or worker's managed configuration, and put references to them in job settings only when necessary. Do not copy secrets into page screenshots, routine logs, or artifact filenames.

These boundaries also improve debugging. Operators can distinguish an event that failed validation from a valid event whose page could not be checked. The API integration planning page helps map those responsibilities across services.

Design recovery before an outage

Maintain an explicit list of unfinished jobs and deliveries that need attention. Give failed work a reason, a next action, and an owner where your team uses ownership. Avoid a single undifferentiated error bucket that mixes a malformed event with a temporary storage interruption.

Build a controlled replay process. An operator should be able to identify the original event, see its prior outcomes, choose the intended recovery action, and record why it was replayed. Confirm the source system's redelivery behavior rather than assuming it will automatically resend every missed event.

Before launch, exercise a short set of scenarios: repeated delivery, worker crash after output creation, unexpected event type, stale revision, inaccessible target page, and unavailable job storage. For each, inspect the final state and determine whether someone can recover without manually reconstructing the event.

Match capacity to the work you accept

A receiver can accept events faster than a browser worker can create screenshots. Decide how much unfinished work your system can hold and what an operator should see when that limit approaches. Track the age of the oldest pending job as well as the number of jobs. Ten tasks delayed for hours may deserve more attention than a brief burst of a hundred tasks that clears quickly.

Choose whether repeated updates to the same document can be combined. A current-page check might replace a still-pending older job with the latest revision, while an audit workflow must preserve every required observation. Record this decision in the event contract. Combining work changes the meaning of the result and should never happen accidentally because a queue became busy.

Keep a small operational view showing received, ignored, pending, active, completed, and failed work. These states help distinguish a quiet source system from a blocked worker and give recovery a concrete starting point.

Conclusion: make every handoff observable

A dependable webhook integration makes its promises small and explicit. It knows which events it accepts, what acceptance means, how work is identified, and what happens when attempts repeat. It preserves enough context to explain an incomplete workflow without retaining unnecessary payload data.

Begin with one event and one bounded action. Connect delivery records, job attempts, and artifacts using a consistent identifier, then inspect both successful and interrupted runs. Pair this design with structured logging for tool workflows so the integration remains understandable as more systems join it.