Create subscriptions directly
Use direct subscription creation when your backend already has a Paypercut Customer and a reusable payment method, and you want your server to create the subscription without sending the customer through Checkout.
Checkout remains the recommended beginner path when the customer still needs to enter or confirm payment details. See Create subscriptions with Checkout.
What this page is for
This page explains the backend-controlled subscription creation path:
- required objects before creation;
- payment behavior at creation;
- what the API can return;
- what to store;
- which events to monitor after creation;
- common mistakes that lead to incorrect access decisions.
When to use direct creation
Use direct creation when:
- your backend already knows the Paypercut Customer;
- the customer already has a reusable payment method;
- you want your backend to create the subscription server-side;
- you can handle payment failure,
past_duestate, invoice events, and recovery.
Direct creation is common for migrations, admin-created subscriptions, backend provisioning flows, and products where payment details were collected earlier through a Setup Intent or another reusable-payment-method flow.
When not to use direct creation
Do not use direct creation when:
- the customer still needs to enter payment details;
- you want Paypercut to host or embed the signup flow;
- you only need a one-time payment;
- you need phase-based billing, intro pricing, or scheduled future changes;
- you cannot receive and process invoice events.
Use Create subscriptions with Checkout when Paypercut should collect the first payment and save the payment method during signup.
If you use a setup flow before direct creation, keep the steps separate in your UI and backend. Setup confirms that a reusable payment method exists. It does not confirm that a subscription was created, that the first invoice was paid, or that access should be granted.
Before you start
You need:
- A Paypercut Customer ID.
- A reusable payment method attached to that customer, or set as the customer's default payment method.
- A recurring Price, or inline
price_datathat matches the public API schema. - A webhook endpoint subscribed to invoice events.
- A database mapping between your internal user or account and the Paypercut Customer and Subscription.
Do not build this flow around send_invoice. The public schema mentions it as coming soon, but backend validation currently rejects invoice-based billing.
If you do not already have a reusable payment method for the customer, collect one before creating the subscription. You cannot create a reusable card payment method with only a secret-key backend request and raw card data.
Common ways to get the reusable payment method are:
| Method | Use when | What to store |
|---|---|---|
Checkout in setup mode |
You want Paypercut to host the payment-method collection flow without charging now. | The Customer ID and the saved Payment Method ID returned after completion. |
| Setup Intent or custom tokenization | You already have an approved customer-facing tokenization flow. | The Customer ID and the reusable Payment Method ID attached through that flow. |
| Existing saved method | The customer already has a reusable method from a previous Checkout or setup flow. | The existing Payment Method ID for that customer. |
For a hosted setup flow, create a Checkout Session in setup mode:
curl https://api.paypercut.io/v1/checkouts \
-X POST \
-H "Authorization: Bearer $PAYPERCUT_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: setup-payment-method-01" \
-d '{
"mode": "setup",
"ui_mode": "hosted",
"currency": "EUR",
"customer": "01KJQ34MWYH0TES77RDXA8T8TT",
"success_url": "https://example.com/billing/setup/success",
"cancel_url": "https://example.com/billing/setup/cancel"
}'
Send the customer to the returned Checkout Session url. After the customer completes the setup flow, retrieve the saved method from your backend:
curl "https://api.paypercut.io/v1/payment_methods?customer=01KJQ34MWYH0TES77RDXA8T8TT&limit=10" \
-H "Authorization: Bearer $PAYPERCUT_SECRET_KEY"
Use a Payment Method ID from that response as default_payment_method when creating the subscription.
Required objects
| Object | Why it matters |
|---|---|
| Customer | The subscription owner. |
| Reusable payment method | Used for the first charge and future renewal billing. |
| Product / Price | Defines the plan and recurring amount. |
| Internal account mapping | Lets your webhook handler grant or recover access for the right user or workspace. |
Actor ownership
| Actor | Responsibility |
|---|---|
| Your backend | Creates the subscription, stores IDs, handles events, and grants or recovers access. |
| Paypercut | Validates the Customer and payment method, creates the subscription, attempts payment collection, creates invoices, and sends events. |
| Customer | Already provided a reusable payment method before this flow starts. |
Direct creation flow
- Your backend creates or selects a Customer.
- Your backend confirms a reusable payment method exists for that Customer.
- Your backend creates the subscription with recurring items.
- Paypercut validates the request and payment method.
- Paypercut attempts payment according to
payment_behavior. - Your backend stores the returned
subscription_id. - Your webhook handler uses
invoice.paidandinvoice.payment_failedto grant, extend, or recover access.
Create the subscription
This example uses error_if_incomplete so the API only creates the subscription if the first payment succeeds. If payment fails, the API returns 402 and no subscription is created.
When you reference an existing catalog Price, keep the item recurring fields aligned with that Price. The public Subscriptions API currently requires the recurring shape on subscription items even when price points to a catalog Price.
curl https://api.paypercut.io/v1/subscriptions \
-X POST \
-H "Authorization: Bearer $PAYPERCUT_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-direct-subscription-01" \
-d '{
"customer": "01KJQ34MWYH0TES77RDXA8T8TT",
"currency": "USD",
"default_payment_method": "01HD7M6DRKZ4Q4QEVWJB0RC1S6",
"payment_behavior": "error_if_incomplete",
"items": [
{
"price": "01XXXXXXXXXXXXXXXXXXXXXXX",
"unit_amount": 2999,
"currency": "USD",
"recurring": {
"interval": "monthly",
"interval_count": 1,
"usage_type": "licensed"
}
}
],
"metadata": {
"internal_account_id": "acct_123"
}
}'
The example uses fields present in the public Subscriptions API schema. It does not include one-time setup-fee items because backend validation and the public ItemWithPrice OpenAPI shape do not currently agree on that request shape.
Payment behavior
payment_behavior controls the first collection attempt.
| Value | What happens | Use when |
|---|---|---|
error_if_incomplete |
Paypercut attempts payment before creating the subscription. If payment succeeds, the subscription is created as active. If payment fails, the API returns 402 and no subscription is created. |
You only want to create the subscription after the first payment succeeds. |
default_incomplete |
Default. Without a trial or explicit anchor, Paypercut attempts payment at creation. Success moves the subscription to active; failure can leave it past_due for recovery. Exhausted retries can end as incomplete_expired. |
You are prepared to handle past_due and recovery after creation. |
allow_incomplete |
Similar creation-time collection behavior, but exhausted retries can cancel the subscription. | You are prepared to create the subscription even if initial payment requires recovery. |
send_invoice |
Mentioned in schema as coming soon, but currently rejected by backend validation. | Do not use in public integrations yet. |
error_if_incomplete cannot be combined with trials or explicit billing anchors because those defer the first charge.
What the API returns
The create endpoint returns a Subscription object when a subscription is created.
Important fields include:
idcustomerstatuscurrent_period_startcurrent_period_endnext_billing_datecancel_at_period_endcancel_atcanceled_attrial_starttrial_endcollection_methoddefault_payment_methodpayment_behavioritemsmetadata
For error_if_incomplete, a payment failure returns 402 and no subscription is created.
For default_incomplete or allow_incomplete, a payment failure can still return a created subscription that needs recovery. Your backend must inspect the returned status and process invoice events.
What to store
Store:
- your internal user, account, workspace, or tenant ID;
customer_id;subscription_id;price_idor internal plan mapping;default_payment_methodwhen useful for support;- relevant
invoice_idvalues from invoice events; - webhook event or delivery IDs for idempotency.
What to listen for
At minimum:
| Event | Use |
|---|---|
invoice.paid |
Grant or extend access for a paid billing period. |
invoice.payment_failed |
Start recovery and fetch latest subscription and invoice state. |
Payment and PaymentIntent events can help with reconciliation, but they should not be the primary subscription access contract.
Access decision
Do not grant access only because the create API returned 201. A created subscription can still need payment recovery depending on payment_behavior.
For paid access:
- Store the
subscription_id. - Inspect the returned
status. - Process
invoice.paididempotently. - Map the invoice back to the subscription and your internal account.
- Grant or extend access for the paid billing period.
For trials, trialing means the subscription is in a trial period. Whether trialing customers receive product access is your product policy.
Common mistakes
- Creating a direct subscription before the customer has a reusable payment method.
- Treating
201 Createdas paid access proof. - Ignoring
invoice.payment_failed. - Using
PaymentIntentevents as the subscription lifecycle source. - Assuming
send_invoiceis supported. - Assuming public
subscription.*webhook events exist. - Documenting setup-fee examples from backend behavior while the public OpenAPI shape remains inconsistent.
Troubleshooting
| Symptom | Check |
|---|---|
| Create request returns validation error | Confirm customer, currency, items, and recurring item fields match the public schema. |
API returns 402 |
If using error_if_incomplete, the first payment failed and no subscription was created. Let the customer update the payment method before retrying. |
Subscription is past_due after creation |
Handle recovery through invoice events and latest subscription state. |
| Renewal access is not extended | Confirm invoice.paid is subscribed and mapped to the stored subscription_id. |
| Failed payment is not recovered | Confirm invoice.payment_failed starts your recovery flow and that the subscription has a valid default payment method before resume. |

