Fitssey Webhooks (v1)

Introduction

Webhooks let your application react to events happening in your Studio in real time. Instead of polling the Fitssey API, you register a callback URL and Fitssey sends an HTTP POST request to it every time a subscribed event occurs.

Webhooks can be configured in your Studio > Integrations > Webhooks.

This document covers webhooks. To read or change data in your Studio, see the API documentation.

Delivery

Each webhook is delivered as an HTTP POST request with the following headers:

Header Value
Content-Type application/json
User-Agent lightenbody/Fitssey (+https://app.fitssey.com/{uuid})
Fitssey-Signature Signature of the request. Sent when the webhook uses the Signature authentication method, see Signature.
Fitssey-Secret The authentication key. Sent when the webhook uses the Secret header authentication method, see Secret header.

The request body is a JSON array containing a single event envelope:

[
  {
    "guid": "8F2B6C1D-4E7A-4B3C-9D21-5A6E7F8C9B0A",
    "webhookGuid": "3C9E1A7B-2D4F-4E8A-B6C0-1F5D9A2E7B43",
    "event": "event.shopping_cart.payment_status",
    "data": { ... },
    "firedAt": "2026-10-20T10:15:00+0200"
  }
]

Verifying requests

Your callback URL accepts requests from anyone who knows it, so always verify that a request was really sent by Fitssey before acting on it. Each webhook has an authentication key and one of two authentication methods, selected in Studio > Integrations > Webhooks:

Method Header Security
Signature (recommended) Fitssey-Signature Strong. The key is never sent; each request carries a signature valid only for its body and time.
Secret header Fitssey-Secret Basic. The key itself is sent with every request; anyone who sees one request can forge others.

Signature

Every request is signed with the authentication key, so you can verify that it was sent by Fitssey and was not modified on the way. The key itself is never sent.

The Fitssey-Signature header contains a Unix timestamp and an HMAC-SHA256 signature:

Fitssey-Signature: t=1760954100,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

To verify the request:

  1. Read the raw request body exactly as received, before parsing the JSON - any re-serialization changes the bytes and breaks the signature.
  2. Split the header into t (timestamp) and v1 (signature).
  3. Reject the request if t differs from the current time by more than 5 minutes to protect against replay attacks.
  4. Compute the HMAC-SHA256 of the string {t}.{rawBody} using your authentication key as the key and encode it as lowercase hex.
  5. Compare the result with v1 using a constant-time comparison.

The v1 prefix identifies the signature scheme. New schemes may be added in the future as additional vN entries, so ignore any you do not recognize.

PHP

$payload = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_FITSSEY_SIGNATURE'] ?? ''), $signature);

if(!isset($signature['t'], $signature['v1']) || 300 < abs(time() - (int) $signature['t']))
{
    http_response_code(400);
    exit;
}

$expected = hash_hmac('sha256', $signature['t'] . '.' . $payload, $secret);

if(!hash_equals($expected, $signature['v1']))
{
    http_response_code(401);
    exit;
}

$events = json_decode($payload, true);

Node.js (Express)

const crypto = require('crypto');

app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
    const signature = Object.fromEntries((req.get('Fitssey-Signature') || '').split(',').map(part => part.split('=')));
    if(!signature.t || !signature.v1 || Math.abs(Date.now() / 1000 - signature.t) > 300) return res.sendStatus(400);

    const expected = crypto.createHmac('sha256', secret).update(`${signature.t}.${req.body}`).digest('hex');
    if(expected.length !== signature.v1.length || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature.v1))) return res.sendStatus(401);

    const events = JSON.parse(req.body);
    res.sendStatus(200);
});

Secret header

The authentication key is sent as-is in the Fitssey-Secret header. Compare it with your key using a constant-time comparison and reject the request when it is missing or does not match:

if(!hash_equals($secret, $_SERVER['HTTP_FITSSEY_SECRET'] ?? ''))
{
    http_response_code(401);
    exit;
}

This method is meant for tools that cannot compute signatures, such as no-code automation platforms. Because the key travels with every request, it does not protect the body from modification, does not prevent replays and exposes the key to anything that logs the request. Use Signature whenever you can.

Handling

Responding

Respond with any 2xx status as quickly as possible and do the heavy processing asynchronously. Redirects are not followed.

Callback URL

The callback URL must be a public https address. URLs resolving to private or reserved IP ranges are rejected.

