Invoices

Receive notifications when invoices are created, updated, paid, failed, or require payment action.

Overview

Invoice webhooks notify your application about the lifecycle of invoices. Use these events to track billing activity, notify customers about payments, handle failed charges, and build automated billing workflows.


Use Cases

  • Notify customers when a new invoice is generated
  • Send payment confirmation emails on successful charges
  • Trigger dunning workflows when payments fail
  • Alert customers when payment action (e.g. 3D Secure) is required
  • Build dashboards to track billing metrics and revenue
  • Send advance notice to customers about upcoming invoices

Events

EventDescription
invoice.createdA new invoice was generated
invoice.updatedAn invoice was updated (e.g. status change, payment applied)
invoice.upcomingAn invoice will be generated soon for an upcoming billing cycle
invoice.payment_succeededPayment was successfully collected for an invoice
invoice.payment_failedPayment collection failed for an invoice
invoice.payment_action_requiredCustomer action is required to complete payment (e.g. 3D Secure authentication)

invoice.created

Triggered When

  • A subscription is created and its first invoice is generated
  • A subscription renews and a new billing cycle invoice is created
  • An invoice is manually created via the API or dashboard

Payload

{
  "type": "invoice.created",
  "id": "evt_HAg8TfEfmhE55hZ3ot6kZ7d2",
  "created": 1704067200,
  "data": {
    "object": {
      "object": "invoice",
      "id": 100001,
      "address_billing": {
        "object": "address",
        "id": 45678,
        "city": "New York",
        "company": "Acme Inc",
        "country": "US",
        "country_name": "United States",
        "created_at": "2024-01-01T00:00:00.000000Z",
        "department": null,
        "first_name": "John",
        "is_default": true,
        "last_name": "Doe",
        "line1": "123 Main Street",
        "line2": "Suite 100",
        "postal_code": "10001",
        "salutation": "Mr",
        "site_ids": [1],
        "state": "NY",
        "state_name": "New York",
        "title": null,
        "type": "billing",
        "updated_at": "2024-01-01T00:00:00.000000Z"
      },
      "address_shipping": {
        "object": "address",
        "id": 45679,
        "city": "New York",
        "company": "Acme Inc",
        "country": "US",
        "country_name": "United States",
        "created_at": "2024-01-01T00:00:00.000000Z",
        "department": null,
        "first_name": "John",
        "is_default": true,
        "last_name": "Doe",
        "line1": "123 Main Street",
        "line2": "Suite 100",
        "postal_code": "10001",
        "salutation": "Mr",
        "site_ids": [1],
        "state": "NY",
        "state_name": "New York",
        "title": null,
        "type": "shipping",
        "updated_at": "2024-01-01T00:00:00.000000Z"
      },
      "amount_due": 3500,
      "amount_paid": 0,
      "amount_remaining": 3500,
      "auto_advance": true,
      "billing": "charge_automatically",
      "billing_address": {
        "object": "address",
        "id": 45678,
        "city": "New York",
        "company": "Acme Inc",
        "country": "US",
        "country_name": "United States",
        "created_at": "2024-01-01T00:00:00.000000Z",
        "department": null,
        "first_name": "John",
        "is_default": true,
        "last_name": "Doe",
        "line1": "123 Main Street",
        "line2": "Suite 100",
        "postal_code": "10001",
        "salutation": "Mr",
        "site_ids": [1],
        "state": "NY",
        "state_name": "New York",
        "title": null,
        "type": "billing",
        "updated_at": "2024-01-01T00:00:00.000000Z"
      },
      "billing_reason": "subscription_cycle",
      "charge_id": null,
      "created": "2024-01-01T12:00:00.000000Z",
      "currency": "usd",
      "customer": {
        "object": "customer",
        "id": 12345,
        "balance": 0,
        "billing_email": null,
        "created": "2023-06-15T09:30:00.000000Z",
        "currency": "usd",
        "default_source": {
          "object": "source",
          "id": 700001,
          "object_id": "pm_XXXXXXXXXXXXXXXXXXXX",
          "object_gateway": "stripe",
          "brand": "visa",
          "country": "US",
          "exp_month": 12,
          "exp_year": 2027,
          "funding": "credit",
          "last4": "4242",
          "site_ids": [1]
        },
        "delinquent": false,
        "display_name": null,
        "email": "[email protected]",
        "email_confirm": null,
        "email_hardbounce": false,
        "first_name": "John",
        "has_password": 1,
        "language": "en",
        "last_active_at": 1704060000,
        "last_login_method": "email",
        "last_name": "Doe",
        "mail_marketing": null,
        "metadata": {
          "customer_source": "web"
        },
        "object_id": "cus_XXXXXXXXXXXXXX",
        "organization": null,
        "password_last_updated_at": null,
        "phone": null,
        "phone_confirm": null,
        "salutation": "Mr",
        "site_ids": [1],
        "tele_marketing": null,
        "title": null,
        "username": null
      },
      "customer_address": null,
      "customer_shipping": {
        "id": 45679,
        "type": "shipping",
        "user_id": 12345,
        "account_id": null,
        "salutation": "Mr",
        "first_name": "John",
        "last_name": "Doe",
        "title": null,
        "company": "Acme Inc",
        "department": null,
        "line1": "123 Main Street",
        "line2": "Suite 100",
        "city": "New York",
        "state": "NY",
        "country": "US",
        "postal_code": "10001",
        "phone": null,
        "metadata": null,
        "old_provider_id": null,
        "is_default": true,
        "pelcro_sync_last_at": null,
        "pelcro_sync_failed_reason": null,
        "created_at": "2024-01-01T00:00:00.000000Z",
        "updated_at": "2024-01-01T00:00:00.000000Z",
        "deleted_at": null,
        "attributes": null
      },
      "days_to_creation": 7,
      "default_tax_rates": null,
      "deleted_at": null,
      "discount_id": null,
      "ending_balance": 0,
      "finalized_at": "2024-01-01T12:00:01.000000Z",
      "hosted_invoice_url": null,
      "invoice_pdf": "https://www.pelcro.com/pdf/invoice/eyJpdiI6Ik...",
      "is_imported": false,
      "lines": {
        "object": "list",
        "data": [
          {
            "object": "line_item",
            "id": 400001,
            "amount": 3500,
            "created_at": "2024-01-01T12:00:01.000000Z",
            "currency": "usd",
            "deleted_at": null,
            "description": "1 x Monthly Plan (at $35.00 / month)",
            "metadata": null,
            "object_id": null,
            "period": {
              "start": 1704067200,
              "end": 1706745600
            },
            "quantity": 1,
            "subscription": {
              "object": "subscription",
              "id": 200001,
              "address_id": 45679,
              "address_shipping": { "...": "Nested Address object" },
              "auto_renew": true,
              "billing": "charge_automatically",
              "cancel_at_period_end": 0,
              "canceled_at": null,
              "created": "2023-06-15T09:30:00.000000Z",
              "current_period_start": "2024-01-01T09:30:00.000000Z",
              "current_period_end": "2024-02-01T09:30:00.000000Z",
              "customer": { "...": "Nested Customer object" },
              "default_source": { "...": "Nested Source object" },
              "latest_invoice": { "...": "Nested Invoice object" },
              "plan": { "...": "Full Plan object with nested Product" },
              "quantity": 1,
              "schedules": { "...": "Subscription schedule phases" },
              "site_id": 1,
              "status": "active",
              "...": "Full subscription object"
            },
            "tax_amounts": null,
            "tax_rates": null,
            "updated_at": "2024-01-01T12:00:01.000000Z"
          }
        ]
      },
      "marked_uncollectible_at": null,
      "number": "XXXXXXXX-000001",
      "object_id": null,
      "paid_at": null,
      "payment_link": "https://www.pelcro.com/invoice/payment/eyJpdiI6Ik...",
      "period_end": "2024-02-01T09:30:00.000000Z",
      "period_start": "2024-01-01T09:30:00.000000Z",
      "plan": {
        "id": 300001,
        "nickname": "Monthly Plan",
        "interval": "month"
      },
      "post_payment_credit_notes_amount": 0,
      "pre_payment_credit_notes_amount": 0,
      "product": {
        "id": 500001,
        "name": "Premium Subscription",
        "name_internal": null,
        "description": "Full access to all premium content",
        "entitlements": ["digital"],
        "type": "service"
      },
      "purchase_order": null,
      "site_id": 1,
      "source": null,
      "starting_balance": 0,
      "status": "open",
      "subscription_id": 200001,
      "subtotal": 3500,
      "tax": 0,
      "tax_percent": null,
      "total": 3500,
      "total_tax_amounts": null,
      "updated_at": "2024-01-01T12:00:01.000000Z",
      "voided_at": null
    }
  }
}

