Webhooks

Listen to events on your SubscriptionFlow account so your integration can automatically trigger reactions.

SubscriptionFlow uses webhooks to notify your application in real time when something happens -- a subscription is renewed, an invoice is paid, a payment method expires, and more. Instead of polling the API, you receive an HTTP POST request at a URL you choose.


How webhooks work

  1. An event occurs in your SubscriptionFlow account (e.g., a customer's subscription renews).
  2. SubscriptionFlow creates a webhook payload containing the event type and the full or custom entity data.
  3. SubscriptionFlow sends an HTTP request to each webhook endpoint registered for that event.
  4. Your server receives the request, processes it, and returns a 2xx status code to acknowledge receipt.

Quickstart

Follow these steps to start receiving webhooks in under 5 minutes.

Step 1: Create your endpoint

Build an HTTP endpoint on your server that can receive POST requests. SubscriptionFlow sends webhook data as application/x-www-form-urlencoded form parameters.

JavaScript
const express = require("express");
const app = express();

app.use(express.urlencoded({ extended: true }));

app.post("/webhooks/subscriptionflow", (req, res) => {
  const event = req.body.event;
  const method = req.body.method;

  // Route to the appropriate handler based on event type
  switch (event) {
    case "renewed":
      handleSubscriptionRenewal(req.body);
      break;
    case "paid":
      handleInvoicePaid(req.body);
      break;
    case "failed":
      handleTransactionFailed(req.body);
      break;
    default:
      console.log(`Unhandled event type: ${event}`);
  }

  // Acknowledge receipt immediately
  res.status(200).send("OK");
});

app.listen(4242, () => console.log("Webhook server running on port 4242"));

Tip: Your endpoint should return a 2xx response as quickly as possible. Move any heavy processing to a background job or queue to avoid timeouts.

Step 2: Register the webhook in SubscriptionFlow

  1. Go to Settings > Webhooks in your SubscriptionFlow dashboard.
  2. Click Create Webhook.
  3. Configure the webhook:
Field Description
Webhook Name A descriptive label (e.g., "Renewal notifications")
Webhook URL Your HTTPS endpoint (e.g., https://example.com/webhooks/subscriptionflow)
Module The entity type to listen to -- Subscription, Invoice, Transaction, Payment Method, or Email
Event The specific event that triggers this webhook (e.g., renewed, paid)
Payload Type DEFAULT for full entity data, or CUSTOM for selected fields only
Description Optional notes about this webhook's purpose
  1. Click Save.

Important: Webhook URLs must use HTTPS with a valid SSL certificate. HTTP endpoints are not accepted.

Step 3: Test your webhook

To verify your endpoint is working:

  1. Create a test subscription or trigger the relevant action in your SubscriptionFlow account.
  2. Check your webhook logs at Settings > Webhooks > Logs to confirm delivery.
  3. Verify your server received and processed the request.

You can also use tools like webhook.site to inspect the raw payload before pointing the webhook at your production server.


Events

Every webhook request includes an event field that tells you what happened. Events are grouped by module.

Subscription events

Event Trigger
subscription.created A new subscription is created
subscription.updated A subscription's details are modified
subscription.deleted A subscription is deleted
subscription.renewed A subscription auto-renews at the end of its billing cycle
subscription.suspended A subscription is suspended (manually or automatically)
subscription.expired A termed subscription passes its billing end date
subscription.cancelled A subscription is cancelled
subscription.resumed A suspended subscription is reactivated
subscription.term_started A new billing term begins on a termed subscription
subscription.entitlement_redeemed An entitlement on the subscription is redeemed
subscription.upgraded A subscription moves to a higher-tier plan
subscription.downgraded A subscription moves to a lower-tier plan

Invoice events

Event Trigger
invoice.created A new invoice is generated
invoice.updated An invoice is modified
invoice.deleted An invoice is deleted
invoice.overdue An invoice passes its due date without payment
invoice.paid An invoice is fully paid
invoice.reversed_charges Charges on an invoice are reversed

Transaction events

Event Trigger
transaction.created A new payment transaction is recorded
transaction.deleted A transaction is deleted
transaction.failed A payment attempt fails

Payment method events

Event Trigger
paymentmethod.created A new payment method is added to a customer
paymentmethod.updated A payment method's details are modified
paymentmethod.deleted A payment method is removed
paymentmethod.expired A payment method passes its expiry date
paymentmethod.activated A payment method is activated

Email events

Event Trigger
emails.created An email record is created
emails.updated An email record is modified
emails.deleted An email record is deleted
emails.failed An email fails to deliver
emails.bounced An email bounces back

Payload format

Every webhook request contains the event data. The shape of the payload depends on the payload type you select when creating the webhook.

Default payload

The default payload sends the full entity representation -- the same data you'd get from the corresponding REST API endpoint.

Example: subscription.renewed

JSON
{
  "type": "subscription",
  "id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
  "attributes": {
    "name": "SUB-00142",
    "display_name": "Pro Monthly - John Doe",
    "status": "active",
    "type": "auto_recurring",
    "total_amount": "49.99",
    "next_bill_date": "2026-05-01",
    "billing_end_date": null,
    "is_auto_renew": true,
    "renewal_type": "auto",
    "renewal_period": "1",
    "renewal_period_type": "month",
    "renewed_at": "2026-04-01",
    "trial_period": null,
    "trial_expiry": null,
    "subscription_mrr": "49.99",
    "payment_status": "paid",
    "tags": ["premium", "annual-promo"],
    "created_at": "2025-01-15T10:30:00.000000Z",
    "updated_at": "2026-04-01T00:00:12.000000Z"
  },
  "relationships": {
    "customer_id": {
      "type": "customer",
      "id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
      "attributes": {
        "first_name": "John",
        "last_name": "Doe",
        "primary_email": "john.doe@example.com"
      }
    }
  },
  "event": "renewed",
  "method": "POST"
}

Example: invoice.paid

JSON
{
  "type": "invoice",
  "id": "2b3c4d5e-6f7a-8b9c-0d1e-2f3a4b5c6d7e",
  "attributes": {
    "name": "INV-00318",
    "status": "paid",
    "invoice_date": "2026-04-01",
    "due_date": "2026-04-15",
    "currency": "USD",
    "sub_total": "49.99",
    "tax_amount": "4.50",
    "total_amount": "54.49",
    "outstanding_amount": "0.00",
    "received_payment": "54.49",
    "description": "Pro Monthly subscription renewal",
    "created_at": "2026-04-01T00:00:12.000000Z",
    "updated_at": "2026-04-01T00:01:05.000000Z"
  },
  "relationships": {
    "customer_id": {
      "type": "customer",
      "id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
      "attributes": {
        "first_name": "John",
        "last_name": "Doe",
        "primary_email": "john.doe@example.com"
      }
    }
  },
  "event": "paid",
  "method": "POST"
}

Example: transaction.failed

JSON
{
  "type": "transaction",
  "id": "3c4d5e6f-7a8b-9c0d-1e2f-3a4b5c6d7e8f",
  "attributes": {
    "name": "TXN-00587",
    "number": "587",
    "status": "failed",
    "amount": "54.49",
    "currency": "USD",
    "date": "2026-04-01",
    "type": "charge",
    "decline_reason": "insufficient_funds",
    "reason_code": "card_declined",
    "created_at": "2026-04-01T00:01:00.000000Z",
    "updated_at": "2026-04-01T00:01:00.000000Z"
  },
  "relationships": {
    "customer_id": {
      "type": "customer",
      "id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
      "attributes": {
        "first_name": "John",
        "last_name": "Doe",
        "primary_email": "john.doe@example.com"
      }
    }
  },
  "event": "failed",
  "method": "POST"
}

Example: paymentmethod.expired

JSON
{
  "type": "paymentmethod",
  "id": "4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f9a",
  "attributes": {
    "name": "Visa ending 4242",
    "status": "expired",
    "payment_method_type": "credit_card",
    "payment_method_subtype": "visa",
    "last_four_digits": "4242",
    "expiry_month": "03",
    "expiry_year": "2026",
    "gateway_name": "Stripe",
    "default": true,
    "first_name": "John",
    "last_name": "Doe",
    "billing_email": "john.doe@example.com",
    "created_at": "2025-01-15T10:32:00.000000Z",
    "updated_at": "2026-04-01T00:00:00.000000Z"
  },
  "relationships": {
    "parent_id": {
      "type": "customer",
      "id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
    }
  },
  "event": "expired",
  "method": "POST"
}

Custom payload

Custom payloads let you control exactly which fields are sent. You map fields using the template syntax {{ModelName_fieldname}} and add any static key-value pairs you need.

Configuration example:

Key Value template
sub_id {{Subscription_id}}
email {{Customer_email}}
plan {{Subscription_plan}}
status {{Subscription_status}}
source subscriptionflow (static)

Resulting payload:

JSON
{
  "sub_id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
  "email": "john.doe@example.com",
  "plan": "Pro Monthly",
  "status": "active",
  "source": "subscriptionflow",
  "event": "renewed",
  "method": "POST"
}

Custom payloads also support the GET method. When using GET, data is sent as query string parameters instead of a POST body.


Delivery details

Property Value
HTTP method POST (default) or GET (custom payloads only)
Content type application/x-www-form-urlencoded
Timeout 30 seconds
Protocol HTTPS required (valid SSL certificate)

Retry behavior

Response Behavior
2xx Delivery confirmed. No retry.
404 Permanently failed. No retry.
Other errors Retried automatically based on your queue configuration.

Fields included in every request

Regardless of payload type, every webhook request includes:

Field Type Description
event string The event name (e.g., renewed, paid, failed)
method string The HTTP method used (GET or POST)

Full field reference

These are all fields available in the DEFAULT payload for each module.

Subscription fields

Attributes

Field Type Description
id uuid Unique identifier
name string Subscription identifier (e.g., SUB-00142)
display_name string Human-readable display name
status string active, suspended, cancelled, expired
type string Subscription type
payment_status string Current payment status
total_amount decimal Total subscription amount
shipping_charge decimal Shipping charges
total_shipping_charges decimal Accumulated shipping charges
subscription_mrr decimal Monthly Recurring Revenue
remaining_term_amount decimal Remaining amount for current term
remaining_term_payments integer Remaining payments for current term
current_term_payments integer Payments made in current term
next_bill_date date Next billing date
billing_end_date date Billing period end date
start_issue integer Starting issue number
end_issue integer Ending issue number
number_of_issues integer Total number of issues
remaining_issues integer Remaining issues
remaining_days integer Remaining days in the term
is_auto_renew boolean Whether auto-renewal is enabled
renewal_type string Renewal type
renewal_period integer Renewal period length
renewal_period_type string day, month, year
renewal_price_change_type string How price changes on renewal
renewal_price_change_value decimal Price change amount or percentage
renewed_at datetime Last renewal timestamp
active_period integer Active period length
trial_period integer Trial period length
trial_period_unit string Trial period unit
trial_expiry date Trial expiration date
termed_initial_period integer Initial term length
termed_initial_period_type string Initial term unit
termed_start_date date Term start date
suspended_at date When the subscription was suspended
resume_suspended_at date When the suspension will lift
cancelled_at date When the subscription was cancelled
invoice_separate boolean Whether invoices are generated separately
skip_projected_invoice boolean Whether projected invoices are skipped
is_gift boolean Whether this is a gift subscription
is_extend_period boolean Whether an extension period is active
extend_period integer Extension period length
extend_period_type string Extension period unit
extend_period_start_date date Extension start date
extend_period_expiry date Extension expiry date
extend_period_allocated_days integer Total allocated extension days
extend_period_used_days integer Used extension days
extend_period_remaining_days integer Remaining extension days
tags array Associated tags
trigger_dates object Configured trigger dates
additional_data object Custom data
data_source string Data source
default_payment_method uuid Default payment method ID
created_at datetime Creation timestamp
updated_at datetime Last update timestamp

Relationships

Field Related entity
customer_id Customer
assigned_to User
assigned_group_id Group
created_by User
updated_by User
recipient_id Contact (gift recipient)
tax_id Tax
invoice_status Invoice
Invoice fields

Attributes

Field Type Description
id uuid Unique identifier
name string Invoice number (e.g., INV-00318)
status string draft, open, paid, overdue, void
invoice_date date Issue date
due_date date Payment due date
currency string Invoice currency code
sub_total decimal Subtotal before tax and discounts
total_amount decimal Total amount due
tax_amount decimal Tax amount
tax_breakdown object Detailed tax breakdown
discount_value decimal Discount applied
shipping_charge decimal Shipping charges
miscellaneous_charges decimal Miscellaneous charges
miscellaneous_charges_breakdown object Miscellaneous charges detail
outstanding_amount decimal Remaining unpaid amount
received_payment decimal Total payments received
opening_balance decimal Opening balance
closing_balance decimal Closing balance
sum_of_credit_notes decimal Total credit notes applied
description string Invoice description
note string Notes
terms string Payment terms
is_oneoff boolean One-off invoice
additional_info object Additional information
additional_data object Custom data
reversed_reference string Reference to reversed charges
accounting_activity_status string Accounting sync status
accounting_sync_details object Accounting sync details
ecommerce_sync_details object E-commerce sync details
shipstation_order string ShipStation order reference
shipstation_order_status string ShipStation order status
data_source string Data source
created_at datetime Creation timestamp
updated_at datetime Last update timestamp

Relationships

Field Related entity
customer_id Customer
assigned_to User
assigned_group_id Group
created_by User
updated_by User
transaction_status Transaction
Transaction fields

Attributes

Field Type Description
id uuid Unique identifier
name string Transaction name
number string Transaction number
transaction_id string External transaction identifier
reference_transaction_id string Reference to a related transaction
reference string Transaction reference string
type string Transaction type
status string success, failed, pending
amount decimal Transaction amount
balance decimal Remaining balance
unapplied_amount decimal Unapplied amount
currency string Currency code
date date Transaction date
cash_or_card string Payment medium
description string Description
decline_reason string Reason for decline
reason_code string Reason code
payment_method_id uuid Associated payment method
payment_type_id uuid Payment type
transaction_category string Category
approval_link string Approval link
miscellaneous_charges decimal Miscellaneous charges
miscellaneous_charges_breakdown object Miscellaneous charges detail
accounting_account_code string Accounting account code
data_source string Data source
unpublished_id string Unpublished identifier
created_at datetime Creation timestamp
updated_at datetime Last update timestamp

Relationships

Field Related entity
customer_id Customer
assigned_to User
assigned_group_id Group
created_by User
updated_by User
Payment method fields

Attributes

Field Type Description
id uuid Unique identifier
name string Payment method name
status string active, expired, inactive
payment_method_type string Type (e.g., credit_card, bank_account)
payment_method_subtype string Subtype (e.g., visa, mastercard)
payment_method_id string External identifier
gateway string Gateway identifier
gateway_name string Gateway display name
default boolean Default payment method
last_four_digits string Last four digits
expiry_date string Expiry date
expiry_month string Expiry month
expiry_year string Expiry year
first_name string Cardholder first name
last_name string Cardholder last name
email string Associated email
phone string Associated phone
billing_name string Billing name
billing_email string Billing email
billing_address1 string Address line 1
billing_address2 string Address line 2
billing_address3 string Address line 3
billing_city string City
billing_state string State/province
billing_country string Country
billing_postalcode string Postal/zip code
currency string Currency
customer_profile_id string Gateway customer profile ID
mandate string Direct debit mandate reference
routing_number string Bank routing number
source_company string Source company
owner_type string Owner type
parent_type string Parent entity type
description string Description
note string Notes
approval_link string Approval link
declined_reason string Decline reason
reason_code string Reason code
data_source string Data source
created_at datetime Creation timestamp
updated_at datetime Last update timestamp

Relationships

Field Related entity
parent_id Customer
assigned_to User
created_by User
updated_by User
Email fields

Attributes

Field Type Description
id uuid Unique identifier
subject string Email subject line
from string Sender email
to string Recipient email(s)
cc string CC recipients
bcc string BCC recipients
reply_to string Reply-to address
email_body string HTML email body
email_text string Plain text body
excerpt string Email preview text
status string sent, failed, bounced, queued
type string Email type
tracking_status string Tracking status (opened, clicked, etc.)
failure_reason string Failure reason
message_id string Email message ID
posted_at datetime Send timestamp
is_archived boolean Whether archived
is_starred boolean Whether starred
is_guest boolean Whether from a guest
additional_payload object Additional data
data_source string Data source
created_at datetime Creation timestamp
updated_at datetime Last update timestamp

Relationships

Field Related entity
recipientable_id Customer / Contact
relatable_id Related entity
assigned_to User
created_by User
updated_by User

Webhook logs

Enable logging from Settings > Webhooks to record all delivery attempts. Each log entry includes:

Field Description
Status completed or failed
Level success or error
HTTP status code The response code from your endpoint
Error message Details when delivery fails
Payload The URL and data that was sent

Use logs to debug failed deliveries, inspect payloads, and confirm your endpoint is processing events correctly.


Best practices

Return a 2xx quickly. Your endpoint should acknowledge receipt within 30 seconds. Offload any heavy processing to a background job.

Handle duplicate deliveries. In rare cases, the same event may be sent more than once. Use the entity id and event to deduplicate on your end.

Keep your SSL certificate valid. SubscriptionFlow requires HTTPS. If your certificate expires, webhook delivery will fail.

Use DEFAULT payloads for complete data. The DEFAULT payload mirrors the API response and includes all fields and relationships. Use CUSTOM payloads only when you need to match a specific external format or limit the data sent.

Monitor your logs. Enable webhook logging and review it regularly to catch delivery failures before they impact your integration.

Don't rely on delivery order. Webhooks may arrive out of order. Use timestamps like updated_at to determine the latest state of an entity.