FlowAlp

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 POST to your webhook URL, encoded as JSON or form data depending on your configuration.
  • All amounts are integers in the currency's smallest unit: 8925 means 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 the invoice data.

Transaction events

The payload wraps a single transaction object. The most important fields:

FieldTypeDescription
id / uuidint / stringInternal ID and public ID of the transaction
statusstringTransaction status, see table below
amountintProcessed amount in minor units
referenceIdstringYour merchant order reference
timestringCreation timestamp in ISO 8601 format
modestringTEST or LIVE
psp / pspIdstring / intIdentifiers of the processing payment provider
typestringE-Commerce, POS-Terminal or Tap to Pay
refundable / partiallyRefundableboolWhether a (partial) refund is possible
invoiceobjectOrder data: currency, products, originalAmount, refundedAmount, custom_fields, …
contactobject or nullCustomer and delivery address data
paymentobjectMeans of payment, e.g. brand and wallet
subscriptionobject or nullPresent when the transaction belongs to a subscription
instanceobjectYour merchant account: name and uuid
payoutUuidstring or nullUUID of the payout that contains this transaction
preAuthorizationIdintOnly for tokenizations/charges: ID of the tokenization source
originalTransactionId / -Uuidint / stringOnly on refund transactions: the charged original transaction

Note that the currency is part of the invoice object (invoice.currency), not a top-level field.

StatusMeaning
waitingOrder placed, payment not completed yet
confirmedSuccessful payment
cancelledPayment aborted by the customer
declinedFailed 3-D Secure or declined by the issuer bank
authorizedSuccessful tokenization
reservedSuccessful reservation (pre-authorization)
refundedFull refund
partially-refundedPartial refund
refund_pendingRefund is being processed
chargebackThe cardholder reclaimed the money
disputedA dispute has been opened for this transaction
errorAn issue occurred during the payment process
expiredPayment aborted due to inactivity
Example: confirmed transactionJSON
{
  "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.

StatusMeaning
activeThe subscription is active, further charges will follow
failedCreation failed, or a charge and its retry both failed
cancelledCancelled by the merchant with immediate effect — no more charges
in_noticeCancelled by the shopper before the end date; the remaining charges still follow
overdueA charge failed and is being retried; a second failure moves it to failed
Example: active subscriptionJSON
{
  "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.

StatusMeaning
processingThe payout file has been handed over to the bank
sentPayout transmitted successfully to the bank
failedPayout 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.

Example: sent payout (shortened)JSON
{
  "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:

Minimal acknowledgementPHP
<?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 (or uuid) plus status before applying changes.
  • Deliveries can arrive out of order — ignore an event if your stored state is already final (e.g. refunded after confirmed).
  • 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.