Note: The payment_link field is conditional — it is only present when the invoice status is open or past_due. For invoices with other statuses (e.g. scheduled, paid, void), this field will be absent from the payload entirely.


invoice.updated

Triggered When

  • An invoice's status changes (e.g. from open to paid)
  • Invoice fields are modified (e.g. amount adjustments, metadata changes)

Payload

{
  "type": "invoice.updated",
  "id": "evt_Kp9xMfEfmhE55hZ3ot6kZ7d3",
  "created": 1704067300,
  "data": {
    "object": {
      "object": "invoice",
      "id": 100001,
      "amount_due": 3500,
      "amount_paid": 3500,
      "amount_remaining": 0,
      "status": "paid",
      "...": "Same full structure as invoice.created"
    },
    "previous_attributes": {
      "amount_paid": 0,
      "amount_remaining": 3500,
      "status": "open"
    }
  }
}

Note: The previous_attributes object contains only the fields that changed, showing their values before the update. Only fields listed in the invoice's publishable attributes are included.


invoice.upcoming

Triggered When

  • A subscription's billing cycle is approaching and an invoice will be generated soon
  • The number of days before the invoice is created is determined by the plan's invoice_upcoming_notification setting or the account-level invoice_upcoming_notification_days setting

Payload

{
  "type": "invoice.upcoming",
  "id": "evt_Rq7xNgGhmjF66iA4pt7lA8e4",
  "created": 1676900000,
  "data": {
    "object": {
      "object": "invoice",
      "id": null,
      "amount_due": 3500,
      "amount_paid": 0,
      "amount_remaining": 3500,
      "currency": "usd",
      "customer": {
        "object": "customer",
        "id": 12345,
        "...": "Full customer object"
      },
      "days_to_creation": 7,
      "plan": {
        "id": 300001,
        "nickname": "Monthly Plan",
        "interval": "month"
      },
      "product": {
        "id": 500001,
        "name": "Premium Subscription",
        "name_internal": null,
        "description": null,
        "entitlements": null,
        "type": "service"
      },
      "status": null,
      "subscription_id": 200001,
      "...": "Same structure as invoice.created"
    }
  }
}