Timeouts and suspension

The connection timeout defaults to 5 seconds. Every failed delivery increases the webhook's fail counter; once it reaches the limit (5 by default) the webhook is suspended and stops receiving events until it is re-enabled. A successful delivery resets the counter. Failed deliveries are not retried.

Idempotency

The same change may be reported more than once, so processing a webhook twice must have the same effect as processing it once. Each event documents which fields identify a repeated change. The envelope guid is unique per event and is useful for logging, but two notifications about the same change carry different guid values, so it cannot be used to detect them.

Client

Client created Webhook

Event name: event.client.created

Fired when a new client is created - added in BackOffice, through the API, or when a client signs up in FrontOffice.

Request Body schema: application/json
Array
guid
string

Unique identifier of this delivery.

webhookGuid
string

Identifier of the webhook (Studio > Integrations > Webhooks) this delivery was sent for. Useful when several webhooks point to the same callback URL.

event
string
Value: event.client.created

Name of the event.

object (User)

Event payload. Its shape depends on the event.

firedAt
string <date-time>

Date and time when the event was fired (ISO 8601).

Responses

Request samples

Content type
application/json
[
  • {
    • "guid": "8F2B6C1D-4E7A-4B3C-9D21-5A6E7F8C9B0A",
    • "webhookGuid": "3C9E1A7B-2D4F-4E8A-B6C0-1F5D9A2E7B43",
    • "event": "event.client.created",
    • "data": {
      • "emailAddress": "[email protected]",
      • "contactEmailAddress": "[email protected]",
      • "firstName": "Maria",
      • "lastName": "Dvorak",
      • "sex": 1,
      • "suspensionType": 1,
      • "isSuspended": false,
      • "suspendedAt": "2019-09-10T15:31:47+02:00",
      • "suspensionExpiresAt": "2019-09-17T15:31:47+02:00",
      • "suspensionReason": "Suspended due to many cancellations",
      • "isLoginEnabled": true,
      • "activatedAt": "2019-09-01T12:02:35+02:00",
      • "lastSeenAt": "2019-09-03T09:54:12+02:00",
      • "client": {
        • "uuid": "1000072",
        • "clientFields": {
          • "8D2A6C1E-4F3B-4A7E-9C21-5B6E0F3D7A12": 1234,
          • "C47E9B05-1A2D-4E6F-8B3C-9D0E2F4A6B81": "ABCD"
          },
        • "agreements": null,
        • "birthdateAt": "1980-01-19T23:00:00+01:00",
        • "lastActiveAt": "2019-09-03T08:06:23+02:00",
        • "user": { },
        • "address": {
          • "street": "Sunny street 77a",
          • "postalCode": "1234AB",
          • "state": "Noord-Holland",
          • "city": "Amsterdam",
          • "countryCode": "NL",
          • "createdAt": "2019-05-12T12:45:47+02:00",
          • "modifiedAt": "2023-09-12T19:18:23+02:00"
          },
        • "phone": {
          • "primaryPhone": "+48111222333",
          • "createdAt": "2019-05-12T12:45:47+02:00",
          • "modifiedAt": "2023-09-12T19:18:23+02:00"
          },
        • "clientNotes": [
          • {
            • "type": "private",
            • "note": "Client prefers morning classes.",
            • "color": "#FF5733",
            • "expiresAt": null,
            • "guid": "364F59AB-5B43-4D02-989E-AE2811422709",
            • "createdAt": "2019-05-12T12:45:47+02:00",
            • "modifiedAt": "2023-09-12T19:18:23+02:00"
            }
          ],
        • "tags": [
          • {
            • "name": "VIP",
            • "color": "#FF5733",
            • "guid": "364F59AB-5B43-4D02-989E-AE2811422709",
            • "createdAt": "2019-05-12T12:45:47+02:00",
            • "modifiedAt": "2023-09-12T19:18:23+02:00"
            }
          ],
        • "age": 34
        },
      • "guid": "364F59AB-5B43-4D02-989E-AE2811422709",
      • "createdAt": "2019-05-12T12:45:47+02:00",
      • "modifiedAt": "2023-09-12T19:18:23+02:00"
      },
    • "firedAt": "2026-10-20T10:15:00+0200"
    }
]

Client updated Webhook

Event name: event.client.updated

Fired when client's data changes - edited in BackOffice or through the API, or updated by the client in FrontOffice.

