Documentation
Webhooks

Payloads

Field-level schema for Flo webhook payloads.

Payloads

Each Flo webhook message contains one normalized Flo event payload.

For concrete examples of delivered events, see Events.

Common Fields

{
  "eventId": "9f6f8b54-8e2d-4f15-8c8a-d7b6d9f41a00",
  "eventVersion": "v1",
  "objectType": "subscription",
  "eventType": "subscription.created",
  "user": {
    "id": "3b4d9a11-0ce8-4a88-9cb1-b4f43d03d2b7",
    "clientUserId": "user_123",
    "email": "user@example.com"
  },
  "subscription": {
    "uuid": "d8a2ad28-b98f-4cb6-bf46-f11cc0f5df16",
    "id": "plan_pro",
    "name": "Pro",
    "description": "Pro subscription",
    "startDate": "2026-04-10T00:00:00.000Z",
    "endDate": "2026-05-10T00:00:00.000Z",
    "state": "active",
    "quantity": 1,
    "currency": "USD",
    "amount": 29,
    "total": 29,
    "cancelAt": null,
    "canceledAt": null,
    "trialEnd": null,
    "pausedAt": null,
    "resumeAt": null,
    "metadata": null,
    "invoiceDetails": null
  }
}

Event Envelope

  • eventId: The Flo webhook delivery identifier for this endpoint delivery. It is not the upstream gateway event id, and automatic retries for the same delivery reuse the same eventId.
  • eventVersion: The webhook schema version.
  • objectType: The top-level Flo resource family for the event.
  • eventType: The normalized Flo event type.
  • user: The canonical user object for the event.
  • subscription: Present on subscription events. May also be present on invoice events as related enrichment.
  • item: Present on item events. May also be present on invoice events as related enrichment.
  • invoice: Present on invoice events.
  • payment: Present on payment authorisation lifecycle events.

Checkout metadata

The merged webhook DTOs expose an optional checkout snapshot on the event's primary object:

For create-time use and the record-by-record copy lifecycle, see preserve checkout context with metadata.

Event DTOPrimary objectField
WebhookPaymentEventDtopaymentpayment.checkoutMetadata
WebhookItemEventDtoitemitem.checkoutMetadata
WebhookSubscriptionEventDtosubscriptionsubscription.checkoutMetadata
WebhookInvoiceEventDtoinvoiceinvoice.checkoutMetadata

The field type is Record<string, string> when present. When the source snapshot is absent, including an omitted or null create value, the webhook key is omitted. An explicit {} snapshot is preserved as {}.

checkoutMetadata is never an event-root field. On invoice events, it appears only on invoice; related root-level subscription and item enrichment keep their existing metadata fields and do not duplicate the checkout snapshot.

Payment primary object

{
  "eventId": "9f6f8b54-8e2d-4f15-8c8a-d7b6d9f41a01",
  "eventVersion": "v1",
  "objectType": "payment",
  "eventType": "payment.authorized",
  "user": {
    "id": "3b4d9a11-0ce8-4a88-9cb1-b4f43d03d2b7",
    "clientUserId": "buyer-123",
    "email": "buyer@example.com"
  },
  "payment": {
    "id": "3c54b6ac-7ad5-4e56-9c2c-5a80c2ef40d0",
    "checkoutSessionId": "fd4475e4-dc19-4439-b830-c9c8f67a35e7",
    "amount": 49.99,
    "currency": "USD",
    "status": "authorized",
    "authorizationExpiresAt": "2026-08-26T14:30:00.000Z",
    "authorizationVoidReason": null,
    "checkoutMetadata": {
      "merchantOrderId": "order-123"
    }
  }
}

Item primary object

{
  "eventId": "9f6f8b54-8e2d-4f15-8c8a-d7b6d9f41a02",
  "eventVersion": "v1",
  "objectType": "item",
  "eventType": "item.purchased",
  "user": {
    "id": "3b4d9a11-0ce8-4a88-9cb1-b4f43d03d2b7",
    "clientUserId": "buyer-123",
    "email": "buyer@example.com"
  },
  "item": {
    "uuid": "da634f98-8748-4e90-bb5d-f3df74003b57",
    "id": "starter",
    "name": "Starter",
    "description": "Starter access",
    "quantity": 1,
    "currency": "USD",
    "amount": 49.99,
    "total": 49.99,
    "invoiceState": "paid",
    "metadata": {
      "catalogReference": "starter"
    },
    "checkoutMetadata": {
      "merchantOrderId": "order-123"
    },
    "invoiceDetails": null
  }
}