Note: The invoice.upcoming event represents a preview of the upcoming invoice. The invoice id may be null since the invoice has not been created yet. The days_to_creation field indicates how many days until the invoice will be generated.


invoice.payment_succeeded

Triggered When

  • Payment is successfully collected for an invoice
  • The invoice status transitions to paid

Payload

{
  "type": "invoice.payment_succeeded",
  "id": "evt_Xt5yPqHinlG77jB5qu8mB9f5",
  "created": 1676985000,
  "data": {
    "object": {
      "object": "invoice",
      "id": 100001,
      "amount_due": 3500,
      "amount_paid": 3500,
      "amount_remaining": 0,
      "charge": {
        "id": 600001,
        "offline": false,
        "payment_category": "card",
        "reference": null
      },
      "paid_at": "2023-02-21T13:10:00.000000Z",
      "status": "paid",
      "source": {
        "object": "source",
        "id": 700001,
        "brand": "Visa",
        "country": "US",
        "exp_month": 12,
        "exp_year": 2025,
        "last_four": "4242",
        "type": "card"
      },
      "...": "Same structure as invoice.created"
    },
    "previous_attributes": {
      "amount_paid": 0,
      "amount_remaining": 3500,
      "status": "open"
    }
  }
}

Note: The previous_attributes object is automatically detected from model changes. It contains the invoice fields that changed during the payment process, with their previous values. The charge and source objects are included when a payment method was used.


invoice.payment_failed

Triggered When

  • An attempt to collect payment for an invoice fails
  • Common causes include insufficient funds, expired cards, or declined transactions

Payload