Request Body schema: application/json
Array
guid
string

Unique identifier of this delivery.

webhookGuid
string

Identifier of the webhook (Studio > Integrations > Webhooks) this delivery was sent for. Useful when several webhooks point to the same callback URL.

event
string
Value: event.client.updated

Name of the event.

object (User)

Event payload. Its shape depends on the event.

firedAt
string <date-time>

Date and time when the event was fired (ISO 8601).

Responses

Request samples

Content type
application/json
[
  • {
    • "guid": "8F2B6C1D-4E7A-4B3C-9D21-5A6E7F8C9B0A",
    • "webhookGuid": "3C9E1A7B-2D4F-4E8A-B6C0-1F5D9A2E7B43",
    • "event": "event.client.updated",
    • "data": {
      • "emailAddress": "[email protected]",
      • "contactEmailAddress": "[email protected]",
      • "firstName": "Maria",
      • "lastName": "Dvorak",
      • "sex": 1,
      • "suspensionType": 1,
      • "isSuspended": false,
      • "suspendedAt": "2019-09-10T15:31:47+02:00",
      • "suspensionExpiresAt": "2019-09-17T15:31:47+02:00",
      • "suspensionReason": "Suspended due to many cancellations",
      • "isLoginEnabled": true,
      • "activatedAt": "2019-09-01T12:02:35+02:00",
      • "lastSeenAt": "2019-09-03T09:54:12+02:00",
      • "client": {
        • "uuid": "1000072",
        • "clientFields": {
          • "8D2A6C1E-4F3B-4A7E-9C21-5B6E0F3D7A12": 1234,
          • "C47E9B05-1A2D-4E6F-8B3C-9D0E2F4A6B81": "ABCD"
          },
        • "agreements": null,
        • "birthdateAt": "1980-01-19T23:00:00+01:00",
        • "lastActiveAt": "2019-09-03T08:06:23+02:00",
        • "user": { },
        • "address": {
          • "street": "Sunny street 77a",
          • "postalCode": "1234AB",
          • "state": "Noord-Holland",
          • "city": "Amsterdam",
          • "countryCode": "NL",
          • "createdAt": "2019-05-12T12:45:47+02:00",
          • "modifiedAt": "2023-09-12T19:18:23+02:00"
          },
        • "phone": {
          • "primaryPhone": "+48111222333",
          • "createdAt": "2019-05-12T12:45:47+02:00",
          • "modifiedAt": "2023-09-12T19:18:23+02:00"
          },
        • "clientNotes": [
          • {
            • "type": "private",
            • "note": "Client prefers morning classes.",
            • "color": "#FF5733",
            • "expiresAt": null,
            • "guid": "364F59AB-5B43-4D02-989E-AE2811422709",
            • "createdAt": "2019-05-12T12:45:47+02:00",
            • "modifiedAt": "2023-09-12T19:18:23+02:00"
            }
          ],
        • "tags": [
          • {
            • "name": "VIP",
            • "color": "#FF5733",
            • "guid": "364F59AB-5B43-4D02-989E-AE2811422709",
            • "createdAt": "2019-05-12T12:45:47+02:00",
            • "modifiedAt": "2023-09-12T19:18:23+02:00"
            }
          ],
        • "age": 34
        },
      • "guid": "364F59AB-5B43-4D02-989E-AE2811422709",
      • "createdAt": "2019-05-12T12:45:47+02:00",
      • "modifiedAt": "2023-09-12T19:18:23+02:00"
      },
    • "firedAt": "2026-10-20T10:15:00+0200"
    }
]

Client deleted Webhook

Event name: event.client.deleted

Fired when a client is deleted. data.isDeleted is true and data.deletedAt holds the date of the deletion.

Request Body schema: application/json
Array
guid
string

Unique identifier of this delivery.

webhookGuid
string

Identifier of the webhook (Studio > Integrations > Webhooks) this delivery was sent for. Useful when several webhooks point to the same callback URL.

event
string
Value: event.client.deleted

Name of the event.

object (User)

Event payload. Its shape depends on the event.

firedAt
string <date-time>

Date and time when the event was fired (ISO 8601).

Responses

Request samples

