Webhooks vs. Synchronous Calls: When to Use Each for Moderation
Blocking the user until moderation completes is not always the right answer. A decision framework for sync, async, and hybrid patterns.
One of the first architectural decisions when integrating a moderation API is whether to block the user until the result comes back. The naive answer is "yes, always". The right answer is "it depends, and you probably want a mix".
Synchronous moderation: block the write
The flow: user hits submit → your backend calls the moderation API → you wait for the response → if flagged, you reject; otherwise you persist and respond to the user.
When it is the right call:
- Chat messages, comments, reviews: anything that appears to other users immediately.
- Account signups and profile fields (display names, bios). Cleaning up after the fact is expensive.
- Any surface where legal or brand exposure is high.
The cost: a moderation call on the critical path. If the API is slow or flaky, so is your app. Budget for 50 to 100ms added to the perceived submit latency.
Asynchronous with webhooks: persist first, moderate after
The flow: user hits submit → you persist immediately in a "pending" state → you enqueue a moderation job → the moderation API processes and hits your webhook → you update the record based on the verdict.
When it is the right call:
- Image and video uploads. Inference is slow (hundreds of milliseconds to seconds); blocking the upload is a terrible UX.
- Long-form content like articles, listings, descriptions. Users have spent 20 minutes writing; do not add a spinner.
- Any content that is hidden by default (draft posts, private uploads) where "moderation happens before publish" is acceptable.
The cost: you have to design for the pending state. Your UI must show "under review". Your database schema needs a moderation status. Your webhook endpoint needs to be idempotent and signed. This is real work.
The hybrid pattern (what most mature platforms actually do)
Cheap synchronous check in the hot path + expensive async check in the background.
- User submits. You do a fast synchronous call against a lightweight model, along the lines of "is this clearly spam or toxic at high confidence". Takes under 30ms.
- If clearly bad, reject immediately. Great UX because the rejection is instant and explanatory.
- If it passes, persist the content as "published" and enqueue an async deep-check with a heavier model, image analysis, cross-user correlation, and so on.
- If the async check later flags the content, hide or downrank it and optionally notify a human reviewer.
This is how gaming chats, large marketplaces and social networks work. The synchronous call catches the obvious stuff and explains itself to the user; the async check catches the sneakier stuff without hurting latency.
A decision table
| Surface | Pattern |
|---|---|
| Chat message (1:1) | Sync |
| Public comment | Sync, heavy stuff optional async |
| Profile name / bio | Sync |
| Image upload (avatar) | Sync (it is small) |
| Image upload (gallery) | Async webhook |
| Video upload | Async webhook, always |
| Long-form post / listing | Hybrid: sync keyword check, async deep review |
| Live stream frames | Sync with aggressive sampling |
Getting webhooks right
If you adopt async moderation, the webhook is now a critical piece of infrastructure. A short checklist:
- Verify signatures. Every moderation provider signs webhooks; if yours does not, replace them. An unverified webhook endpoint is an open door for anyone to mark content as "approved".
- Be idempotent. Providers retry. Your handler must treat duplicate deliveries as no-ops.
- Respond fast. Acknowledge within a few hundred milliseconds. Do the actual work in a background job.
- Design for ordering. A "rejected" verdict may arrive after an "approved" one due to retries. Always compare timestamps; never trust arrival order.
- Handle the missing-webhook case. If nothing comes back within N minutes, reconcile via a polling job. Webhooks fail more often than vendors admit.
The subtle argument for sync even when async is "faster"
There is a behavioural argument for sync moderation that developers often miss: users who get instant feedback change their behaviour. When a user sees "this looks like spam, please rephrase" immediately, a sizable minority rephrase and post something fine. When the feedback comes 30 seconds later via email, they have moved on, frustrated, and less likely to post again at all.
If you have a community where you want to coach users toward better behaviour (most communities), sync moderation is a feature, not just an engineering convenience.
Keep reading
How to Moderate User-Uploaded Images Before Saving Them to S3
Moderating after upload means the unsafe content already exists on your infrastructure. Here is how to flip th...
Rate Limiting and Retries: Best Practices When Calling a Moderation API
Exponential backoff, jitter, circuit breakers, and what to do when the API is briefly unavailable. Keep your a...
Moderation on the Edge: Cloudflare Workers + ToxicFilter
Run a thin moderation proxy at the edge. Lower latency, smaller blast radius on outages, and a simpler client-...