Subscription primary object

{
  "eventId": "9f6f8b54-8e2d-4f15-8c8a-d7b6d9f41a03",
  "eventVersion": "v1",
  "objectType": "subscription",
  "eventType": "subscription.created",
  "user": {
    "id": "3b4d9a11-0ce8-4a88-9cb1-b4f43d03d2b7",
    "clientUserId": "buyer-123",
    "email": "buyer@example.com"
  },
  "subscription": {
    "uuid": "d8a2ad28-b98f-4cb6-bf46-f11cc0f5df16",
    "id": "plan_starter",
    "name": "Starter",
    "description": "Starter subscription",
    "startDate": "2026-08-25T00:00:00.000Z",
    "endDate": "2026-09-25T00:00:00.000Z",
    "state": "active",
    "quantity": 1,
    "currency": "USD",
    "amount": 49.99,
    "total": 49.99,
    "cancelAt": null,
    "canceledAt": null,
    "trialEnd": null,
    "pausedAt": null,
    "resumeAt": null,
    "metadata": {
      "catalogReference": "plan_starter"
    },
    "checkoutMetadata": {
      "merchantOrderId": "order-123"
    },
    "invoiceDetails": null
  }
}

Invoice primary object

{
  "eventId": "9f6f8b54-8e2d-4f15-8c8a-d7b6d9f41a04",
  "eventVersion": "v1",
  "objectType": "invoice",
  "eventType": "invoice.paid",
  "user": {
    "id": "3b4d9a11-0ce8-4a88-9cb1-b4f43d03d2b7",
    "clientUserId": "buyer-123",
    "email": "buyer@example.com"
  },
  "invoice": {
    "id": "7bb5ce14-59e0-41ee-8fb7-84d6967cdb68",
    "invoiceNumber": "FLO-1001",
    "state": "paid",
    "billingReason": "subscription_cycle",
    "currency": "USD",
    "amountDue": 49.99,
    "amountPaid": 49.99,
    "subtotal": 49.99,
    "total": 49.99,
    "paidAt": "2026-08-25T10:20:00.000Z",
    "hostedInvoiceUrl": "https://merchant.example/invoice",
    "invoicePdfUrl": "https://merchant.example/invoice.pdf",
    "invoiceDetails": null,
    "checkoutMetadata": {
      "merchantOrderId": "order-123"
    }
  },
  "subscription": {
    "uuid": "d8a2ad28-b98f-4cb6-bf46-f11cc0f5df16",
    "id": "plan_starter",
    "name": "Starter",
    "description": "Starter subscription",
    "quantity": 1,
    "currency": "USD",
    "amount": 49.99,
    "total": 49.99,
    "metadata": {
      "catalogReference": "plan_starter"
    }
  },
  "item": {
    "uuid": "da634f98-8748-4e90-bb5d-f3df74003b57",
    "id": "starter",
    "name": "Starter",
    "description": "Starter access",
    "quantity": 1,
    "currency": "USD",
    "amount": 49.99,
    "total": 49.99,
    "metadata": {
      "catalogReference": "starter"
    }
  }
}

Correlate after deduplication

Use the top-level eventId to deduplicate webhook delivery. Payment and invoice events use the primary entity id as the FloPay record identifier. Item and subscription events also expose a provider catalog id, so use their primary entity uuid as the FloPay record identifier. Use checkoutMetadata.merchantOrderId only for business correlation after the event has been reserved successfully.

type CheckoutMetadata = Record<string, string>;
type IdPrimary = { id: string; checkoutMetadata?: CheckoutMetadata };
type UuidPrimary = { uuid: string; checkoutMetadata?: CheckoutMetadata };
type FloEvent =
  | { eventId: string; objectType: 'payment'; payment: IdPrimary }
  | { eventId: string; objectType: 'item'; item: UuidPrimary }
  | { eventId: string; objectType: 'subscription'; subscription: UuidPrimary }
  | { eventId: string; objectType: 'invoice'; invoice: IdPrimary };

declare function reserveEvent(eventId: string): Promise<boolean>;
declare function correlateOrder(
  merchantOrderId: string,
  objectType: FloEvent['objectType'],
  entityId: string,
): Promise<void>;

export async function handleFloEvent(event: FloEvent) {
  if (!(await reserveEvent(event.eventId))) return;

  const { primary, entityId } =
    event.objectType === 'payment'
      ? { primary: event.payment, entityId: event.payment.id }
      : event.objectType === 'item'
        ? { primary: event.item, entityId: event.item.uuid }
        : event.objectType === 'subscription'
          ? { primary: event.subscription, entityId: event.subscription.uuid }
          : { primary: event.invoice, entityId: event.invoice.id };
  const merchantOrderId = primary.checkoutMetadata?.merchantOrderId;
  if (!merchantOrderId) return;

  await correlateOrder(merchantOrderId, event.objectType, entityId);
}