Content type
application/json
[
  • {
    • "guid": "8F2B6C1D-4E7A-4B3C-9D21-5A6E7F8C9B0A",
    • "webhookGuid": "3C9E1A7B-2D4F-4E8A-B6C0-1F5D9A2E7B43",
    • "event": "event.client.deleted",
    • "data": {
      • "emailAddress": "[email protected]",
      • "contactEmailAddress": "[email protected]",
      • "firstName": "Maria",
      • "lastName": "Dvorak",
      • "sex": 1,
      • "suspensionType": 1,
      • "isSuspended": false,
      • "suspendedAt": "2019-09-10T15:31:47+02:00",
      • "suspensionExpiresAt": "2019-09-17T15:31:47+02:00",
      • "suspensionReason": "Suspended due to many cancellations",
      • "isLoginEnabled": true,
      • "activatedAt": "2019-09-01T12:02:35+02:00",
      • "lastSeenAt": "2019-09-03T09:54:12+02:00",
      • "client": {
        • "uuid": "1000072",
        • "clientFields": {
          • "8D2A6C1E-4F3B-4A7E-9C21-5B6E0F3D7A12": 1234,
          • "C47E9B05-1A2D-4E6F-8B3C-9D0E2F4A6B81": "ABCD"
          },
        • "agreements": null,
        • "birthdateAt": "1980-01-19T23:00:00+01:00",
        • "lastActiveAt": "2019-09-03T08:06:23+02:00",
        • "user": { },
        • "address": {
          • "street": "Sunny street 77a",
          • "postalCode": "1234AB",
          • "state": "Noord-Holland",
          • "city": "Amsterdam",
          • "countryCode": "NL",
          • "createdAt": "2019-05-12T12:45:47+02:00",
          • "modifiedAt": "2023-09-12T19:18:23+02:00"
          },
        • "phone": {
          • "primaryPhone": "+48111222333",
          • "createdAt": "2019-05-12T12:45:47+02:00",
          • "modifiedAt": "2023-09-12T19:18:23+02:00"
          },
        • "clientNotes": [
          • {
            • "type": "private",
            • "note": "Client prefers morning classes.",
            • "color": "#FF5733",
            • "expiresAt": null,
            • "guid": "364F59AB-5B43-4D02-989E-AE2811422709",
            • "createdAt": "2019-05-12T12:45:47+02:00",
            • "modifiedAt": "2023-09-12T19:18:23+02:00"
            }
          ],
        • "tags": [
          • {
            • "name": "VIP",
            • "color": "#FF5733",
            • "guid": "364F59AB-5B43-4D02-989E-AE2811422709",
            • "createdAt": "2019-05-12T12:45:47+02:00",
            • "modifiedAt": "2023-09-12T19:18:23+02:00"
            }
          ],
        • "age": 34
        },
      • "guid": "364F59AB-5B43-4D02-989E-AE2811422709",
      • "createdAt": "2019-05-12T12:45:47+02:00",
      • "modifiedAt": "2023-09-12T19:18:23+02:00"
      },
    • "firedAt": "2026-10-20T10:15:00+0200"
    }
]

Client restored Webhook

Event name: event.client.restored

Fired when a deleted client is restored in BackOffice.

Request Body schema: application/json
Array
guid
string

Unique identifier of this delivery.

webhookGuid
string

Identifier of the webhook (Studio > Integrations > Webhooks) this delivery was sent for. Useful when several webhooks point to the same callback URL.

event
string
Value: event.client.restored

Name of the event.

object (User)

Event payload. Its shape depends on the event.

firedAt
string <date-time>

Date and time when the event was fired (ISO 8601).

Responses

Request samples

Content type
application/json
[
  • {
    • "guid": "8F2B6C1D-4E7A-4B3C-9D21-5A6E7F8C9B0A",
    • "webhookGuid": "3C9E1A7B-2D4F-4E8A-B6C0-1F5D9A2E7B43",
    • "event": "event.client.restored",
    • "data": {
      • "emailAddress": "[email protected]",
      • "contactEmailAddress": "[email protected]",
      • "firstName": "Maria",
      • "lastName": "Dvorak",
      • "sex": 1,
      • "suspensionType": 1,
      • "isSuspended": false,
      • "suspendedAt": "2019-09-10T15:31:47+02:00",
      • "suspensionExpiresAt": "2019-09-17T15:31:47+02:00",
      • "suspensionReason": "Suspended due to many cancellations",
      • "isLoginEnabled": true,
      • "activatedAt": "2019-09-01T12:02:35+02:00",
      • "lastSeenAt": "2019-09-03T09:54:12+02:00",
      • "client": {
        • "uuid": "1000072",
        • "clientFields": {
          • "8D2A6C1E-4F3B-4A7E-9C21-5B6E0F3D7A12": 1234,
          • "C47E9B05-1A2D-4E6F-8B3C-9D0E2F4A6B81": "ABCD"
          },
        • "agreements": null,
        • "birthdateAt": "1980-01-19T23:00:00+01:00",
        • "lastActiveAt": "2019-09-03T08:06:23+02:00",
        • "user": { },
        • "address": {
          • "street": "Sunny street 77a",
          • "postalCode": "1234AB",
          • "state": "Noord-Holland",
          • "city": "Amsterdam",
          • "countryCode": "NL",
          • "createdAt": "2019-05-12T12:45:47+02:00",
          • "modifiedAt": "2023-09-12T19:18:23+02:00"
          },
        • "phone": {
          • "primaryPhone": "+48111222333",
          • "createdAt": "2019-05-12T12:45:47+02:00",
          • "modifiedAt": "2023-09-12T19:18:23+02:00"
          },
        • "clientNotes": [
          • {
            • "type": "private",
            • "note": "Client prefers morning classes.",
            • "color": "#FF5733",
            • "expiresAt": null,
            • "guid": "364F59AB-5B43-4D02-989E-AE2811422709",
            • "createdAt": "2019-05-12T12:45:47+02:00",
            • "modifiedAt": "2023-09-12T19:18:23+02:00"
            }
          ],
        • "tags": [
          • {
            • "name": "VIP",
            • "color": "#FF5733",
            • "guid": "364F59AB-5B43-4D02-989E-AE2811422709",
            • "createdAt": "2019-05-12T12:45:47+02:00",
            • "modifiedAt": "2023-09-12T19:18:23+02:00"
            }
          ],
        • "age": 34
        },
      • "guid": "364F59AB-5B43-4D02-989E-AE2811422709",
      • "createdAt": "2019-05-12T12:45:47+02:00",
      • "modifiedAt": "2023-09-12T19:18:23+02:00"
      },
    • "firedAt": "2026-10-20T10:15:00+0200"
    }
]

Shopping cart

Payment status Webhook

Event name: event.shopping_cart.payment_status

Fired when a Client checks out a shopping cart and every time the payment gateway reports a change of the payment status (paid, cancelled, rejected, failed...). It is also fired when a contract instalment is charged automatically.

Use data.orderStatus to determine the current state of the payment.

Duplicates

Payment gateways might notify several times about the same payment. Fitssey forwards only changes of the status - the webhook is fired once per data.orderId + data.orderStatus pair and repeated notifications with the same status are not sent again. A real change of the status (e.g. paid followed by a chargeback) fires the webhook again.

Request Body schema: application/json
Array
guid
string

Unique identifier of this delivery.

webhookGuid
string

Identifier of the webhook (Studio > Integrations > Webhooks) this delivery was sent for. Useful when several webhooks point to the same callback URL.

event
string
Value: event.shopping_cart.payment_status

Name of the event.

object (ShoppingCartWebhookPaymentStatus)

Event payload. Its shape depends on the event.

firedAt
string <date-time>

Date and time when the event was fired (ISO 8601).

Responses

Request samples

Content type
application/json
[
  • {
    • "guid": "8F2B6C1D-4E7A-4B3C-9D21-5A6E7F8C9B0A",
    • "webhookGuid": "3C9E1A7B-2D4F-4E8A-B6C0-1F5D9A2E7B43",
    • "event": "event.shopping_cart.payment_status",
    • "data": {
      • "metadata": { },
      • "paymentGateway": "stripe",
      • "paymentGatewayResponse": { },
      • "orderId": "3C9A1E7B-2D4F-4A6B-8C1E-9F0D2B3A4C5D",
      • "orderStatus": 1,
      • "user": {
        • "emailAddress": "[email protected]",
        • "firstName": "Maria",
        • "lastName": "Dvorak",
        • "client": {
          • "uuid": "1000072",
          • "address": {
            • "street": "Sunny street 77a",
            • "postalCode": "1234AB",
            • "state": "Noord-Holland",
            • "city": "Amsterdam",
            • "countryCode": "NL"
            }
          },
        • "guid": "364F59AB-5B43-4D02-989E-AE2811422709"
        },
      • "purchase": {
        • "items": [
          • {
            • "id": "1024",
            • "name": "Monthly pass",
            • "internalName": "Monthly pass - autumn promo",
            • "price": 1750
            }
          ],
        • "currency": "PLN",
        • "totalPrice": 1750,
        • "totalPriceBeforeDiscount": 2000,
        • "discountAmount": 250,
        • "paymentMethod": 0
        }
      },
    • "firedAt": "2026-10-20T10:15:00+0200"
    }
]

