---
updatedAt: 2026-05-12T20:14:45.000Z
agentTools:
  projectIndex: https://developer.fastspring.com/llms.txt
---

# Unsuccessful Subscription Rebills

subscription.charge.failed

# Overview of the `subscription.charge.failed` webhook

When a `subscription.charge.failed` event is triggered, FastSpring sends a webhook payload with details about the rebill failure. The payload contains the failure reason, which is most commonly due to an expired or declined payment method.

If <a href="https://developer.fastspring.com/reference/webhook-expansion">webhook expansion</a> is enabled, the payload includes full account and product objects. Otherwise, the payload includes only the corresponding IDs.

This page includes:

* A full sample payload showing a populated `subscription.charge.failed` webhook
* A detailed table listing every payload property, including name, type, and description
* Notes on when each field is included, omitted, or dependent on specific update types

Browse the table sections below or use the quick links to jump to a specific group of fields.

> **Tip:** Not all fields are always included. Refer to the [Payload properties](#payload-properties) table to understand when a field appears.

<div class="spacer-md" />

# Webhook payload example (expansion enabled)

When a `subscription.charge.failed` event is triggered, the webhook sends the following JSON payload:

```json
{
    "reason": "EXPIRED_CARD",
    "account": {
      "id": "abCdE1FGH2Hij3KLMnOpqR",
      "account": "abCdE1FGH2Hij3KLMnOpqR",
      "contact": {
        "first": "Jane",
        "last": "Doe",
        "email": "jane.doe@company.com",
        "company": "Company Inc.",
        "phone": "5555555",
        "subscribed": true
      },
      "address": {
        "address line 1": "123 Business Rd",
        "address line 2": "Floor 4",
        "city": "Metropolis",
        "country": "US",
        "postal code": "12345",
        "region": "US-NY",
        "region custom": null,
        "company": "Company Inc."
      },
      "language": "en",
      "country": "US",
      "lookup": {
        "global": "lookup-001"
      },
      "url": "https://company.onfastspring.com/account"
    },
    "subscription": {
      "id": "1abc2DE_FGhIjKLm3NoPQR",
      "quote": null,
      "subscription": "1abc2DE_FGhIjKLm3NoPQR",
      "active": true,
      "state": "trial",
      "isSubscriptionEligibleForPauseByBuyer": false,
      "isPauseScheduled": false,
      "changed": 1749236508799,
      "changedValue": 1749236508799,
      "changedInSeconds": 1749236508,
      "changedDisplay": "6/6/25",
      "changedDisplayISO8601": "2025-06-06",
      "changedDisplayEmailEnhancements": "Jun 06, 2025",
      "changedDisplayEmailEnhancementsWithTime": "Jun 06, 2025 07:01:48 PM",
      "paymentMethodAction": "none",
      "live": false,
      "currency": "USD",
      "declineReason": null,
      "account": "abCdE1FGH2Hij3KLMnOpqR",
      "product": "furious-falcon",
      "sku": "SKU-FF-102",
      "display": "Furious Falcon",
      "quantity": 1,
      "adhoc": false,
      "autoRenew": true,
      "price": 14.95,
      "priceDisplay": "$14.95",
      "priceInPayoutCurrency": 14.95,
      "priceInPayoutCurrencyDisplay": "$14.95",
      "discount": 0,
      "discountDisplay": "$0.00",
      "discountInPayoutCurrency": 0,
      "discountInPayoutCurrencyDisplay": "$0.00",
      "subtotal": 14.95,
      "subtotalDisplay": "$14.95",
      "subtotalInPayoutCurrency": 14.95,
      "subtotalInPayoutCurrencyDisplay": "$14.95",
      "next": 1749340800000,
      "nextValue": 1749340800000,
      "nextInSeconds": 1749340800,
      "nextDisplay": "6/8/25",
      "nextDisplayISO8601": "2025-06-08",
      "end": null,
      "endValue": null,
      "endInSeconds": null,
      "endDisplay": null,
      "endDisplayISO8601": null,
      "canceledDate": null,
      "canceledDateValue": null,
      "canceledDateInSeconds": null,
      "canceledDateDisplay": null,
      "canceledDateDisplayISO8601": null,
      "deactivationDate": 1749945600000,
      "deactivationDateValue": 1749945600000,
      "deactivationDateInSeconds": 1749945600,
      "deactivationDateDisplay": "6/15/25",
      "deactivationDateDisplayISO8601": "2025-06-15",
      "sequence": 1,
      "periods": null,
      "remainingPeriods": null,
      "begin": 1749236450805,
      "beginValue": 1749236450805,
      "beginInSeconds": 1749236450,
      "beginDisplay": "6/6/25",
      "beginDisplayISO8601": "2025-06-06",
      "beginDisplayEmailEnhancements": "Jun 06, 2025",
      "beginDisplayEmailEnhancementsWithTime": "Jun 06, 2025 07:00:50 PM",
      "nextDisplayEmailEnhancements": "Jun 08, 2025",
      "nextDisplayEmailEnhancementsWithTime": "Jun 08, 2025 12:00:00 AM",
      "intervalUnit": "month",
      "intervalUnitAbbreviation": "mo",
      "intervalLength": 1,
      "intervalLengthGtOne": false,
      "nextChargeCurrency": "USD",
      "nextChargeDate": 1749340800000,
      "nextChargeDateValue": 1749340800000,
      "nextChargeDateInSeconds": 1749340800,
      "nextChargeDateDisplay": "6/8/25",
      "nextChargeDateDisplayISO8601": "2025-06-08",
      "nextChargePreTax": 13.84,
      "nextChargePreTaxDisplay": "$13.84",
      "nextChargePreTaxInPayoutCurrency": 13.84,
      "nextChargePreTaxInPayoutCurrencyDisplay": "$13.84",
      "nextChargeTotal": 14.95,
      "nextChargeTotalDisplay": "$14.95",
      "nextChargeTotalInPayoutCurrency": 14.95,
      "nextChargeTotalInPayoutCurrencyDisplay": "$14.95",
      "cancellationSetting": {
        "cancellation": "AFTER_PAYMENT_FAILURE",
        "intervalUnit": "week",
        "intervalLength": 1
      },
      "fulfillments": {
        "instructions": "<p>Thank you for subscribing to Example Subscription Monthly. Please download the installer file using the button or link found on this page. Your license key is also displayed here.</p>"
      },
      "instructions": [
        {
          "type": "trial",
          "trialType": "PAID",
          "isNotTrial": false,
          "periodStartDate": 1749340800000,
          "periodStartDateValue": 1749340800000,
          "periodStartDateInSeconds": 1749340800,
          "periodStartDateDisplay": "6/8/25",
          "periodStartDateDisplayISO8601": "2025-06-08",
          "periodEndDate": 1749254400000,
          "periodEndDateValue": 1749254400000,
          "periodEndDateInSeconds": 1749254400,
          "periodEndDateDisplay": "6/7/25",
          "periodEndDateDisplayISO8601": "2025-06-07",
          "discountDurationUnit": "day",
          "discountDurationLength": 1,
          "discountPercent": 100,
          "discountPercentValue": 100,
          "discountPercentDisplay": "100%",
          "unitDiscount": 14.95,
          "unitDiscountDisplay": "$14.95",
          "unitDiscountInPayoutCurrency": 14.95,
          "unitDiscountInPayoutCurrencyDisplay": "$14.95",
          "discountTotal": 14.95,
          "discountTotalDisplay": "$14.95",
          "discountTotalInPayoutCurrency": 14.95,
          "discountTotalInPayoutCurrencyDisplay": "$14.95",
          "total": 0,
          "totalDisplay": "$0.00",
          "totalInPayoutCurrency": 0,
          "totalInPayoutCurrencyDisplay": "$0.00",
          "totalWithTaxes": 14.95,
          "totalWithTaxesDisplay": "$14.95",
          "totalWithTaxesInPayoutCurrency": 14.95,
          "totalWithTaxesInPayoutCurrencyDisplay": "$14.95",
          "price": 14.95,
          "priceDisplay": "$14.95",
          "priceInPayoutCurrency": 14.95,
          "priceInPayoutCurrencyDisplay": "$14.95",
          "priceTotal": 14.95,
          "priceTotalDisplay": "$14.95",
          "priceTotalInPayoutCurrency": 14.95,
          "priceTotalInPayoutCurrencyDisplay": "$14.95",
          "unitPrice": 0,
          "unitPriceDisplay": "$0.00",
          "unitPriceInPayoutCurrency": 0,
          "unitPriceInPayoutCurrencyDisplay": "$0.00"
        },
        {
          "product": "furious-falcon",
          "type": "regular",
          "isNotTrial": true,
          "periodStartDate": 1749340800000,
          "periodStartDateValue": 1749340800000,
          "periodStartDateInSeconds": 1749340800,
          "periodStartDateDisplay": "6/8/25",
          "periodStartDateDisplayISO8601": "2025-06-08",
          "periodEndDate": null,
          "periodEndDateValue": null,
          "periodEndDateInSeconds": null,
          "periodEndDateDisplay": null,
          "periodEndDateDisplayISO8601": null,
          "intervalUnit": "month",
          "intervalLength": 1,
          "discountPercent": 0,
          "discountPercentValue": 0,
          "discountPercentDisplay": "0%",
          "discountTotal": 0,
          "discountTotalDisplay": "$0.00",
          "discountTotalInPayoutCurrency": 0,
          "discountTotalInPayoutCurrencyDisplay": "$0.00",
          "unitDiscount": 0,
          "unitDiscountDisplay": "$0.00",
          "unitDiscountInPayoutCurrency": 0,
          "unitDiscountInPayoutCurrencyDisplay": "$0.00",
          "price": 14.95,
          "priceDisplay": "$14.95",
          "priceInPayoutCurrency": 14.95,
          "priceInPayoutCurrencyDisplay": "$14.95",
          "priceTotal": 14.95,
          "priceTotalDisplay": "$14.95",
          "priceTotalInPayoutCurrency": 14.95,
          "priceTotalInPayoutCurrencyDisplay": "$14.95",
          "unitPrice": 14.95,
          "unitPriceDisplay": "$14.95",
          "unitPriceInPayoutCurrency": 14.95,
          "unitPriceInPayoutCurrencyDisplay": "$14.95",
          "total": 14.95,
          "totalDisplay": "$14.95",
          "totalInPayoutCurrency": 14.95,
          "totalInPayoutCurrencyDisplay": "$14.95",
          "totalWithTaxes": 14.95,
          "totalWithTaxesDisplay": "$14.95",
          "totalWithTaxesInPayoutCurrency": 14.95,
          "totalWithTaxesInPayoutCurrencyDisplay": "$14.95"
        }
      ],
      "initialOrderId": "S6XqptRvRJmzS1qceZKcNA",
      "initialOrderReference": "ABC1234567-8910-11121"
    }
  }
```

<div class="spacer-md" />

# Navigate this webhook

The `subscription.charge.failed` webhook payload includes dozens of fields. Use the cards below to jump to a specific section of the property reference.

<Cards columns={3}>
  <Card title="Event Metadata" href="#event-metadata" icon="fa-circle-exclamation" />
  <Card title="Account Object" href="#account-object" icon="fa-user" />
  <Card title="Subscription Object" href="#subscription-object" icon="fa-id-card" />
  <Card title="Pricing" href="#pricing" icon="fa-tags" />
  <Card title="Rebill and Expiration" href="#rebill-and-expiration" icon="fa-rotate" />
  <Card title="Cancellation and Deactivation" href="#cancellation-and-deactivation" icon="fa-ban" />
  <Card title="Billing Schedule" href="#billing-schedule" icon="fa-calendar" />
  <Card title="Next Charge Details" href="#next-charge-details" icon="fa-receipt" />
  <Card title="Cancellation Settings" href="#cancellation-settings" icon="fa-gear" />
  <Card title="Fulfillment Object" href="#fulfillment-object" icon="fa-download" />
  <Card title="Instructions Array" href="#instructions-array" icon="fa-list-ol" />
</Cards>

<div class="spacer-md" />

# Payload properties

<span id="navigate-this-webhook" />

All fields below are included in the `subscription.charge.failed` webhook payload. Fields are grouped into categories for easier navigation.

<table style={{ tableLayout: "fixed", width: "100%" }}>
  <colgroup>
    <col style={{ width: "35%" }} />
    <col style={{ width: "10%" }} />
    <col style={{ width: "55%" }} />
  </colgroup>
  <thead>
    <tr>
      <th>Name</th>
      <th>Type</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>

<tr id="event-metadata" style={{ borderTop: "4px solid #ddd" }}>
    <td colSpan="3" style={{ paddingTop: "4px", fontWeight: "600" }}>
        <a href="#navigate-this-webhook">Event Metadata</a>
    </td>
</tr>

<tr><td>reason</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Failure reason code for the charge, such as `EXPIRED_CARD`. See <a href="#reason-codes">Reason codes</a> for the full list and suggested actions.</td></tr>


<tr id="account-object" style={{ borderTop: "4px solid #ddd" }}>
    <td colSpan="3" style={{ paddingTop: "4px", fontWeight: "600" }}>
    <a href="#navigate-this-webhook">Account Object</a>
    </td>
</tr>

<tr><td>account</td><td>object</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Customer account object containing ID, contact information, language, country, and account lookup values</td></tr>
<tr><td>account.id</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>FastSpring-generated customer account ID</td></tr>
<tr><td>account.account</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Duplicate of `account.id` for backward compatibility</td></tr>
<tr><td>account.contact.first</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>First name of the customer</td></tr>
<tr><td>account.contact.last</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Last name of the customer</td></tr>
<tr><td>account.contact.email</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Email address of the customer</td></tr>
<tr><td>account.contact.company</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Company name of the customer when provided</td></tr>
<tr><td>account.contact.phone</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Phone number of the customer when provided</td></tr>
<tr><td>account.contact.subscribed</td><td>boolean</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Whether the account contact is subscribed to updates</td></tr>
<tr><td>account.address.address line 1</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Primary street address line</td></tr>
<tr><td>account.address.address line 2</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Secondary street address line</td></tr>
<tr><td>account.address.city</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>City of the account address</td></tr>
<tr><td>account.address.country</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Two-letter ISO country code for the address</td></tr>
<tr><td>account.address.postal code</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Postal or ZIP code of the address</td></tr>
<tr><td>account.address.region</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Region or state of the address</td></tr>
<tr><td>account.address.region custom</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Custom region when not standard</td></tr>
<tr><td>account.address.company</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Company associated with the address</td></tr>
<tr><td>account.language</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Two-letter ISO code for the customer’s preferred language</td></tr>
<tr><td>account.country</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Two-letter ISO country code for the customer</td></tr>
<tr><td>account.lookup.global</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Globally unique public ID used to look up the account in customer-facing portals</td></tr>
<tr><td>account.url</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Customer-facing account management URL</td></tr>

<tr id="subscription-object" style={{ borderTop: "4px solid #ddd" }}>
    <td colSpan="3" style={{ paddingTop: "4px", fontWeight: "600" }}>
        <a href="#navigate-this-webhook">Subscription Object</a>
    </td>
</tr>

<tr><td>subscription.id</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Unique identifier for the subscription</td></tr>
<tr><td>subscription.quote</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Associated quote ID if created from a quote</td></tr>
<tr><td>subscription.subscription</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Duplicate of `subscription.id` for backward compatibility</td></tr>
<tr><td>subscription.active</td><td>boolean</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Whether the subscription is currently active</td></tr>
<tr><td>subscription.state</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Current state such as `trial`, `active`, `overdue`, `deactivated`, or `canceled`</td></tr>
<tr><td>subscription.isSubscriptionEligibleForPauseByBuyer</td><td>boolean</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Whether the buyer can pause the subscription from their account</td></tr>
<tr><td>subscription.isPauseScheduled</td><td>boolean</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Whether a pause has been scheduled to take effect on the next rebill</td></tr>
<tr><td>paymentMethodAction</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Whether the payment method changed, such as `updated` or `none`</td></tr>
<tr><td>subscription.live</td><td>boolean</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Whether the subscription was created in live mode</td></tr>
<tr><td>subscription.currency</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Three-letter ISO currency code for the subscription</td></tr>
<tr><td>subscription.declineReason</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Reason for payment decline when applicable</td></tr>
<tr><td>subscription.account</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Account ID associated with the subscription</td></tr>
<tr><td>subscription.product</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Product ID associated with the subscription</td></tr>
<tr><td>subscription.sku</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>SKU of the subscription product</td></tr>
<tr><td>subscription.display</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Customer-facing display name of the subscription</td></tr>
<tr><td>subscription.quantity</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Quantity of the subscription product</td></tr>
<tr><td>subscription.adhoc</td><td>boolean</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Whether the subscription is manually billed outside standard flows</td></tr>
<tr><td>subscription.autoRenew</td><td>boolean</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Whether the subscription automatically renews</td></tr>


<tr id="pricing" style={{ borderTop: "4px solid #ddd" }}>
  <td colSpan="3" style={{ paddingTop: "4px", fontWeight: "600" }}>
    <a href="#navigate-this-webhook">Pricing</a>
  </td>
</tr>

<tr><td>subscription.price</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Amount charged per billing period in transaction currency</td></tr>
<tr><td>subscription.priceDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `price`</td></tr>
<tr><td>subscription.priceInPayoutCurrency</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Subscription price in payout currency</td></tr>
<tr><td>subscription.priceInPayoutCurrencyDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `priceInPayoutCurrency`</td></tr>
<tr><td>subscription.discount</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Total discount applied to the subscription in transaction currency</td></tr>
<tr><td>subscription.discountDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `discount`</td></tr>
<tr><td>subscription.discountInPayoutCurrency</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Discount amount in payout currency</td></tr>
<tr><td>subscription.discountInPayoutCurrencyDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `discountInPayoutCurrency`</td></tr>
<tr><td>subscription.subtotal</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Subtotal amount before taxes</td></tr>
<tr><td>subscription.subtotalDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `subtotal`</td></tr>
<tr><td>subscription.subtotalInPayoutCurrency</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Subtotal amount in payout currency</td></tr>
<tr><td>subscription.subtotalInPayoutCurrencyDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `subtotalInPayoutCurrency`</td></tr>


<tr id="rebill-and-expiration" style={{ borderTop: "4px solid #ddd" }}>
  <td colSpan="3" style={{ paddingTop: "4px", fontWeight: "600" }}>
    <a href="#navigate-this-webhook">Rebill and Expiration</a>
  </td>
</tr>

<tr><td>subscription.next</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in milliseconds for the next scheduled billing</td></tr>
<tr><td>subscription.nextValue</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Duplicate of `next` for backward compatibility</td></tr>
<tr><td>subscription.nextInSeconds</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in seconds for the next scheduled billing</td></tr>
<tr><td>subscription.nextDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of the next scheduled billing date</td></tr>
<tr><td>subscription.nextDisplayISO8601</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Next scheduled billing date in ISO 8601 format</td></tr>
<tr><td>subscription.end</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in milliseconds when the subscription ends, if scheduled</td></tr>
<tr><td>subscription.endValue</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Duplicate of `end` for backward compatibility</td></tr>
<tr><td>subscription.endInSeconds</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in seconds when the subscription ends, if scheduled</td></tr>
<tr><td>subscription.endDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of the subscription end date</td></tr>
<tr><td>subscription.endDisplayISO8601</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Subscription end date in ISO 8601 format</td></tr>


<tr id="cancellation-and-deactivation" style={{ borderTop: "4px solid #ddd" }}>
  <td colSpan="3" style={{ paddingTop: "4px", fontWeight: "600" }}>
    <a href="#navigate-this-webhook">Cancellation and Deactivation</a>
  </td>
</tr>

<tr><td>subscription.canceledDate</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in milliseconds when the subscription was canceled, if applicable</td></tr>
<tr><td>subscription.canceledDateValue</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Duplicate of `canceledDate` for backward compatibility</td></tr>
<tr><td>subscription.canceledDateInSeconds</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in seconds when the subscription was canceled</td></tr>
<tr><td>subscription.canceledDateDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `canceledDate`</td></tr>
<tr><td>subscription.canceledDateDisplayISO8601</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Cancellation date in ISO 8601 format</td></tr>
<tr><td>subscription.deactivationDate</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in milliseconds when the subscription deactivates, if applicable</td></tr>
<tr><td>subscription.deactivationDateValue</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Duplicate of `deactivationDate` for backward compatibility</td></tr>
<tr><td>subscription.deactivationDateInSeconds</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in seconds when the subscription deactivates</td></tr>
<tr><td>subscription.deactivationDateDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `deactivationDate`</td></tr>
<tr><td>subscription.deactivationDateDisplayISO8601</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Deactivation date in ISO 8601 format</td></tr>


<tr id="billing-schedule" style={{ borderTop: "4px solid #ddd" }}>
  <td colSpan="3" style={{ paddingTop: "4px", fontWeight: "600" }}>
    <a href="#navigate-this-webhook">Billing Schedule</a>
  </td>
</tr>

<tr><td>subscription.sequence</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Sequence number of the current billing period</td></tr>
<tr><td>subscription.periods</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Total number of billing periods for the subscription</td></tr>
<tr><td>subscription.remainingPeriods</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Number of billing periods remaining</td></tr>
<tr><td>subscription.begin</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in milliseconds when the subscription began</td></tr>
<tr><td>subscription.beginValue</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Duplicate of `begin` for backward compatibility</td></tr>
<tr><td>subscription.beginInSeconds</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in seconds when the subscription began</td></tr>
<tr><td>subscription.beginDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of the subscription start date</td></tr>
<tr><td>subscription.beginDisplayISO8601</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Start date in ISO 8601 format</td></tr>
<tr><td>subscription.beginDisplayEmailEnhancements</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>User-friendly subscription start date for email display</td></tr>
<tr><td>subscription.beginDisplayEmailEnhancementsWithTime</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>User-friendly subscription start date and time for email display</td></tr>
<tr><td>nextDisplayEmailEnhancements</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>User-friendly next charge date for email display</td></tr>
<tr><td>nextDisplayEmailEnhancementsWithTime</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>User-friendly next charge date and time for email display</td></tr>
<tr><td>subscription.intervalUnit</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Unit of time between billing periods such as `month`</td></tr>
<tr><td>subscription.intervalUnitAbbreviation</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Abbreviated unit of time between billing periods such as `mo`</td></tr>
<tr><td>subscription.intervalLength</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Number of `intervalUnit`s between charges</td></tr>
<tr><td>subscription.intervalLengthGtOne</td><td>boolean</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Whether `intervalLength` is greater than one</td></tr>


<tr id="next-charge-details" style={{ borderTop: "4px solid #ddd" }}>
  <td colSpan="3" style={{ paddingTop: "4px", fontWeight: "600" }}>
    <a href="#navigate-this-webhook">Next Charge Details</a>
  </td>
</tr>

<tr><td>nextChargeCurrency</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Three-letter ISO currency code for the next scheduled charge</td></tr>
<tr><td>nextChargeDate</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in milliseconds of the next scheduled charge</td></tr>
<tr><td>nextChargeDateValue</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Duplicate of `nextChargeDate` for backward compatibility</td></tr>
<tr><td>nextChargeDateInSeconds</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in seconds of the next scheduled charge</td></tr>
<tr><td>nextChargeDateDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of the next scheduled charge date</td></tr>
<tr><td>nextChargeDateDisplayISO8601</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Next scheduled charge date in ISO 8601 format</td></tr>
<tr><td>nextChargePreTax</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Pre-tax amount for the next charge in transaction currency</td></tr>
<tr><td>nextChargePreTaxDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `nextChargePreTax`</td></tr>
<tr><td>nextChargePreTaxInPayoutCurrency</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Pre-tax charge amount in payout currency</td></tr>
<tr><td>nextChargePreTaxInPayoutCurrencyDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `nextChargePreTaxInPayoutCurrency`</td></tr>
<tr><td>nextChargeTotal</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Total charge amount including tax when applicable</td></tr>
<tr><td>nextChargeTotalDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `nextChargeTotal`</td></tr>
<tr><td>nextChargeTotalInPayoutCurrency</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Total charge amount in payout currency</td></tr>
<tr><td>nextChargeTotalInPayoutCurrencyDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `nextChargeTotalInPayoutCurrency`</td></tr>


<tr id="cancellation-settings" style={{ borderTop: "4px solid #ddd" }}>
  <td colSpan="3" style={{ paddingTop: "4px", fontWeight: "600" }}>
    <a href="#navigate-this-webhook">Cancellation Settings</a>
  </td>
</tr>

<tr><td>cancellationSetting.cancellation</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Cancellation trigger such as `AFTER_PAYMENT_FAILURE` or `AFTER_LAST_NOTIFICATION`</td></tr>
<tr><td>cancellationSetting.intervalUnit</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Time unit used with `intervalLength` to determine when cancellation occurs</td></tr>
<tr><td>cancellationSetting.intervalLength</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Number of `intervalUnit`s to wait before cancellation</td></tr>


<tr id="fulfillment-object" style={{ borderTop: "4px solid #ddd" }}>
  <td colSpan="3" style={{ paddingTop: "4px", fontWeight: "600" }}>
    <a href="#navigate-this-webhook">Fulfillment Object</a>
  </td>
</tr>

<tr><td>fulfillments</td><td>object</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Container for fulfillment data associated with the subscription</td></tr>
<tr><td>fulfillments.instructions</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>HTML content with delivery instructions such as download links or license keys</td></tr>


<tr id="instructions-array" style={{ borderTop: "4px solid #ddd" }}>
  <td colSpan="3" style={{ paddingTop: "4px", fontWeight: "600" }}>
    <a href="#navigate-this-webhook">Instructions Array</a>
  </td>
</tr>

<tr><td>instructions</td><td>array</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Array of billing instruction objects for each subscription period</td></tr>
<tr><td>instructions.product</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Product ID for the billing instruction period (only present on regular instructions)</td></tr>
<tr><td>instructions.type</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Type of billing instruction such as `trial` or `regular`</td></tr>
<tr><td>instructions.trialType</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Trial type such as `PAID`, `FREE_WITH_PAYMENT`, or `FREE_WITHOUT_PAYMENT`; present only on trial instructions</td></tr>
<tr><td>instructions.isNotTrial</td><td>boolean</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Whether the instruction is not part of a trial period</td></tr>
<tr><td>instructions.periodStartDate</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in milliseconds for the start of the billing period</td></tr>
<tr><td>instructions.periodStartDateValue</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Duplicate of `periodStartDate` for backward compatibility</td></tr>
<tr><td>instructions.periodStartDateInSeconds</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in seconds for the start of the billing period</td></tr>
<tr><td>instructions.periodStartDateDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `periodStartDate`</td></tr>
<tr><td>instructions.periodStartDateDisplayISO8601</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Start date in ISO 8601 format</td></tr>
<tr><td>instructions.periodEndDate</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in milliseconds for the end of the billing period, if known</td></tr>
<tr><td>instructions.periodEndDateValue</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Duplicate of `periodEndDate` for backward compatibility</td></tr>
<tr><td>instructions.periodEndDateInSeconds</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Timestamp in seconds for the end of the billing period, if known</td></tr>
<tr><td>instructions.periodEndDateDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `periodEndDate`</td></tr>
<tr><td>instructions.periodEndDateDisplayISO8601</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>End date in ISO 8601 format</td></tr>
<tr><td>instructions.discountDurationUnit</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Unit of time over which the discount applies, present only on trial instructions</td></tr>
<tr><td>instructions.discountDurationLength</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Length of the discount duration in `discountDurationUnit`s, present only on trial instructions</td></tr>
<tr><td>instructions.discountPercent</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Percentage discount applied during the period</td></tr>
<tr><td>instructions.discountPercentValue</td><td>integer</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Duplicate of `discountPercent` for backward compatibility</td></tr>
<tr><td>instructions.discountPercentDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `discountPercent`</td></tr>
<tr><td>instructions.unitDiscount</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Per-unit discount amount in transaction currency</td></tr>
<tr><td>instructions.unitDiscountDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `unitDiscount`</td></tr>
<tr><td>instructions.unitDiscountInPayoutCurrency</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Per-unit discount amount in payout currency</td></tr>
<tr><td>instructions.unitDiscountInPayoutCurrencyDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `unitDiscountInPayoutCurrency`</td></tr>
<tr><td>instructions.discountTotal</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Total discount applied during the period in transaction currency</td></tr>
<tr><td>instructions.discountTotalDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `discountTotal`</td></tr>
<tr><td>instructions.discountTotalInPayoutCurrency</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Total discount in payout currency</td></tr>
<tr><td>instructions.discountTotalInPayoutCurrencyDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `discountTotalInPayoutCurrency`</td></tr>
<tr><td>instructions.total</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Total amount due for the period in transaction currency</td></tr>
<tr><td>instructions.totalDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `total`</td></tr>
<tr><td>instructions.totalInPayoutCurrency</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Total amount in payout currency</td></tr>
<tr><td>instructions.totalInPayoutCurrencyDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `totalInPayoutCurrency`</td></tr>
<tr><td>instructions.totalWithTaxes</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Total charge amount including taxes in transaction currency</td></tr>
<tr><td>instructions.totalWithTaxesDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `totalWithTaxes`</td></tr>
<tr><td>instructions.totalWithTaxesInPayoutCurrency</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Total including taxes in payout currency</td></tr>
<tr><td>instructions.totalWithTaxesInPayoutCurrencyDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `totalWithTaxesInPayoutCurrency`</td></tr>
<tr><td>instructions.price</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>List price before discounts in transaction currency</td></tr>
<tr><td>instructions.priceDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `price`</td></tr>
<tr><td>instructions.priceInPayoutCurrency</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>List price in payout currency</td></tr>
<tr><td>instructions.priceInPayoutCurrencyDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `priceInPayoutCurrency`</td></tr>
<tr><td>instructions.priceTotal</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Total list price before discounts in transaction currency</td></tr>
<tr><td>instructions.priceTotalDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `priceTotal`</td></tr>
<tr><td>instructions.priceTotalInPayoutCurrency</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Total list price before discounts in payout currency</td></tr>
<tr><td>instructions.priceTotalInPayoutCurrencyDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `priceTotalInPayoutCurrency`</td></tr>
<tr><td>instructions.unitPrice</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Unit price after discounts in transaction currency</td></tr>
<tr><td>instructions.unitPriceDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `unitPrice`</td></tr>
<tr><td>instructions.unitPriceInPayoutCurrency</td><td>number</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Unit price after discounts in payout currency</td></tr>
<tr><td>instructions.unitPriceInPayoutCurrencyDisplay</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Formatted display of `unitPriceInPayoutCurrency`</td></tr>
<tr><td>subscription.initialOrderId</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Initial order ID that created the subscription</td></tr>
<tr><td>subscription.initialOrderReference</td><td>string</td><td style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>Initial order reference identifier for the subscription</td></tr>

  </tbody>
</table>

<div class="spacer-md" />

## Reason codes

The `reason` field in a `subscription.charge.failed` webhook reports why the charge failed. Codes are grouped below by category — open a section for the full list, suggested action, and whether FastSpring will retry automatically.

<Accordion title="Soft declines (4 codes)" icon="fa-sync-alt" iconColor="blue">

FastSpring retries these automatically per your dunning configuration. The buyer may also need to update their payment method.

| Code | Meaning | Suggested Action |
| --- | --- | --- |
| `DECLINED_SOFT` | General soft decline from the issuer. | FastSpring retries automatically. Prompt the buyer to update their payment method if retries fail. |
| `INCOMPLETE_PAYMENT` | Payment process was not completed. | FastSpring retries automatically. No buyer action required. |
| `INSUFFICIENT_FUNDS` | Not enough funds in the account. | FastSpring retries automatically. Prompt the buyer to update their payment method. |
| `VOICE_AUTH` | Issuer requires voice authorization to proceed. | Ask the buyer to contact their bank. |

</Accordion>

<Accordion title="Hard declines (6 codes)" icon="fa-ban" iconColor="red">

These will not resolve on retry. Prompt the buyer to update their payment method or contact their bank.

| Code | Meaning | Suggested Action |
| --- | --- | --- |
| `DECEASED` | Account holder is deceased. | No automated action. |
| `DECLINED` | Hard decline from the issuer. | Ask the buyer to use a different payment method or contact their bank. |
| `DISPUTED` | Payment is in active dispute. | No automated action. Contact FastSpring support if needed. |
| `EXPIRED_CARD` | Card has expired. | Ask the buyer to update their card. |
| `RESTRICTED` | Card or account is restricted. | Ask the buyer to contact their bank. |
| `UNSUPPORTED_COUNTRY` | Country is not supported for this payment method. | Contact FastSpring support. |

</Accordion>

<Accordion title="Payment method issues (4 codes)" icon="fa-credit-card" iconColor="orange">

Something specific is wrong with the payment details. Prompt the buyer to re-enter or verify their information. These will not resolve on retry.

| Code | Meaning | Suggested Action |
| --- | --- | --- |
| `API_INVALID_IBAN` | IBAN is invalid. | Ask the buyer to verify their IBAN. |
| `CC_ADDRESS_VERIFICATION` | Billing address verification (AVS) failed. | Ask the buyer to verify their billing address. |
| `CC_CVV` | CVV / security code mismatch. | Ask the buyer to re-enter their card details. |
| `INVALID_TOKEN` | Payment token is invalid or expired. | Ask the buyer to re-enter their payment details. |

</Accordion>

<Accordion title="Bank / ACH errors (2 codes)" icon="fa-university" iconColor="yellow">

These apply to ACH and bank account payment methods only. They will not resolve on retry.

| Code | Meaning | Suggested Action |
| --- | --- | --- |
| `ACH_INVALID_ACCOUNT_NUMBER` | Invalid ACH account number. | Ask the buyer to verify their account number. |
| `ACH_INVALID_ROUTING_NUMBER` | Invalid ACH routing number. | Ask the buyer to verify their routing number. |

</Accordion>

<Accordion title="Technical / temporary errors (9 codes)" icon="fa-cog" iconColor="green">

System or communication issues. Most are retried automatically; `API_INVALID_REQUEST_DATA` and `API_REFUND_FAILED` indicate problems that require FastSpring support.

| Code | Meaning | Suggested Action |
| --- | --- | --- |
| `API_BANK_ACCOUNT_LOGIN_ERROR` | Bank account login failed (open-banking flows). | FastSpring retries automatically. Ask the buyer to re-authenticate with their bank if retries fail. |
| `API_GENERIC_ERROR` | Generic API error from the payment provider. | FastSpring retries automatically. No buyer action required. |
| `API_INVALID_REQUEST_DATA` | Invalid data in the payment request. | Contact FastSpring support. Usually indicates an integration issue. |
| `API_REFUND_FAILED` | Refund attempt failed. | Contact FastSpring support. |
| `API_TRANSACTION_DECLINED` | Transaction declined via the payment API. | FastSpring retries automatically. If retries fail, ask the buyer to use a different payment method. |
| `CONNECTION` | Communication error with the payment processor. | FastSpring retries automatically. No buyer action required. |
| `INTERNAL_ERROR` | Internal processing error. | FastSpring retries automatically. No buyer action required. |
| `TIMEOUT` | Payment request timed out. | FastSpring retries automatically. No buyer action required. |
| `UNKNOWN` | Decline reason could not be determined. | FastSpring retries automatically. Treat as a soft decline. |

</Accordion>

<Accordion title="Risk & security (2 codes)" icon="fa-shield-alt" iconColor="purple">

Transaction was blocked by FastSpring or processor risk controls. These will not resolve on retry.

| Code | Meaning | Suggested Action |
| --- | --- | --- |
| `PROC_RISK` | Blocked by the payment processor's risk controls. | Ask the buyer to use a different payment method, or contact FastSpring support. |
| `RISK` | Blocked by FastSpring's risk controls. | Ask the buyer to use a different payment method, or contact FastSpring support. |

</Accordion>

> **Forward compatibility:** Treat any unrecognized `reason` value as a soft decline — FastSpring will continue dunning per your configuration. New codes may be added without breaking changes.