Forte delivers signed webhook events to your backend endpoint whenever a payment status changes. For VDG purchases, deliver the item to the user only after receiving a payment_approved event.
Setup Overview
Create a subscription
Register your webhook endpoint with Forte via POST /notifications/v1/subscriptions.
Receive and verify events
Forte will POST signed events to your endpoint. Verify the signature using your signing_key and respond with 200.
Managing Subscriptions
Create a Subscription
POST /notifications/v1/subscriptions
Authorization: Bearer {{access_token}}
Content-Type: application/json
{
"endpoint": "https://yourdomain.com/webhooks/forte",
"description": "Production payment events"
}
Response:
{
"data": {
"id": "c83ff48a-cd80-4f02-b63f-2155a27682cd",
"endpoint": "https://yourdomain.com/webhooks/forte",
"description": "Production payment events",
"signing_key": "43dec7af-275d-4c5f-b502-c9772858daab",
"status": "enabled",
"version": "1"
}
}
Store the signing_key securely. It is used to verify the authenticity of all incoming webhook events.
Subscription limits| Environment | Max enabled subscriptions |
|---|
| Production | 1 |
| Sandbox / Development | 3 |
Attempting to enable a subscription beyond this limit returns 429.
List Subscriptions
GET /notifications/v1/subscriptions
Authorization: Bearer {{access_token}}
Enable / Disable a Subscription
# Enable
POST /notifications/v1/subscriptions/{id}/enable
Authorization: Bearer {{access_token}}
# Disable
POST /notifications/v1/subscriptions/{id}/disable
Authorization: Bearer {{access_token}}
Update a Subscription
PATCH /notifications/v1/subscriptions/{id}
Authorization: Bearer {{access_token}}
Content-Type: application/json
{
"endpoint": "https://yourdomain.com/webhooks/forte-v2",
"description": "Updated description"
}
Delete a Subscription
DELETE /notifications/v1/subscriptions/{id}
Authorization: Bearer {{access_token}}
Subscription deletion is irreversible.
Test a Subscription
Send a test event to confirm your endpoint is reachable:
POST /notifications/v1/subscriptions/{id}/test
Authorization: Bearer {{access_token}}
Returns 200 if your endpoint responds with 200, otherwise 502.
Receiving Webhooks
Your endpoint must respond with HTTP 200 for every webhook received.
If Forte does not receive a 200, it will retry 3 times before marking the webhook as failed. Subscriptions that repeatedly fail may be automatically disabled.
Verifying Signatures
Forte signs all webhooks with HMAC-SHA256 using the signing_key returned when you created the subscription. The signature is included in the X-Forte-Payments-Webhook-Signature request header. Always verify this signature before processing any payload.
Replaying Webhooks
If your endpoint was unavailable during a period, replay all webhooks from that window:
POST /notifications/v1/webhooks/replay?start_timestamp={{utc_timestamp}}&end_timestamp={{utc_timestamp}}
Authorization: Bearer {{access_token}}
Webhook Payloads
Payment Status Flow
Created → Approved (deliver item)
Created → Declined (do not deliver)
Created → Expired (do not deliver)
Approved, Declined, and Expired are terminal states. No further events follow.
Payment Created
Sent when the user initiates a payment session.
{
"event_type": "payment_created",
"timestamp": "1725481536",
"version": 1,
"event_payload": {
"payment_intent_id": "eda5d152-b75c-4f44-bb5e-65308518def9",
"payment_status": "Created",
"payment_method": "credit",
"amount": "2.99",
"currency": "EUR",
"item_title": "Bundle of Gold",
"item_desc": "1,000 Gold Coins",
"customer_id": "3bd8c4f5-d25e-4260-b401-c5ce4e8bcc9a"
}
}
Payment Approved
Sent when payment settles. Deliver the item to the user upon receiving this event.
{
"event_type": "payment_approved",
"timestamp": "1725481561",
"version": 1,
"event_payload": {
"payment_intent_id": "eda5d152-b75c-4f44-bb5e-65308518def9",
"payment_status": "Approved",
"payment_method": "credit",
"amount": "2.99",
"currency": "EUR",
"item_title": "Bundle of Gold",
"item_desc": "1,000 Gold Coins",
"customer_id": "3bd8c4f5-d25e-4260-b401-c5ce4e8bcc9a"
}
}
Payment Declined
Sent when the card is declined or a crypto transaction fails.
{
"event_type": "payment_declined",
"timestamp": "1725481601",
"version": 1,
"event_payload": {
"payment_intent_id": "b3e6a9bb-54f8-4fb5-8732-c3b334ec1a39",
"payment_status": "Declined",
"payment_method": "credit",
"amount": "2.99",
"currency": "EUR",
"item_title": "Bundle of Gold",
"item_desc": "1,000 Gold Coins",
"customer_id": "3bd8c4f5-d25e-4260-b401-c5ce4e8bcc9a"
}
}
If a card is declined before a 3DS challenge is presented, payment_declined fires immediately with no preceding payment_created event.
Payment Expired
Sent when the user is inactive for 30 minutes and the session times out.
{
"event_type": "payment_intent_expired",
"timestamp": "1725481601",
"version": 1,
"event_payload": {
"payment_intent_id": "afb168c8-b951-429f-9ccf-83bf3e03f6e3",
"payment_status": "Expired",
"payment_method": null,
"amount": "2.99",
"currency": "EUR",
"item_title": "Bundle of Gold",
"item_desc": "1,000 Gold Coins",
"customer_id": "3bd8c4f5-d25e-4260-b401-c5ce4e8bcc9a"
}
}