Compatibility Fields

New integrations should build against user, eventType, objectType, and the explicit resource root (subscription, item, invoice, or payment). On invoice events, root-level subscription and item are enrichment fields. Flo includes them when the invoice points at a linked local transaction that resolves to a checkout subscription and/or checkout item.

User Object

{
  "id": "3b4d9a11-0ce8-4a88-9cb1-b4f43d03d2b7",
  "clientUserId": "user_123",
  "email": "user@example.com"
}

Payment Payload

Payment authorisation events include the provider-neutral payment state. id is FloPay's payment UUID for the trusted-server capture and cancellation operations; it is not a provider intent id.

{
  "id": "3c54b6ac-7ad5-4e56-9c2c-5a80c2ef40d0",
  "checkoutSessionId": "fd4475e4-dc19-4439-b830-c9c8f67a35e7",
  "amount": 49.99,
  "currency": "GBP",
  "status": "authorized",
  "authorizationExpiresAt": "2026-08-11T14:30:00.000Z",
  "authorizationVoidReason": null
}
  • checkoutSessionId: FloPay checkout session associated with the payment.
  • amount / currency: Full authorised amount in the payment currency.
  • status: Canonical payment state. Authorisation events use authorized or voided; see the payment state reference.
  • authorizationExpiresAt: Provider-derived capture deadline for an active hold.
  • authorizationVoidReason: merchant_requested, expired, or provider_canceled after a hold becomes voided; otherwise null.

Subscription Payload

  • uuid: Flo's checkout subscription UUID when the event is linked to a checkout session.
  • id: The checkout subscription's provider plan id.
  • name / description: The checkout subscription's provider-backed display fields.
{
  "uuid": "d8a2ad28-b98f-4cb6-bf46-f11cc0f5df16",
  "id": "plan_pro",
  "name": "Pro",
  "description": "Pro subscription",
  "startDate": "2026-04-10T00:00:00.000Z",
  "endDate": "2026-05-10T00:00:00.000Z",
  "state": "active",
  "quantity": 1,
  "currency": "USD",
  "amount": 29,
  "total": 29,
  "cancelAt": null,
  "canceledAt": null,
  "trialEnd": null,
  "pausedAt": null,
  "resumeAt": null,
  "metadata": null,
  "invoiceDetails": null
}

Automated subscription recovery does not add campaign, round, or recovered fields to webhook payloads. Use the existing event sequence to follow lifecycle state, then use subscription reporting for recovered attribution. During a campaign, subscription.state moves through past_due, active after confirmed recovery, or expired after terminal exhaustion. The paid-through dates in startDate and endDate remain unchanged on failed attempts.

Item Payload

  • uuid: Flo's checkout item UUID when the event is linked to a checkout session.
  • id: The checkout item's provider item id.
  • name / description: The checkout item's provider-backed display fields.
  • amount / currency: The value and currency of the item event. On item.charged_back, the amount is money withdrawn by the provider dispute.
{
  "uuid": "da634f98-8748-4e90-bb5d-f3df74003b57",
  "id": "item_123",
  "name": "Product",
  "description": "Product description",
  "quantity": 1,
  "currency": "USD",
  "amount": 29,
  "total": 29,
  "invoiceState": "paid",
  "metadata": null,
  "invoiceDetails": null
}

For item money-movement events, use eventType as the primary branch and invoiceDetails.transactionType as the financial discriminator:

EventinvoiceDetails.transactionTypeMeaning
item.refundedrefundA genuine merchant or administrative refund.
item.charged_backchargebackMoney was withdrawn because of a provider dispute.

The chargeback case keeps the existing item and invoiceDetails field shapes. For example:

{
  "eventType": "item.charged_back",
  "item": {
    "amount": 29,
    "currency": "USD",
    "invoiceState": "voided",
    "invoiceDetails": {
      "transactionType": "chargeback",
      "transactionId": "dp_123",
      "chargeId": "ch_123",
      "currency": "USD"
    }
  }
}

A genuine refund carries invoiceDetails.transactionType: "refund". Do not infer a chargeback from item.refunded or from provider-specific trace fields.

Invoice Payload

