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_due state, 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:

  1. A Paypercut Customer ID.
  2. A reusable payment method attached to that customer, or set as the customer's default payment method.
  3. A recurring Price, or inline price_data that matches the public API schema.
  4. A webhook endpoint subscribed to invoice events.
  5. 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

  1. Your backend creates or selects a Customer.
  2. Your backend confirms a reusable payment method exists for that Customer.
  3. Your backend creates the subscription with recurring items.
  4. Paypercut validates the request and payment method.
  5. Paypercut attempts payment according to payment_behavior.
  6. Your backend stores the returned subscription_id.
  7. Your webhook handler uses invoice.paid and invoice.payment_failed to 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:

  • id
  • customer
  • status
  • current_period_start
  • current_period_end
  • next_billing_date
  • cancel_at_period_end
  • cancel_at
  • canceled_at
  • trial_start
  • trial_end
  • collection_method
  • default_payment_method
  • payment_behavior
  • items
  • metadata

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_id or internal plan mapping;
  • default_payment_method when useful for support;
  • relevant invoice_id values 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:

  1. Store the subscription_id.
  2. Inspect the returned status.
  3. Process invoice.paid idempotently.
  4. Map the invoice back to the subscription and your internal account.
  5. 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 Created as paid access proof.
  • Ignoring invoice.payment_failed.
  • Using PaymentIntent events as the subscription lifecycle source.
  • Assuming send_invoice is 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.