Webhooks
Fangate exposes two webhook surfaces:
- Inbound webhooks — server-to-server callbacks from payment and verification providers (Fangate receives events).
- Outbound webhooks — creator-registered HTTPS endpoints where Fangate pushes payment events to third-party integrators.
Inbound webhooks (providers)
These are not creator-authenticated API endpoints. They are meant for payment providers and verification providers.
Payment provider webhooks
Confirmed live web routes:
| Endpoint | Method | Purpose |
|---|---|---|
/pwh/paypal | POST | PayPal payment callbacks |
/pwh/stripe | POST | Stripe payment callbacks |
/pwh/epoch | POST | Epoch payment callbacks |
/dataplus/epoch | POST | Epoch DataPlus callbacks |
Payment provider notes
- these endpoints are part of the web route surface, not
/api - they are server-to-server integration points
- frontend and partner clients should not call them directly
Verification webhooks
Confirmed live routes:
| Endpoint | Method | Purpose |
|---|---|---|
/api/yoti/webhook | POST | Buyer age-verification status callback |
/veriff/events | POST | Veriff event callback |
/veriff/decision | POST | Veriff decision callback |
/veriff/auto | POST | Veriff automated callback |
Verification notes
/api/yoti/webhookis part of the API route group- Veriff webhook routes are part of the web route surface
- public API consumers should treat these as provider-owned callbacks, not interactive client endpoints
Inbound security expectations
Integrators operating inbound webhook infrastructure should assume:
- HTTPS only
- provider-side signature or authenticity checks where available
- idempotent processing on duplicate callbacks
- quick acknowledgement with backend processing deferred where needed
Outbound webhooks (integrators)
Creators and integrators register HTTPS endpoints to receive real-time payment notifications instead of polling transaction APIs.
- Auth: Creator Sanctum bearer token or API key with
api-key:webhooks.manage(see API Keys). - Delivery: Async queue (
webhooksqueue); does not block payment processing. - Retries: Up to 3 delivery attempts with backoff 1 min → 5 min → 30 min between failures.
- HTTPS only at registration time.
Supported events (v1)
| Event | Trigger |
|---|---|
payment.successful | Purchase completed and confirmed by PSP (payment_status = success) |
payment.failed | Payment rejected, declined, or cancelled (failed / cancelled) |
payment.pending | Payment initiated but not yet confirmed (e.g. Epoch, PayPal) |
content.approved is planned for a later release and is not emitted in v1.
Registration API
| Method | Path | Description |
|---|---|---|
POST | /api/webhooks | Register endpoint URL, event subscriptions, optional include_set_price |
GET | /api/webhooks | List registered webhooks for the authenticated creator |
PATCH | /api/webhooks/{id} | Update URL, events, include_set_price, or is_active |
DELETE | /api/webhooks/{id} | Remove a webhook |
POST /api/webhooks
Request body:
{
"url": "https://partner.example.com/hooks/fangate",
"events": ["payment.successful", "payment.failed", "payment.pending"],
"include_set_price": true
}| Field | Required | Notes |
|---|---|---|
url | Yes | Must be https:// |
events | Yes | Non-empty array of supported event names |
include_set_price | No | Default false. When true, set_price is included in payment payloads (sensitive pricing data). |
Response 201 includes the webhook resource plus secret (shown once). Store it immediately; it cannot be retrieved later via API.
GET /api/webhooks
Returns an array of webhook objects without the signing secret.
PATCH /api/webhooks/{id}
Partial update. Supported fields: url, events, include_set_price, is_active.
DELETE /api/webhooks/{id}
Removes the webhook and its delivery history.
Delivery payload
Each POST to your URL uses Content-Type: application/json.
{
"event": "payment.successful",
"timestamp": "2026-05-19T10:42:00Z",
"data": {
"transaction_id": "txn_123",
"buyer_email": "buyer@example.com",
"seller_earning": 8.50,
"currency": "EUR",
"product_id": 42,
"media_ids": ["m_101", "m_102"],
"set_price": 10.00
}
}| Field | Always included | Notes |
|---|---|---|
event | Yes | Event type string |
timestamp | Yes | ISO 8601 UTC |
data.transaction_id | Yes | Fangate transaction id (txn_{id}) |
data.buyer_email | Yes | Buyer email on the transaction |
data.seller_earning | Yes | Net amount credited to the creator (major units, e.g. 8.50) |
data.currency | Yes | ISO 4217 currency code (uppercase) |
data.product_id | Yes | Purchased product id |
data.media_ids | Yes | Always an array of media ids (m_{id}), even for a single file |
data.set_price | No | Only when include_set_price: true on the webhook |
media_ids is always an array, matching the live n:m product_media pivot between products and library media (see Products and Media Library).
Delivery headers
| Header | Description |
|---|---|
X-Fangate-Signature | HMAC-SHA256 of the raw JSON body using your webhook secret, format sha256={hex} |
X-Fangate-Delivery-Id | UUID for this delivery attempt (idempotency key) |
Content-Type | application/json |
Verifying signatures (example)
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_FANGATE_SIGNATURE'] ?? '';
$secret = getenv('FANGATE_WEBHOOK_SECRET');
$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('Invalid signature');
}Respond with 2xx to acknowledge successful processing. Non-2xx responses trigger retries per the backoff schedule.
Idempotency
Use X-Fangate-Delivery-Id to deduplicate deliveries. The same id is used across retry attempts for one logical delivery record.
Out of scope (v1)
- Creator dashboard UI for webhook management (API only)
- Delivery log API for integrators
- More than 3 delivery attempts
- Filtering events by
product_idormedia_id content.approvedevents
Related documentation
- API Keys —
can_manage_webhooks/api-key:webhooks.manage - Products — product/media n:m linking via
media_ids - Media Library — standalone library upload and attach/detach
- Upload & payment flow — when transactions move between pending and success