{
  "type": "invoice.payment_failed",
  "id": "evt_Zu6zRsJjomH88kC6rv9nC0g6",
  "created": 1676985100,
  "data": {
    "object": {
      "object": "invoice",
      "id": 100001,
      "amount_due": 3500,
      "amount_paid": 0,
      "amount_remaining": 3500,
      "status": "open",
      "...": "Same structure as invoice.created"
    },
    "previous_attributes": {
      "status": "open"
    }
  }
}

Note: The previous_attributes object is automatically detected from model changes. If no publishable fields changed during the failed payment attempt, previous_attributes will be an empty object.


invoice.payment_action_required

Triggered When

  • Payment requires additional customer action to complete (e.g. 3D Secure authentication, bank redirect)
  • The payment gateway returns a requires_action status

Payload

{
  "type": "invoice.payment_action_required",
  "id": "evt_Av7ARtKkpnI99lD7sw0oD1h7",
  "created": 1676985200,
  "data": {
    "object": {
      "object": "invoice",
      "id": 100001,
      "amount_due": 3500,
      "amount_paid": 0,
      "amount_remaining": 3500,
      "hosted_invoice_url": null,
      "status": "open",
      "...": "Same structure as invoice.created"
    }
  }
}

Note: This event does not include previous_attributes. Since the invoice status is open, the payment_link field will be present — use it to redirect the customer to complete the required payment action.


Payload Fields

All invoice events share the same core payload structure under data.object. Fields are sorted alphabetically with object and id prepended.

Invoice Object

FieldTypeDescription
objectstringAlways "invoice"
idintegerThe invoice ID
address_billingobject | nullCustomer's default billing address, formatted through AddressResource (nested Address object)
address_shippingobject | nullShipping address from the subscription, formatted through AddressResource (nested Address object)
amount_dueintegerAmount in cents that is owed on this invoice
amount_paidintegerAmount in cents that has been paid
amount_remainingintegerAmount in cents remaining to be paid
auto_advancebooleanWhether the invoice will automatically advance to the next status
billingstring | integerCollection method ("charge_automatically" or "send_invoice")
billing_addressobject | nullCustomer's first billing-type address, formatted through AddressResource. Equivalent to address_billing — both are sourced from the customer's billing address.
billing_reasonstring | nullReason the invoice was created (e.g. "subscription_create", "subscription_cycle")
chargeobject | nullAssociated charge details (present when a payment was attempted)
charge_idinteger | nullAssociated charge ID
couponobject | nullApplied coupon details (when a discount is applied)
createdstringTimestamp when the invoice was created
currencystringThree-letter ISO currency code (e.g. "usd")
customerobject | nullCustomer who owns this invoice (nested Customer object)
customer_addressobject | nullCustomer's billing address snapshot stored on the invoice at creation time. This is a raw database record — field structure differs from the AddressResource format used in address_billing. Includes internal fields such as user_id, account_id, phone, metadata, old_provider_id, pelcro_sync_last_at, pelcro_sync_failed_reason, deleted_at, and attributes.
customer_shippingobject | nullCustomer's shipping address snapshot stored on the invoice at creation time. This is a raw database record — field structure differs from the AddressResource format used in address_shipping. Includes internal fields such as user_id, account_id, phone, metadata, old_provider_id, pelcro_sync_last_at, pelcro_sync_failed_reason, deleted_at, and attributes.
days_to_creationinteger | nullDays until the invoice will be created (for upcoming invoices)
default_tax_ratesobject | nullDefault tax rates applied to the invoice
deleted_atstring | nullTimestamp when the invoice was deleted (soft delete)
discount_idinteger | nullApplied discount ID
ending_balanceintegerCustomer's ending balance after the invoice is finalized
finalized_atstring | nullTimestamp when the invoice was finalized
hosted_invoice_urlstring | nullURL for an externally-hosted invoice page (null for Pelcro Billing Engine accounts)
invoice_pdfstringURL to download the invoice PDF
is_importedboolean | nullWhether the invoice was imported from an external system
linesobject | nullInvoice line items (nested list of Line Item objects)
marked_uncollectible_atstring | nullTimestamp when the invoice was marked uncollectible
numberstring | nullUnique invoice number
object_idstring | nullExternal identifier (null for Pelcro Billing Engine accounts)
orderobject | nullAssociated order details (for e-commerce invoices)
paid_atstring | nullTimestamp when the invoice was paid
payment_linkstringConditional. URL for the Pelcro-hosted payment page. Only present when status is open or past_due — absent for all other statuses.
period_endstringEnd of the billing period covered by the invoice
period_startstringStart of the billing period covered by the invoice
planobjectAssociated plan details (id, nickname, interval)
post_payment_credit_notes_amountintegerTotal amount of credit notes issued after payment
pre_payment_credit_notes_amountintegerTotal amount of credit notes issued before payment
productobjectAssociated product details (id, name, name_internal, description, entitlements, type)
purchase_orderstring | nullPurchase order number
site_idintegerSite ID where the invoice was created
sourceobject | nullPayment source used (nested Source object)
starting_balanceintegerCustomer's starting balance before the invoice
statusstringInvoice status (see Invoice Statuses below)
subscription_idinteger | nullAssociated subscription ID
subtotalintegerSubtotal in cents before tax and discounts
taxinteger | nullTotal tax amount in cents
tax_percentnumber | nullTax percentage applied
totalintegerTotal amount in cents
total_tax_amountsobject | nullBreakdown of tax amounts by rate
updated_atstringTimestamp when the invoice was last updated
voided_atstring | nullTimestamp when the invoice was voided

