Receive PayPal Account Webhook
POST/api/webhooks/paypal/:integration_instance_id
Called by PayPal, not by an integrator. Receives webhook events for one PayPal account connected with its own API keys. SKU.io subscribes this URL (the integration's webhook_url) on the merchant's PayPal REST app when the keys are saved. No bearer token.
Routing: the workspace and account are identified from integration_instance_id in the path (unknown id → 404). The signature is verified against the webhook subscribed on the merchant's own app, using that app's credentials. If the account has since been disconnected or switched to Connect with PayPal, the delivery is acknowledged with 200 {"message": "This PayPal webhook is no longer in use."} so PayPal stops retrying. The merchant id in the event is remembered on the account, so a later switch to Connect with PayPal can confirm it is the same PayPal account.
Signature verification: every delivery must carry PayPal's PAYPAL-TRANSMISSION-ID, PAYPAL-TRANSMISSION-TIME, PAYPAL-TRANSMISSION-SIG, PAYPAL-CERT-URL and PAYPAL-AUTH-ALGO headers (missing any → 400). SKU.io posts them, together with the exact raw request body and the expected webhook id, to PayPal's verify-webhook-signature API and only continues when PayPal answers SUCCESS; otherwise 400. The body is never re-serialized before verification.
Processing: a verified event is stored once per PayPal event id (redeliveries are acknowledged without a duplicate) and processed in the background; it then appears under List PayPal Webhook Events. A body without id or event_type returns 400. Successful deliveries return 200 {"received": true}.
Authentication: none (PayPal signature headers, verified with PayPal).
Request
Responses
- 200
- 400
- 404
- 422
- 429
OK
Response Headers
Bad Request
Response Headers
Not found — no record with the given identifier (or the route does not exist). Verify the ID before retrying.
Validation failed — the body is a field → messages map (Laravel shape) or the platform envelope with a stable machine-readable code. Fix the payload and resubmit.
Rate limited — platform limit is 1,000 requests/min; individual tokens may carry lower limits. Honor the Retry-After header before retrying. See the Rate Limits guide.