{
  "id": "7bb5ce14-59e0-41ee-8fb7-84d6967cdb68",
  "invoiceNumber": "FLO-1001",
  "state": "pastDue",
  "billingReason": "subscription_cycle",
  "currency": "GBP",
  "amountDue": 27,
  "amountPaid": 0,
  "subtotal": 27,
  "total": 27,
  "paidAt": null,
  "hostedInvoiceUrl": "https://example.com/invoice",
  "invoicePdfUrl": "https://example.com/invoice.pdf",
  "invoiceDetails": null
}

Invoice Event Enrichment

  • On invoice events, Flo may include root-level subscription and/or item alongside invoice.
  • These related objects are resolved from the linked local transaction.
  • An invoice event can include either, both, or neither of these related objects.
{
  "eventId": "9f6f8b54-8e2d-4f15-8c8a-d7b6d9f41a00",
  "eventVersion": "v1",
  "objectType": "invoice",
  "eventType": "invoice.paid",
  "user": {
    "id": "3b4d9a11-0ce8-4a88-9cb1-b4f43d03d2b7",
    "clientUserId": "user_123",
    "email": "user@example.com"
  },
  "invoice": {
    "id": "7bb5ce14-59e0-41ee-8fb7-84d6967cdb68",
    "invoiceNumber": "FLO-1001",
    "state": "paid",
    "billingReason": "subscription_cycle",
    "currency": "GBP",
    "amountDue": 27,
    "amountPaid": 27,
    "subtotal": 27,
    "total": 27,
    "paidAt": "2026-04-11T10:20:00.000Z",
    "hostedInvoiceUrl": "https://example.com/invoice",
    "invoicePdfUrl": "https://example.com/invoice.pdf",
    "invoiceDetails": {
      "gatewayEventType": "invoice.paid",
      "invoiceId": "in_123",
      "invoiceStatus": "paid",
      "billingReason": "subscription_cycle",
      "transactionId": "pi_123",
      "currency": "GBP",
      "amountDue": 27,
      "amountPaid": 27,
      "total": 27
    }
  },
  "subscription": {
    "uuid": "d8a2ad28-b98f-4cb6-bf46-f11cc0f5df16",
    "id": "hub-membership",
    "name": "Hub Membership",
    "description": "Hub membership billed every four weeks",
    "quantity": 1,
    "currency": "GBP",
    "amount": 22,
    "total": 22,
    "metadata": {
      "source": "checkout-subscription"
    }
  },
  "item": {
    "uuid": "da634f98-8748-4e90-bb5d-f3df74003b57",
    "id": "priority-support",
    "name": "Priority Support",
    "description": "Priority support add-on",
    "quantity": 1,
    "currency": "GBP",
    "amount": 5,
    "total": 5,
    "metadata": {
      "source": "checkout-item"
    }
  }
}
{
  "uuid": "d8a2ad28-b98f-4cb6-bf46-f11cc0f5df16",
  "id": "hub-membership",
  "name": "Hub Membership",
  "description": "Hub membership billed every four weeks",
  "quantity": 1,
  "currency": "GBP",
  "amount": 22,
  "total": 22,
  "metadata": {
    "source": "checkout-subscription"
  }
}
{
  "uuid": "da634f98-8748-4e90-bb5d-f3df74003b57",
  "id": "priority-support",
  "name": "Priority Support",
  "description": "Priority support add-on",
  "quantity": 1,
  "currency": "GBP",
  "amount": 5,
  "total": 5,
  "metadata": {
    "source": "checkout-item"
  }
}

Invoice Details

invoiceDetails is optional on subscription, item, and invoice events. When present, it includes invoice and payment context associated with the Flo event.

Most integrations should build against eventType, objectType, and the explicit resource root (subscription, item, or invoice). invoiceDetails.gatewayEventType is additional trace context and should not be the primary switch for business logic.

{
  "gatewayEventType": "invoice.paid",
  "invoiceId": "in_123",
  "invoiceNumber": "FLO-1001",
  "invoiceStatus": "paid",
  "billingReason": "subscription_cycle",
  "invoicePaymentId": "inpay_123",
  "transactionId": "pi_123",
  "transactionType": "payment_intent",
  "paymentIntentId": "pi_123",
  "chargeId": "ch_123",
  "paymentRecordId": null,
  "receiptNumber": null,
  "hostedInvoiceUrl": "https://...",
  "invoicePdfUrl": "https://...",
  "paidAt": "2026-04-10T12:00:00.000Z",
  "currency": "USD",
  "amountDue": 29,
  "amountPaid": 29,
  "subtotal": 29,
  "total": 29
}

On this page