Skip to content

webhooks: add adcp.webhooks.deliver() helper to eliminate seller boilerplate #212

@bokelley

Description

@bokelley

Context

Round-4 webhooks DX exploration (PR #205) found that every seller implementing webhook emission rewrites ~30 lines of boilerplate:

  1. Read push_notification_config / reporting_webhook from the request
  2. Build the payload via adcp.webhooks.create_mcp_webhook_payload(...) or create_a2a_webhook_payload(...) (branch on transport)
  3. Sign with adcp.webhooks.get_adcp_signed_headers_for_webhook(...)
  4. POST via httpx with json=payload, headers=signed
  5. Echo PushNotificationConfig.token into the payload for buyer-side auth
  6. Handle authentication.schemes[] (Bearer, HMAC, HMAC+timestamp)
  7. Handle retries with byte-identical re-POST

The signer's serialization-format mismatch with json= was already silent-failing across SDKs before PR #205 fixed it. That bug lived in that boilerplate — wherever the seam is between "build payload" and "POST payload," there's room for drift. Collapsing the seam kills the class of bug.

Proposal

async def deliver(
    config: PushNotificationConfig | ReportingWebhook,
    payload: dict | AdCPBaseModel,
    *,
    client: httpx.AsyncClient | None = None,
    secret: str | None = None,  # falls through to config.authentication
) -> httpx.Response:
    \"\"\"One-shot webhook dispatch. Handles signing, token echo, transport
    branching, auth scheme selection, and compact-separator JSON serialization.
    Returns the response for status inspection.\"\"\"

The helper collapses steps 3-6 (and owns step 2 as a convenience) into one call, routes the right payload builder based on the transport hint in config, and signs against the same bytes it POSTs — making the signer/wire-format drift structurally impossible.

Acceptance

  • New adcp.webhooks.deliver() function + tests covering: happy path (MCP + A2A), HMAC auth, Bearer auth, token echo from PushNotificationConfig.token, retry idempotency (re-POST produces identical bytes).
  • Seller skill's "Emitting Webhooks" section switches to await deliver(config, payload) as the primary pattern. The manual 6-step path stays documented for advanced users who need to customize.

Priority

4.1 — DX improvement, not a bug fix. The fix for the signer/wire-format drift landed in 4.0 (PR #205); the helper would have made it harder for that bug to exist in the first place.

Surfaced during round-4 DX exploration: #205

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions