Documentation Index

Fetch the complete documentation index at: https://help.frisbii.com/llms.txt

Use this file to discover all available pages before exploring further.

Callback Events Reference

Prev

Frisbii Media sends callbacks (webhooks) to your registered endpoints in real time when lifecycle events occur — new orders, subscription changes, invoice creation, customer updates, and more. This article covers setup, security, payload structure, all available event types, and retry behaviour.

Data protection note: Most callback types transmit personal data. The receiving system may qualify as an additional data processor under applicable data protection law. Take this into account when configuring callbacks.


Setup in the Merchant Backend

Callbacks are created and managed at client level under Settings → Development → Callbacks.

Create a callback (without authentication)

  1. Click + Create a Callback in the action bar

  2. Enter the URL of your web service endpoint

  3. Once a URL is entered, tick the Enabled checkbox to activate it

  4. Select the Callback type from the drop-down menu (e.g. "Create customer")

  5. Save — Frisbii Media automatically generates a unique callback secret for this callback, visible in the detail view

Create a callback (with authentication)

Tick Endpoint requires authentication and choose one or both methods:

Method 1 — Basic Auth (username + password) The credentials are sent with every request in the standard HTTP Authorization header.

Method 2 — Custom HTTP header(s) Add one or more key-value pairs. These are sent as additional headers with every request.

After saving, a callback secret is generated. This secret is used to verify the plenigo-signature header (see Verifying the signature below).

Manage existing callbacks

From the overview, each callback can be:

  • Opened via Details — shows full configuration and the callback secret

  • Modified via Edit — change URL, callback type, or enable/disable

  • Traced via Search in Log — opens the callback log pre-filtered to this callback ID


Request headers

Every callback request contains the following headers:

Header

Example value

Description

content-type

application/json

Always JSON

plenigo-signature

t=1729583536,s=fdcd0a0c…

HMAC-SHA256 signature for verification (see below)

x-plenigo-api-version

20240827

API version used for this callback

authorization

Basic cGxl…

Present when Basic Auth is configured

(custom headers)

Present when custom HTTP headers are configured


Verifying the plenigo-signature

Every callback is signed. Verifying the signature ensures the request originates from Frisbii Media and has not been tampered with.

Signature format

plenigo-signature: t=1729583536,s=fdcd0a0ccd0b4db629d35a33c3aada5cf669a28f91adb38abcc9ffcdb1663d38

Part

Description

t

Unix timestamp (seconds since epoch) — when the callback was generated

s

HMAC-SHA256 signature (hex-encoded)

How Frisbii Media generates the signature

  1. Takes the current Unix timestamp as a string

  2. Concatenates: timestamp + "." + raw request body (JSON string)

  3. Computes HMAC-SHA256 using the callback secret as the key

  4. Sets the header: plenigo-signature: t=<timestamp>,s=<signature>

How to verify it (step by step)

Step 1 — Parse the header

Split on , to get elements. Split each element on = to get prefix/value pairs. Extract t (timestamp) and s (signature). Ignore any other elements.

Step 2 — Build the signed payload string

signed_payload = t + "." + raw_request_body

Where raw_request_body is the exact JSON string received — do not parse and re-serialize it.

Step 3 — Compute the expected signature

Compute HMAC-SHA256 with:

  • Key: your callback secret (from the Merchant Backend detail view)

  • Message: signed_payload from Step 2

Step 4 — Compare

Compare your computed signature with s from the header. Also check that the difference between the current time and t is within an acceptable tolerance window (e.g. 5 minutes).

If the timestamp is far in the past, do not process the callback — stale callbacks can cause data inconsistency, especially for customer data.

PHP example

function verifyPlenigoSignature(string $rawBody, string $signatureHeader, string $callbackSecret): bool
{
    // Parse header: "t=1729583536,s=fdcd0a0c..."
    $parts = [];
    foreach (explode(',', $signatureHeader) as $part) {
        [$key, $value] = explode('=', $part, 2);
        $parts[$key] = $value;
    }
    $timestamp = $parts['t'] ?? null;
    $receivedSignature = $parts['s'] ?? null;
    if (!$timestamp || !$receivedSignature) {
        return false;
    }
    // Reject callbacks older than 5 minutes
    if (abs(time() - (int) $timestamp) > 300) {
        return false;
    }
    // Build signed payload and compute expected signature
    $signedPayload = $timestamp . '.' . $rawBody;
    $expectedSignature = hash_hmac('sha256', $signedPayload, $callbackSecret);
    return hash_equals($expectedSignature, $receivedSignature);
}
// Usage in your endpoint
$rawBody = file_get_contents('php://input');
$signatureHeader = $_SERVER['HTTP_PLENIGO_SIGNATURE'] ?? '';
$callbackSecret = 'YOUR_CALLBACK_SECRET'; // from Merchant Backend detail view
if (!verifyPlenigoSignature($rawBody, $signatureHeader, $callbackSecret)) {
    http_response_code(401);
    exit;
}
// Signature valid — process the callback
$payload = json_decode($rawBody, true);

Payload structure

All callbacks share the same outer structure:

{
  "entityType": "CUSTOMER",
  "callbackType": "CREATION",
  "entityId": "29",
  "entity": {
    // content depends on entityType
  }
}

Field

Description

entityType

The type of object affected (e.g. CUSTOMER, SUBSCRIPTION, INVOICE)

callbackType

The action that occurred (e.g. CREATION, CHANGE, CANCELLATION)

entityId

The ID of the affected entity — customer ID, subscription ID, invoice ID, etc.

entity

The full entity data object. null for DELETION events.


All callback types

Callback type

Entity type

Description

CREATION

APP_STORE_ORDER

An app store order (Apple or Google) was created or updated

CHANGE

APP_STORE_SUBSCRIPTION

An app store subscription was activated or deactivated

CREATION

APP_STORE_SUBSCRIPTION

An app store subscription was created

CHANGE

CUSTOMER

Customer data changed (name, email, password, status, token verification, etc.)

CREATION

CUSTOMER

A customer was created or registered

DELETION

CUSTOMER

A customer was deleted (entity is null)

CHANGE

CUSTOMER_OPT_IN

Customer opt-in was created or updated

PAYMENT_FAILED

FAILED_PAYMENT

A payment attempt failed

CHANGE

INVOICE

An invoice changed — cancellation, correction, or status change

CREATION

INVOICE

An invoice was created

CREATION

INVOICE_CANCELLATION

An invoice was cancelled

CREATION

INVOICE_CORRECTION

An invoice was corrected

CANCELLATION

MULTIUSER_SUBSCRIPTION

A multi-user subscription was cancelled

CREATION

MULTIUSER_SUBSCRIPTION

A multi-user subscription was created

UNDO_CANCELLATION

MULTIUSER_SUBSCRIPTION

A multi-user subscription cancellation was reversed

CREATION

ORDER

An order was created

CONDITIONS_FULFILLED

PURCHASED_ADDON

A purchased add-on's conditions were fulfilled

CANCELLATION

SUBSCRIPTION

A subscription was cancelled

CHANGE

SUBSCRIPTION

A subscription was changed

CREATION

SUBSCRIPTION

A subscription was created

ENDED

SUBSCRIPTION

A subscription ended

UNDO_CANCELLATION

SUBSCRIPTION

A subscription cancellation was reversed

CREATION

CALLBACK_RENEWAL

An API access token was renewed

Full JSON schemas for all entity types are available as a download here.


Payload examples

Create customer

{
  "entityType": "CUSTOMER",
  "callbackType": "CREATION",
  "entityId": "29",
  "entity": {
    "customerId": "29",
    "email": "user@example.com",
    "firstName": "Test",
    "lastName": "",
    "username": "Test",
    "language": "de",
    "salutation": "",
    "status": "ACTIVATED",
    "registrationSource": "Supporter",
    "registrationDate": "2024-08-27T11:49:41.389622Z",
    "createdDate": "2024-08-27T11:49:41.389622Z",
    "changedDate": "2024-08-27T11:49:41.389622Z",
    "twoFactorEnabled": false,
    "ageVerificationPinEnabled": false,
    "externalSystemId": "",
    "miscellaneousData": {}
  }
}

Delete customer

{
  "entityType": "CUSTOMER",
  "callbackType": "DELETION",
  "entityId": "33",
  "entity": null
}

entityId is the customer number. entity is null because the data has been deleted.

Create invoice

{
  "entityType": "INVOICE",
  "callbackType": "CREATION",
  "entityId": "223",
  "entity": {
    "invoiceId": 223,
    "invoiceDate": "2024-08-28T00:08:51.868510Z",
    "status": "NOT_PAID",
    "type": "INVOICE",
    "currency": "EUR",
    "accumulatedPrice": 0.5,
    "paymentMethod": "BILLING",
    "invoiceCustomerId": "23",
    "customerEmail": "user@example.com",
    "invoiceAddress": {
      "type": "INVOICE",
      "salutation": "NONE",
      "firstName": "Test",
      "lastName": "User",
      "city": "Kempten",
      "postcode": "87435",
      "country": "DE",
      "businessAddress": false
    },
    "items": [
      {
        "position": 1,
        "quantity": 1,
        "price": 0.5,
        "tax": 7,
        "taxCountry": "DE",
        "taxType": "DIGITALNEWSPAPER",
        "plenigoOfferId": "O_D0SO735QL1AXZH7FTG",
        "plenigoProductId": "P_DSQ20PV66PNWGK0LPV",
        "periodStartDate": "2024-08-28T00:00:00.000000Z",
        "periodEndDate": "2024-08-29T23:59:59.000000Z"
      }
    ]
  }
}

Change subscription

{
  "entityType": "SUBSCRIPTION",
  "callbackType": "CHANGE",
  "entityId": "1230116",
  "entity": {
    "subscriptionId": 1230116,
    "status": "ACTIVE",
    "plenigoOfferId": "O_628IU8F9CB7I6EH5ZG",
    "subscriptionType": "ISSUE_BASED",
    "cancellationType": "ISSUE_BASED_REGULAR",
    "startDate": "2024-08-02T09:43:13.348191Z",
    "cancellationDate": null,
    "endDate": null,
    "nextBookingDate": null,
    "deliveries": 8,
    "openDeliveries": 4,
    "finishedDeliveries": 4,
    "paymentMethod": "CREDIT_CARD",
    "paymentMethodDetails": { "brand": "AMERICAN_EXPRESS" },
    "invoiceCustomerId": "8",
    "deliveryCustomerId": "8",
    "currency": "EUR",
    "managedBy": "PLENIGO",
    "managedExternal": false,
    "connectedOffer": false
  }
}

Create in-app subscription

{
  "entityType": "APP_STORE_SUBSCRIPTION",
  "callbackType": "CREATION",
  "version": "3.0",
  "entityId": "1230306",
  "entity": {
    "appStoreSubscriptionId": 1230206,
    "customerId": "29",
    "status": "ACTIVE",
    "startDate": "2023-12-29T15:01:34.393Z",
    "endDate": "2023-12-29T15:06:32.364Z",
    "cancellationDate": null,
    "accessRightUniqueId": "inappsub",
    "externalSystemId": "subscription_1_app_2",
    "chainId": 1230206
  }
}

Responding to callbacks

Your endpoint must return HTTP 200 to acknowledge receipt. Return 200 as quickly as possible — offload processing to a background queue:

// 1. Acknowledge immediately
http_response_code(200);
// 2. Verify signature (see above)
$rawBody = file_get_contents('php://input');
$signatureHeader = $_SERVER['HTTP_PLENIGO_SIGNATURE'] ?? '';
if (!verifyPlenigoSignature($rawBody, $signatureHeader, $callbackSecret)) {
    exit; // already sent 200 — just don't process
}
// 3. Queue for async processing
$payload = json_decode($rawBody, true);
enqueueCallbackJob($payload['entityType'], $payload['callbackType'], $payload['entityId'], $payload['entity']);
exit;

Retry behaviour

If your endpoint does not return HTTP 200, Frisbii Media retries the callback on the following schedule:

Attempt

Delay after previous attempt

1st retry

1 hour

2nd retry

6 hours

3rd retry

24 hours

No further retries

Exception: If the first attempt returns an error indicating the server does not exist or the callback secret is incorrect, no retries are made.

Auto-deactivation: If a callback fails 10 or more times within 7 consecutive days, it is automatically deactivated by the system.

Alert email: If a callback fails 10 or more times within a single day, an automatic system email is sent to the contact email address configured in the client's company data in the Merchant Backend.