> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flexprice.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Paddle Integration Workflow

> End-to-end guide to the Paddle integration: subscription sync, customer checkout, and invoice auto-charge

## Overview

This guide walks you through the complete Paddle integration workflow in Flexprice.

**Division of responsibilities:**

* **Flexprice** handles usage metering, billing logic, plan configuration, and invoice generation.
* **Paddle** handles payment method collection, secure card storage, and automatic charging.

**What you'll achieve:**

* Subscriptions created in Flexprice automatically sync to Paddle as \$0 subscriptions
* Customers save their payment method via a Paddle-hosted checkout overlay
* Every invoice Flexprice generates is automatically charged against the saved card via Paddle

## Prerequisites

Before following this guide:

1. Complete the [Paddle Connection Setup](/integrations/paddle/connection-setup) — you need an active Paddle connection in your Flexprice environment.
2. Ensure your Paddle webhook destination is configured with both required events: `transaction.completed` and `subscription.activated`.

***

## How It Works

The integration runs in two phases:

**Phase 1 — Subscription & Card Setup**

```mermaid theme={null}
graph TD
    A[Create Flexprice subscription] --> B["Flexprice creates a zero-amount Paddle subscription"]
    B --> C["paddle_checkout_url and paddle_transaction_id in metadata"]
    C --> D[Share checkout URL with customer]
    D --> E[Customer saves card via Paddle overlay]
    E --> F[Paddle fires subscription.activated webhook]
    F --> G[Card is on file — ready to charge]
```

**Phase 2 — Invoice Auto-Charge**

```mermaid theme={null}
graph TD
    A[Flexprice billing cycle runs] --> B[Invoice generated]
    B --> C[Invoice synced to Paddle as a transaction]
    C --> D[Paddle auto-charges saved card]
    D --> E[Paddle fires transaction.completed webhook]
    E --> F[Flexprice reconciles invoice as paid]
```

***

## Step 1: Create a Subscription

Create a subscription in Flexprice as you normally would. Flexprice automatically creates a corresponding \$0 subscription in Paddle and returns the checkout URL in the response metadata.

```bash theme={null}
curl -X POST https://api.flexprice.io/v1/subscriptions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cust_01abc",
    "plan_id": "plan_01xyz",
    "currency": "USD",
    "billing_cadence": "RECURRING",
    "billing_period": "MONTHLY",
    "billing_period_count": 1,
    "start_date": "2025-06-01T00:00:00Z"
  }'
```

**Response** (abbreviated):

```json theme={null}
{
  "id": "sub_01def456",
  "status": "active",
  "customer_id": "cust_01abc",
  "plan_id": "plan_01xyz",
  "metadata": {
    "paddle_checkout_url": "https://checkout.paddle.com/checkout/custom/...",
    "paddle_transaction_id": "txn_01abc123def"
  }
}
```

The same metadata fields are also included in the `subscription.created` webhook payload.

<Note>
  `paddle_checkout_url` and `paddle_transaction_id` are only present when your Flexprice environment has an active Paddle connection. If these fields are missing, verify the connection in **Integrations → Paddle**.
</Note>

### With a Trial Period

To give the customer a free trial before billing begins, pass `trial_period_days`:

```bash theme={null}
curl -X POST https://api.flexprice.io/v1/subscriptions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cust_01abc",
    "plan_id": "plan_01xyz",
    "currency": "USD",
    "billing_cadence": "RECURRING",
    "billing_period": "MONTHLY",
    "billing_period_count": 1,
    "start_date": "2025-06-01T00:00:00Z",
    "trial_period_days": 14
  }'
```

The `paddle_checkout_url` is still returned immediately. Prompt the customer to save their card during the trial window — if no card is on file when the trial ends and the first invoice is generated, the auto-charge will fail.

For full details on trial behavior, see [Trialing](/docs/subscriptions/workflows/trialing).

***

## Step 2: Customer Saves Their Card

Extract `paddle_checkout_url` from the subscription response and share it with your customer — embed it in your app, send it via email, or redirect to it after signup.

When the customer opens the URL, they see a Paddle-hosted overlay to enter and save their payment method. No card data passes through Flexprice or your servers.

Once the card is saved:

* Paddle fires a `subscription.activated` webhook to your Flexprice webhook endpoint.
* Flexprice records that the payment method is on file.
* The customer is ready to be charged automatically on the next invoice.

<Note>
  The customer should save their card before Flexprice raises the first invoice, or the auto-charge will fail.
</Note>

<Note>
  `subscription.activated` here is a **Paddle webhook event** — it fires when the Paddle-side \$0 subscription is activated after card capture. This is separate from the Flexprice `subscription.activated` event, which fires when a Flexprice subscription transitions from `trialing` to `active`.
</Note>

***

## Step 3: Invoice Auto-Charge

No action is required from you for recurring charges. When Flexprice generates an invoice on the billing cycle:

1. The invoice is synced to Paddle as a transaction against the customer's account.
2. Paddle automatically charges the saved card.
3. Paddle fires a `transaction.completed` webhook to Flexprice.
4. Flexprice marks the invoice as paid and reconciles the amounts.

<Note>
  If Paddle applies tax on top of the invoice amount and the charged total exceeds the invoice total, Flexprice marks the invoice as **overpaid**. To avoid this, align your Paddle tax mode (internal vs. external) with your Flexprice pricing setup.
</Note>

***

## Customer & Address Sync

Customer sync is **on-demand** and **Flexprice → Paddle** only. Customers are synced to Paddle automatically when a subscription or invoice operation requires it.

Paddle requires a customer address (at minimum: a country code) to create transactions. If a customer is missing a country, invoice sync will fail with `"Paddle address ID not found"`. Add the country to the customer record in Flexprice before creating a subscription.

***

## Troubleshooting

| Issue                                              | Cause                                                                  | Solution                                                                                                                     |
| -------------------------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| No `paddle_checkout_url` in subscription metadata  | No active Paddle connection in environment                             | Verify connection in **Integrations → Paddle**                                                                               |
| `transaction.completed` webhook not received       | Webhook destination not configured or wrong events                     | Go to Paddle → Developers → Notifications and confirm both `transaction.completed` and `subscription.activated` are selected |
| Invoice not auto-charged                           | Customer hasn't saved card yet (`subscription.activated` not received) | Share the `paddle_checkout_url` with the customer; wait for card save before expecting auto-charges                          |
| Invoice sync fails — "Paddle address ID not found" | Customer missing country on their address                              | Add country to the customer's address in Flexprice                                                                           |
| Invoice marked overpaid                            | Paddle applied tax on top of the invoice amount                        | Align tax mode (internal vs. external) in Paddle settings with your Flexprice pricing                                        |
| Checkout URL expired or invalid                    | Paddle transaction expired                                             | Re-create the subscription or contact Flexprice support to re-sync the Paddle subscription                                   |
