AnnouncementsIntroducing Arkoren

    Menu

    Provider Adapters

    Choose the correct provider verifier, supply endpoint credentials, and preserve native event keys and delivery identities.

    What's on this page
    1. Compare body signatures, token verification, Basic Auth, and remote verification.
    2. Configure sources for Resend, Mailgun, Postmark, OpenAI, Stripe, and PayPal.
    3. Preserve provider event keys and distinguish delivery identity from resource identity.
    On this page

    A valid webhook proves only what its provider’s protocol authenticates. A body signature protects different data from Mailgun’s signed token or Postmark’s Basic Auth. Select the matching adapter and credentials before treating a notification as application input.

    Each adapter returns a webhook.Source and publishes credential-free event definitions. Bind those definitions using Receiving Webhooks. Outbound drivers do not register endpoints, and their API credentials are not interchangeable with endpoint verification credentials.

    Known authentication failures wrap webhook.ErrRejected and answer 400 by default. A notification that passes verification can still fail envelope or payload decoding with 503. Neither answer guarantees the provider will retry; use the acknowledgement rules when deciding your endpoint’s failure policy.

    Verification Boundaries

    This comparison covers the shipped adapters, not every protocol the providers offer. All except PayPal verify locally without fetching event resources. Delivery.ID is the identity used by optional core deduplication, not necessarily the identifier of the email, response, or payment affected.

    Adapter Authentication Delivery.ID
    Resend Svix v1 HMAC covering ID, timestamp, and exact body svix-id header
    Mailgun HMAC covering timestamp + token, not event-data UTC event date + ":" + event ID
    Postmark outbound Endpoint Basic Auth, not a body signature X-PM-Webhook-Trace-Id, or empty when absent
    Postmark inbound Separate inbound source with endpoint Basic Auth Inbound MessageID
    OpenAI SDK verification of exact body and webhook headers webhook-id header, not JSON event ID
    Stripe SDK verification of exact body and Stripe-Signature Native event ID
    PayPal Remote verification of original event and signing metadata Native event ID, not transmission ID

    Use HTTPS at the public endpoint. Timestamp freshness checks do not prevent replays inside the accepted window; configure delivery policies and make business effects idempotent. Empty IDs disable core duplicate suppression. Do not substitute resource IDs or attempt IDs: they can collapse distinct events or fail to recognize redeliveries.

    Mollie’s classic callback is an unauthenticated status hint, not an authenticated source. Use the application-owned Mollie callback and retrieve authoritative state through the payment API.

    Resend

    Choose resendwebhook for Resend email notifications. Register your public webhook URL and selected events in Resend, then load that endpoint’s whsec_ secret. No sending API key is required. From your application’s module directory:

    go get arkoren.dev/driver/resend

    Import arkoren.dev/driver/resend/resendwebhook and construct the source:

    source, err := resendwebhook.New(signingSecret)

    The secret must contain nonempty standard base64 after whsec_, without line breaks. Invalid secrets return webhook.ErrInvalid. The verifier requires unique Svix authentication headers and accepts matching v1 signatures within five minutes of local time in either direction. It verifies before parsing the envelope; bad signatures wrap both webhook.ErrRejected and resendwebhook.ErrInvalidSignature.

    Bind EmailSent, EmailDelivered, EmailBounced, EmailComplained, or EmailReceived to receive typed Email metadata. EmailDelivered means acceptance by the recipient’s mail server, not inbox placement or reading. EmailReceived does not fetch email bodies or attachments; perform those lookups in application work after acceptance. See the mail integration and resendwebhook API.

    Mailgun

    Choose mailgunwebhook for Mailgun’s JSON event notifications. Configure the webhook URL and events in Mailgun and load the account’s HTTP webhook signing key, not an outbound API key.

    Mailgun signs only timestamp + token, not event-data. A successful token check does not prove payload integrity. Protect the HTTPS endpoint and account for this limitation before allowing a payload to authorize a consequential action.

    Install the module:

    go get arkoren.dev/driver/mailgun

    With import arkoren.dev/driver/mailgun/mailgunwebhook, construct the source:

    source, err := mailgunwebhook.New(signingKey)

    Construction rejects empty keys, whitespace, and control characters without trimming or repairing them. The source checks HMAC and a local timestamp window of thirty minutes in either direction. There is no token cache: a valid token can verify more than once. Signature failures wrap webhook.ErrRejected and mailgunwebhook.ErrInvalidSignature.

    If delayed deliveries require a wider local window, choose that policy explicitly. With time imported, this alternative construction allows one hour:

    source, err := mailgunwebhook.New(signingKey, func(signature *mailgunwebhook.Builder) {
    	signature.Tolerance(time.Hour)
    })

    Zero restores thirty minutes; a negative tolerance is invalid. A larger window also accepts older captured tokens. The window measures signature age, not event age. Delivery identity instead uses the event’s UTC date and native ID because Mailgun event IDs are unique only within a day.

    Accepted, Delivered, Failed, Opened, Clicked, Unsubscribed, and Complained decode native Mailgun event payloads. Failed retains temporary or permanent severity rather than treating every delivery failure as terminal. See Mailgun notifications and the mailgunwebhook API.

    Postmark

    Choose postmarkwebhook for outbound notifications or parsed inbound email. Configure endpoint-specific Basic Auth credentials on the Postmark webhook URL; the sending API token is not a receiver credential. Public HTTPS is mandatory, though TLS can terminate at a trusted proxy. Apply Postmark IP allowlisting at the trusted edge. Basic Auth neither signs the body nor prevents a credential holder from submitting or replaying it.

    Install the module:

    go get arkoren.dev/driver/postmark

    Import arkoren.dev/driver/postmark/postmarkwebhook. For outbound notifications:

    source, err := postmarkwebhook.New(username, password)

    The constructor rejects empty credentials, control characters, and a colon in the username. The source rejects missing, incorrect, or duplicate Authorization headers before parsing the body. Authentication failures wrap webhook.ErrRejected; malformed authenticated JSON answers 503 rather than a credential rejection.

    Bind Delivered, Bounced, SpamComplaint, Opened, Clicked, or SubscriptionChanged for their typed outbound payloads. Core deduplication uses X-PM-Webhook-Trace-Id when present. Do not replace a missing trace ID with an outbound MessageID: different opens or clicks can share the same message ID.

    For inbound email, configure a separate URL and construct a separate source:

    source, err := postmarkwebhook.NewInbound(username, password)

    Bind Inbound on that receiver. Inbound JSON has no RecordType, so the adapter supplies the exact event key Inbound; its delivery ID is the inbound MessageID. The same HTTPS and credential-replay limits apply. Treat email contents as untrusted input even when endpoint authentication succeeds.

    See outbound notifications, inbound email, and the postmarkwebhook API.

    OpenAI

    Choose openaiwebhook for background response lifecycle notifications. Register the endpoint and events in OpenAI and load its dashboard endpoint secret, not your model API key. Install the module:

    go get arkoren.dev/driver/openai

    With import arkoren.dev/driver/openai/openaiwebhook:

    source, err := openaiwebhook.New(signingSecret)

    The secret must use canonical whsec_-prefixed base64 with at least 24 decoded bytes. Invalid secrets wrap webhook.ErrInvalid. The OpenAI SDK verifies the exact body and authentication headers locally before envelope parsing. Signature failures wrap webhook.ErrRejected and openaiwebhook.ErrInvalidSignature. There is no network call or source lifecycle hook.

    ResponseCompleted, ResponseFailed, ResponseCanceled, and ResponseIncomplete produce a Response reference with an ID and event time, not generated output or failure details. ResponseCanceled retains the native key response.cancelled. Use the response ID for a later API lookup; preserve the distinct webhook-id as Delivery.ID. See response notifications and the openaiwebhook API.

    Stripe

    Choose stripewebhook for Stripe snapshot events, not thin notifications. Register the endpoint and event selection in Stripe and load that endpoint’s whsec_ signing secret separately from the payment API key.

    go get arkoren.dev/driver/stripe

    Import arkoren.dev/driver/stripe/stripewebhook:

    source, err := stripewebhook.New(signingSecret)

    Construction requires a whsec_ prefix, a nonempty suffix, and no whitespace or control characters. The Stripe SDK verifies the exact body and Stripe-Signature locally. Signature age defaults to five minutes; an optional construction callback can set stripewebhook.Builder.Tolerance. Zero restores the default, and a negative value is invalid. Signature failures wrap webhook.ErrRejected, stripewebhook.ErrInvalidSignature, and the SDK cause.

    Shipped event definitions cover checkout, PaymentIntent, refund, subscription, invoice, and invoice-payment snapshots and decode to payment.Observation. Bound snapshot decoders require an exact API-version match with the installed Stripe SDK and the expected resource schema. Mismatches answer 503, not a bad signature rejection. Authenticated unbound events do not undergo those bound schema checks.

    No Stripe resource is fetched and no payment state is changed by decoding. Use Stripe notifications for event selection and reconciliation; checkout completion is not itself proof of collected funds. The full definitions are in the stripewebhook API.

    PayPal

    Choose paypalwebhook for PayPal notifications whose original event and signing metadata can be verified through PayPal’s API. You need a client ID, client secret, and registered webhook ID from the same environment. The source defaults to sandbox independently of any payment driver.

    go get arkoren.dev/driver/paypal

    Import arkoren.dev/driver/paypal/paypalwebhook. With matching sandbox credentials:

    source, err := paypalwebhook.New(clientID, clientSecret, webhookID)

    Construction makes no network call. Request verification does: PayPal OAuth or verification outages can prevent acceptance of a genuine notification and answer 503. Missing signing metadata, invalid event JSON, and a confirmed verification failure wrap webhook.ErrRejected. An unknown verification result remains a processing failure. The PAYPAL-CERT-URL header is sent as data to PayPal, not fetched as an application-selected URL.

    For production, explicitly select live mode with matching live credentials and registration in this alternative construction:

    source, err := paypalwebhook.New(clientID, clientSecret, webhookID, func(listener *paypalwebhook.Builder) {
    	listener.Live(true)
    })

    To bound verification I/O, HTTPClient accepts an application-supplied *http.Client. The source copies it, disables redirects, and uses thirty seconds when its timeout is nonpositive; transports remain shared and are not closed by the source. Choose a timeout within the provider’s response deadline.

    Shipped definitions cover order approval, captures, sales, refunds, and subscription lifecycle events. They decode to payment.Observation without applying business state. Delivery identity is the event ID, not the retry-specific transmission ID. Follow PayPal notifications for bindings and reconciliation, and consult the paypalwebhook API.

    Boundary Tests

    Test with valid provider fixtures, then supply incorrect credentials or required authentication metadata and confirm rejection before your handler runs. For body-signing protocols, also confirm that altering the body invalidates the signature. Do not expect that protection from Mailgun’s token signature or Postmark’s Basic Auth; test their stated limits rather than assuming body integrity. Include authenticated unknown events and bound schema failures: authentication success does not mean every payload is decodable. For SDK-backed verification, use the SDK version your application actually ships.

    A substitute source can test core outcomes and queue handoff, but it cannot prove provider authentication, live PayPal verification, or actual provider retries. Check those boundaries against your configured provider environment separately. Then choose Delivery Policies for receipts, queued work, and receiver diagnostics; their categories are not provider event keys.

    Open a bulk documentation archive?

    This file contains every guide in its scope, not API declarations. Loading it all into an agent can exhaust its context window and increase processing time and cost. For coding tasks, start with the index and read only the relevant pages.

    Use the full archive for offline indexing, bulk processing, or deliberate large-context use.

    Browse the index instead
    Open archive