Intempt Docs
GuidesCustomer Data PlatformWebhooks

PayPal webhook

Collect checkout, payment, refund, dispute, subscription and invoice events from PayPal.

Research Preview. This connector is rolling out account by account. Ask your Intempt contact to enable it for your workspace.

The PayPal webhook collects payment activity: checkouts approved, captures completed and declined, refunds, disputes opened and resolved, subscriptions through their lifecycle, invoices paid.

For a merchant taking PayPal this is the revenue spine, and disputes in particular are a signal that rarely reaches the customer profile.

Available events

EventDescription
CHECKOUT.ORDER.APPROVEDBuyer approved the order. Fires for every approved order
CHECKOUT.ORDER.COMPLETEDOrder completed. Marketplace and platform integrations only
CHECKOUT.ORDER.PROCESSEDOrder processed (Orders v2, marketplaces)
PAYMENT.ORDER.CREATEDOrder created (Payments v1, deprecated; the v1 order carries no payer)
PAYMENT.ORDER.CANCELLEDOrder cancelled (Payments v1, deprecated)
PAYMENT.AUTHORIZATION.CREATEDPayment authorized, not yet captured
PAYMENT.AUTHORIZATION.VOIDEDAuthorization voided
PAYMENT.CAPTURE.COMPLETEDCapture succeeded. This is the payment
PAYMENT.CAPTURE.DECLINEDCapture declined
PAYMENT.CAPTURE.DENIEDCapture denied
PAYMENT.CAPTURE.REFUNDEDCapture refunded
PAYMENT.REFUND.FAILEDRefund failed. The buyer has not been refunded
PAYMENT.SALE.COMPLETEDSale completed (Payments v1). Still fires for every Subscriptions v1 recurring charge
PAYMENT.SALE.DENIEDSale denied (Payments v1)
PAYMENT.SALE.REFUNDEDSale refunded (Payments v1)
CUSTOMER.DISPUTE.CREATEDDispute opened. dispute_life_cycle_stage tells you whether it's an INQUIRY or money already reversed
CUSTOMER.DISPUTE.RESOLVEDDispute closed, with outcome
BILLING.SUBSCRIPTION.CREATEDSubscription created, before buyer approval
BILLING.SUBSCRIPTION.ACTIVATEDSubscription active. Also fires on resume from SUSPENDED
BILLING.SUBSCRIPTION.UPDATEDPlan, quantity or shipping revised. Full post-change subscription, no diff
BILLING.SUBSCRIPTION.CANCELLEDSubscription cancelled
BILLING.SUBSCRIPTION.SUSPENDEDPaused by the merchant, or automatically after repeated payment failures
BILLING.SUBSCRIPTION.EXPIREDFinite plan reached its last cycle
BILLING.SUBSCRIPTION.PAYMENT.FAILEDRecurring charge failed. Failure detail is in billing_info.last_failed_payment
INVOICING.INVOICE.CREATEDInvoice created as DRAFT. The recipient hasn't been notified yet
INVOICING.INVOICE.PAIDInvoice paid. Also fires on PARTIALLY_PAID and PAYMENT_PENDING, so check status before counting revenue
INVOICING.INVOICE.CANCELLEDInvoice cancelled
INVOICING.INVOICE.REFUNDEDInvoice refunded
VAULT.PAYMENT-TOKEN.CREATEDPayment method saved
VAULT.PAYMENT-TOKEN.DELETEDPayment method removed
CATALOG.PRODUCT.CREATEDCatalog product created
CATALOG.PRODUCT.UPDATEDCatalog product changed. Full product, no diff

Each event is normalised onto a named stream, so you build segments and journey triggers on the event name rather than on the payload shape.

Two subscription notes:

  • Don't also subscribe to RISK.DISPUTE.CREATED. It's deprecated and superseded by CUSTOMER.DISPUTE.CREATED. Enabling both double-counts every dispute.
  • There's no recurring-payment-succeeded event. A successful subscription charge arrives as an ordinary PAYMENT.SALE.COMPLETED carrying billing_agreement_id. Intempt uses that field to emit billing_success and separate it from a one-off sale.

Identity

Checkout, subscription and invoice events carry the buyer inline: payer.email_address, subscriber.email_address or the invoice recipient's billing_info.email_address.

Captures, refunds and disputes carry no buyer. The payee on those payloads is you, the merchant. Intempt never maps payee.email_address or payee.merchant_id to a person. To find the buyer it follows the payload's links back to the checkout order and reads payer.email_address from there. A dispute payload has buyer.name, which is a display string, not an identity.

Two consequences:

  • A refund or dispute on a Payments v1 sale can't be resolved to a buyer once PayPal switches v1 off, because the lookup goes through the deprecated v1 payment resource.
  • A refund or dispute on an invoice payment resolves through the invoice recipient instead, using invoice_number.

payer.payer_id is stable per PayPal account per merchant, but it's PayPal's id, not yours. Intempt matches on email first. customer.merchant_customer_id on vault events is your own customer id and the strongest join key when you supply it at vaulting.

Endpoint

The collector path for PayPal events is:

https://<your collector host>/webhooks/events/paypal

PayPal doesn't let you add request headers, so the source token travels in the endpoint URL Intempt gives you.

Signature verification is a call back to PayPal, not a local HMAC. PayPal sends PAYPAL-TRANSMISSION-ID, PAYPAL-TRANSMISSION-SIG, PAYPAL-CERT-URL and related headers, and the only supported way to check them is POST /v1/notifications/verify-webhook-signature with the webhook id and the raw body. Intempt makes that call for every delivery and rejects anything PayPal doesn't confirm. The envelope id is the idempotency key, so a redelivery never double-counts.

Historical load

PayPal's historical load runs on the certified Airbyte source source-paypal-transaction (Airbyte name Paypal Transaction). It loads Transaction Search, Invoicing and Disputes records using the same client_id and client_secret you enter above, so one set of credentials covers both halves.

One caveat worth knowing: Transaction Search only returns transactions from the last 3 years, and PayPal serves it in 31-day windows, so a long backfill runs as many requests.

Setup

PayPal webhooks are configured per app in the Developer Dashboard. Create the source in Intempt first so you have the endpoint.

Configure PayPal

  1. Create a PayPal source on the Integrations page and copy the endpoint URL.

  2. In the PayPal Developer Dashboard, open Apps & Credentials, choose Live, and open the app whose events you want.

  3. Under Webhooks, click Add Webhook and paste the Intempt endpoint.

  4. Select the event types in the table above. Leave RISK.DISPUTE.CREATED unticked.

  5. Save and copy the Webhook ID into the Intempt source. Intempt needs it for signature verification.

  6. Use Webhook simulator in the dashboard, or make a small live capture, and confirm PAYMENT.CAPTURE.COMPLETED appears in the Intempt event stream.

On this page