Webhook events and payloads
July 30, 2026
Reference for FlowAlp Pay webhook events: transaction, subscription and payout payloads with JSON examples, plus tips for idempotent processing.
FlowAlp Pay delivers three kinds of webhook events to the URL you configured: transaction events, subscription events and payout events. This page documents the payload of each and how your endpoint should acknowledge them.
Delivery basics
- Every event is an HTTP
POSTto your webhook URL, encoded as JSON or form data depending on your configuration. - All amounts are integers in the currency's smallest unit:
8925means CHF 89.25. - Transaction and subscription events fire on every status change; payout events fire once a payout is actually processed.
- Identify your order through
referenceId— or through the payment link ID inside theinvoicedata.
Transaction events
The payload wraps a single transaction object. The most important fields:
| Field | Type | Description |
|---|---|---|
| id / uuid | int / string | Internal ID and public ID of the transaction |
| status | string | Transaction status, see table below |
| amount | int | Processed amount in minor units |
| referenceId | string | Your merchant order reference |
| time | string | Creation timestamp in ISO 8601 format |
| mode | string | TEST or LIVE |
| psp / pspId | string / int | Identifiers of the processing payment provider |
| type | string | E-Commerce, POS-Terminal or Tap to Pay |
| refundable / partiallyRefundable | bool | Whether a (partial) refund is possible |
| invoice | object | Order data: currency, products, originalAmount, refundedAmount, custom_fields, … |
| contact | object or null | Customer and delivery address data |
| payment | object | Means of payment, e.g. brand and wallet |
| subscription | object or null | Present when the transaction belongs to a subscription |
| instance | object | Your merchant account: name and uuid |
| payoutUuid | string or null | UUID of the payout that contains this transaction |
| preAuthorizationId | int | Only for tokenizations/charges: ID of the tokenization source |
| originalTransactionId / -Uuid | int / string | Only on refund transactions: the charged original transaction |
Note that the currency is part of the invoice object (invoice.currency), not a top-level field.
| Status | Meaning |
|---|---|
waiting | Order placed, payment not completed yet |
confirmed | Successful payment |
cancelled | Payment aborted by the customer |
declined | Failed 3-D Secure or declined by the issuer bank |
authorized | Successful tokenization |
reserved | Successful reservation (pre-authorization) |
refunded | Full refund |
partially-refunded | Partial refund |
refund_pending | Refund is being processed |
chargeback | The cardholder reclaimed the money |
disputed | A dispute has been opened for this transaction |
error | An issue occurred during the payment process |
expired | Payment aborted due to inactivity |
{
"transaction": {
"id": 1234,
"uuid": "1122aabb",
"status": "confirmed",
"amount": 8925,
"referenceId": "ORDER-975382",
"time": "2026-07-30T09:36:07+00:00",
"lang": "de",
"psp": "Native_PSP",
"pspId": 44,
"mode": "TEST",
"type": "E-Commerce",
"refundable": true,
"partiallyRefundable": true,
"instance": {
"name": "demo-shop",
"uuid": "aabb1122"
},
"metadata": {},
"invoice": {
"number": "IV_2041",
"currency": "CHF",
"test": 1,
"referenceId": "ORDER-975382",
"originalAmount": 8925,
"refundedAmount": 0,
"shippingAmount": null,
"paymentLink": null,
"paymentRequestId": null,
"products": [
{
"name": "Season pass",
"quantity": 1,
"price": 8925,
"sku": null,
"vatRate": "8.1",
"description": ""
}
],
"discount": {
"code": null,
"amount": 0,
"percentage": null
},
"custom_fields": [
{
"type": "email",
"name": "E-Mail",
"value": "buyer@example.com"
}
]
},
"contact": {
"id": 1234,
"uuid": "aabb1122",
"firstname": "Jane",
"lastname": "Doe",
"company": "Alpina Sport AG",
"street": "Burgstrasse 20",
"zip": "3600",
"place": "Thun",
"country": "Switzerland",
"countryISO": "CH",
"phone": "0335500010",
"email": "buyer@example.com"
},
"payment": {
"brand": "visa",
"wallet": null
},
"subscription": null,
"payoutUuid": null
}
}Subscription events
Subscription events fire whenever the state of a recurring billing relationship changes. Fields: id, uuid, status, start, end, valid_until (the next pay date), paymentInterval (a duration such as P1M, following the PHP DateInterval specification), plus invoice and contact objects.
| Status | Meaning |
|---|---|
active | The subscription is active, further charges will follow |
failed | Creation failed, or a charge and its retry both failed |
cancelled | Cancelled by the merchant with immediate effect — no more charges |
in_notice | Cancelled by the shopper before the end date; the remaining charges still follow |
overdue | A charge failed and is being retried; a second failure moves it to failed |
{
"id": 5678,
"uuid": "ccdd3344",
"status": "active",
"start": "2026-01-01",
"end": null,
"valid_until": "2026-12-31",
"paymentInterval": "P1M",
"invoice": {
"currency": "CHF",
"referenceId": "SUB-2026-042",
"originalAmount": 1990,
"refundedAmount": 0,
"products": [
{
"name": "Monthly membership",
"price": 1990,
"quantity": 1,
"sku": null,
"vatRate": "8.1",
"description": ""
}
],
"discount": {
"code": null,
"amount": 0,
"percentage": null
},
"custom_fields": []
},
"contact": {
"id": 1234,
"uuid": "aabb1122",
"firstname": "Jane",
"lastname": "Doe",
"email": "buyer@example.com"
}
}Payout events
Payout events describe money leaving your FlowAlp Pay balance towards your bank account. Key fields: uuid, mode, object (always payout), amount, total_fees, currency (ISO 4217), date, statement (bank statement text), payer (initiator of the payout), status, destination (for example a bank account with IBAN), transfers (the contained transactions and fees), merchant and is_manual_payout.
| Status | Meaning |
|---|---|
processing | The payout file has been handed over to the bank |
sent | Payout transmitted successfully to the bank |
failed | Payout failed and was returned by the bank |
No webhooks are sent for the internal payout states initiated, pending and under-review — the first event you receive is processing.
{
"uuid": "AABB1122",
"mode": "LIVE",
"object": "payout",
"amount": 29390,
"total_fees": 610,
"currency": "CHF",
"date": "2026-07-28",
"statement": "Alpina Sport AG Thun",
"status": "sent",
"is_manual_payout": false,
"destination": {
"type": "bank_account",
"iban": "CH00 0000 0000 0000 0000 0",
"account_holder": "Alpina Sport AG"
},
"transfers": [
{
"type": "payout-fee",
"amount": -10,
"date_time": "2026-07-28T05:00:00+00:00",
"items": [
{ "type": "payout-fee", "amount": -10 }
],
"transaction": {}
},
{
"type": "transaction",
"amount": 29400,
"date_time": "2026-07-27T13:44:30+00:00",
"items": [
{ "type": "transaction", "amount": 30000 },
{ "type": "transaction-fee", "amount": -600 }
],
"transaction": {
"type": "transaction",
"uuid": "1122aabb",
"amount": 30000,
"currency": "CHF",
"reference_id": "ORDER-975382"
}
}
]
}Acknowledge with HTTP 200
Respond with a 2xx status within 20 seconds. Any other response — or a timeout — counts as a failed delivery and is retried according to your retry configuration. Keep the synchronous part minimal:
<?php
$rawBody = file_get_contents('php://input');
// 1. Verify the signature first (see the signature verification guide)
// 2. Queue the raw event for asynchronous processing
http_response_code(200);Process events idempotently
- Retries mean duplicates: deduplicate on transaction
id(oruuid) plusstatusbefore applying changes. - Deliveries can arrive out of order — ignore an event if your stored state is already final (e.g.
refundedafterconfirmed). - Store the raw payload and your processing outcome so incidents can be replayed and audited.
- Never process an event before verifying its signature.
Next step: secure your endpoint with webhook signature verification before you go live.