Retail

Checkout Webhook

Event name: event.retail.checkout

Fired when a sale is registered for a client - at the point of sale in BackOffice, through the API (POST /retail/orders/checkout, POST /retail/contracts/checkout) or when a pricing option is assigned to the client automatically.

A single checkout may contain several orders, contracts and wallet payments. Online purchases made by clients in FrontOffice are reported by Payment status instead.

Request Body schema: application/json
Array
guid
string

Unique identifier of this delivery.

webhookGuid
string

Identifier of the webhook (Studio > Integrations > Webhooks) this delivery was sent for. Useful when several webhooks point to the same callback URL.

event
string
Value: event.retail.checkout

Name of the event.

object (RetailWebhookCheckout)

Event payload. Its shape depends on the event.

firedAt
string <date-time>

Date and time when the event was fired (ISO 8601).

Responses

Request samples

Content type
application/json
[
  • {
    • "guid": "8F2B6C1D-4E7A-4B3C-9D21-5A6E7F8C9B0A",
    • "webhookGuid": "3C9E1A7B-2D4F-4E8A-B6C0-1F5D9A2E7B43",
    • "event": "event.retail.checkout",
    • "data": {
      • "user": {
        • "emailAddress": "[email protected]",
        • "firstName": "Maria",
        • "lastName": "Dvorak",
        • "client": {
          • "uuid": "1000072",
          • "address": {
            • "street": "Sunny street 77a",
            • "postalCode": "1234AB",
            • "state": "Noord-Holland",
            • "city": "Amsterdam",
            • "countryCode": "NL"
            }
          },
        • "guid": "364F59AB-5B43-4D02-989E-AE2811422709"
        },
      • "clientOrders": [
        • {
          • "orderStatus": 1,
          • "paymentMethod": 0,
          • "paidAmount": 1750,
          • "dueAmount": 0,
          • "paidAt": "2025-05-12T12:45:47+02:00",
          • "clientOrderItems": [
            • {
              • "name": "Yoga Card x4",
              • "internalName": "Yoga Card x4 [previously 4x card]",
              • "netPrice": 1428,
              • "totalPrice": 1750,
              • "taxAmount": 322,
              • "taxRate": 0.23,
              • "retailItemId": "5B66DF6C-00A3-4024-8A5D-D30BE8538D09",
              • "retailItemType": 0,
              • "serviceType": "TYPE_CLASSROOM_PRICING_OPTION_SERVICE",
              • "guid": "364F59AB-5B43-4D02-989E-AE2811422709"
              }
            ],
          • "totalPrice": 1750,
          • "discountAmount": 250,
          • "totalPriceBeforeDiscount": 2000,
          • "guid": "364F59AB-5B43-4D02-989E-AE2811422709"
          }
        ],
      • "clientContracts": [
        • {
          • "name": "Membership",
          • "startsAt": "2019-06-01T22:00:00+02:00",
          • "endsAt": "2020-05-31T21:59:59+02:00",
          • "qualifiedName": "1 Year Contract",
          • "guid": "364F59AB-5B43-4D02-989E-AE2811422709"
          }
        ],
      • "clientWalletPayments": [
        • {
          • "clientOrderItem": {
            • "name": "Yoga Card x4",
            • "internalName": "Yoga Card x4 [previously 4x card]",
            • "netPrice": 1428,
            • "totalPrice": 1750,
            • "taxAmount": 322,
            • "taxRate": 0.23,
            • "retailItemId": "5B66DF6C-00A3-4024-8A5D-D30BE8538D09",
            • "retailItemType": 0,
            • "serviceType": "TYPE_CLASSROOM_PRICING_OPTION_SERVICE",
            • "guid": "364F59AB-5B43-4D02-989E-AE2811422709"
            },
          • "guid": "364F59AB-5B43-4D02-989E-AE2811422709"
          }
        ]
      },
    • "firedAt": "2026-10-20T10:15:00+0200"
    }
]