Vatly
Python Fluent

Process signed webhooks

Fluent verifies a delivery, updates local snapshots and dispatches events through your storage driver.

Fluent verifies a delivery, updates local snapshots and dispatches events through your storage driver. Your framework route supplies the raw bytes and signature. Use the vatly instance wired in Configuration.

from vatly_fluent import Vatly


async def example(vatly: Vatly, raw_body: bytes, signature: str):
    processed = await vatly.webhook_processor().handle(raw_body, signature)
    return "processed" if processed else "duplicate"

Respond 2xx after commit, including for a committed duplicate. Invalid signatures raise VatlyInvalidSignatureError; return 400. Let processing/storage failures return 5xx so Vatly retries. Do not parse and re-encode the body before verification.

Make delivery atomic

Driver responsibilityRequired behavior
webhook_calls.process(event, work)Claim signed event.id; run work; commit all writes together
Repositories and checkout linksParticipate in that same transaction
events.dispatch(event)Write an outbox entry, not a network side effect
Failure or concurrent duplicateRoll back on failure; enforce DB uniqueness/locking

The signature's default clock tolerance is 300 seconds; secret sequences support rotation. Deduplicate the signed ID, not an unsigned event-ID header. Scope all claims by tenant/storefront/mode. Unknown signed events remain retained/dispatched.

Know what the built-in reactions do

EventLocal effect
subscription.startedUpsert; first save emits local.subscription_created
Subscription update/billing/resume/cancel eventsUpdate an existing snapshot and lifecycle expiry
order.paid, order.payment_failedUpsert order snapshot; first paid save emits local.order_created
order.canceledUpdate an existing order
Refund/chargeback eventsUpsert optional reversal repositories
checkout.paidLink through a host-owned checkout record
Other/unknown eventsDispatch without an invented resource write

Reactions make no implicit API reads. Customer bindings never determine ownership. Created events can precede eventual ownership linkage; outbox consumers must read current persisted ownership before acting. Dispatch external effects after commit with idempotent consumer keys. Deduplication is not event ordering: reconcile stale or conflicting snapshots explicitly. See subscription recovery.

API reference · Endpoint API reference

Copyright © 2026