Line Item Object

Each line item in the lines.data array contains:

FieldTypeDescription
objectstringAlways "line_item"
idintegerLine item ID
amountintegerAmount in cents for this line item
created_atstringTimestamp when the line item was created
currencystringThree-letter ISO currency code
descriptionstring | nullDescription of the line item
metadataobject | nullAdditional metadata
object_idstring | nullExternal identifier (null for Pelcro Billing Engine accounts)
periodobjectBilling period (start and end as unix timestamps)
quantityintegerQuantity of items
subscriptionobject | nullFully expanded subscription object including nested customer, default_source, plan (with product), address_shipping, latest_invoice, and schedules (with phases and phase plans)
tax_amountsobject | nullTax amounts for this line item
tax_ratesobject | nullTax rates applied to this line item

Charge Object

When a payment was attempted, the charge object includes:

FieldTypeDescription
idintegerCharge ID
offlinebooleanWhether this was an offline payment
payment_categorystringPayment method category (e.g. "card", "bank")
referencestring | nullExternal payment reference

Invoice Statuses

StatusDescription
scheduledInvoice is scheduled for a future billing cycle and not yet open for payment
draftInvoice is a draft and not yet finalized
openInvoice is finalized and awaiting payment
paidInvoice has been paid in full
past_dueInvoice is past its due date and still unpaid
uncollectibleInvoice has been marked as uncollectible
voidInvoice has been voided

Events with Previous Attributes

The following events include a previous_attributes object showing changed field values before the update:

EventHow Previous Attributes Are Determined
invoice.updatedPassed explicitly from the domain event — contains the previous values of changed publishable fields
invoice.payment_succeededAuto-detected from model changes via getChanges() — filtered to publishable fields only
invoice.payment_failedAuto-detected from model changes via getChanges() — filtered to publishable fields only

Events without previous_attributes: invoice.created, invoice.upcoming, invoice.payment_action_required.


Invoice Lifecycle

The typical lifecycle of an invoice follows one of these sequences:

Successful payment (charge automatically):

  1. invoice.created - Invoice generated
  2. invoice.payment_succeeded - Payment collected
  3. invoice.updated - Status changed to paid

Successful payment (send invoice):

  1. invoice.upcoming - Advance notice of upcoming invoice
  2. invoice.created - Invoice generated and sent
  3. invoice.payment_succeeded - Customer pays the invoice
  4. invoice.updated - Status changed to paid

Failed payment:

  1. invoice.created - Invoice generated
  2. invoice.payment_failed - Payment attempt failed
  3. invoice.updated - Status may change (e.g. to uncollectible)

Payment requiring action (3D Secure):

  1. invoice.created - Invoice generated
  2. invoice.payment_action_required - Customer must complete authentication
  3. invoice.payment_succeeded - Payment completed after authentication
  4. invoice.updated - Status changed to paid