Skip to main content
An invoice is a numbered bill for one or more of your variants, addressed to one customer. A variant is a price and billing setup you sell, usually for a product. Its ID starts with plan_, and invoice items take it as plan_id. Use an invoice when you agree a price with a customer before they pay, such as a quote, a service, or a business order. You build a draft until it’s right, then issue it to lock the totals and open it for payment. Sending it does that and emails it in one call.
Invoices on this page need Api-Version-Date: 2026-10-24 or later on every request. Requests with an earlier version reach the legacy invoice API, which works differently. See Before you start.

How an invoice works

A native invoice is a payment. Its ID starts with pay_ and stays the same after the customer pays, so you can also read it with Retrieve Payment. Each invoice also gets the next number in your account’s own sequence, such as #00000042, or #ACME-00000042 when your account has an invoice prefix.
  1. You create a draft with items, a customer, and billing details. A draft charges nothing and emails no one.
  2. You issue it, or send it. Issuing calculates final tax and opens the invoice for payment. Sending issues it and emails it, either now or at a time you schedule.
  3. The customer pays. Whop charges the customer’s saved payment method when you issue the invoice, or the customer pays through Whop’s hosted checkout.
  4. You fulfill the order from the payment.succeeded webhook.
Invoices bill buy-now variants, the ones with release_method: buy_now rather than a waitlist. A one-time variant is billed once, and a recurring variant starts a subscription when the invoice is paid. See Invoice a subscription. Installment plans are refused.

Before you start

Send the API version

/invoices serves two different APIs, and the Api-Version-Date your request resolves to decides which one answers. Issue, send, cancel a scheduled send, and generate a PDF exist only in the native API, so they reach native invoices whatever version you send. The paths both APIs have, such as GET /invoices and POST /invoices/{id}/void, follow the version. Send the header on every request rather than relying on a key’s pin, so changing keys never moves you between the two. API versions explains pins and the changelog. The native API doesn’t list or retrieve legacy inv_ invoices, and the legacy API doesn’t return native ones.

Call the API

The examples on this page call the API through a small helper that sends the version header on every request.
Send an Idempotency-Key on every POST so a retry never creates a second invoice or repeats an action. Generate Invoice PDF is the exception: each call makes a new file, so it doesn’t replay responses.

Create a draft

Create Invoice saves a draft with POST /invoices.
The response is 201 Created with the draft. Creating a draft charges nothing, sends no email, and sends no webhook. This response shows a subset of the fields:
account_id is the account issuing the invoice, prefixed biz_. An account API key can leave it out to use its own account. Create Invoice lists every request and response field. A draft uses its number when you create it, so a draft you discard leaves a gap in your sequence. To discard a draft, void it.

Add items

items takes 1 to 50 items. Each item takes either plan_id for an existing variant or plan to find or create one inline, plus an optional quantity. An inline plan uses the same find-or-create input as Create Payment, for example { "plan": { "product_id": "prod_xxxxxxxxxxxxx", "plan_type": "one_time", "initial_price": 250, "currency": "usd" } }. It needs plan:create, and an inline product also needs the product permission, access_pass:create or access_pass:update. Create and update refuse items that break these rules:
  • Each item is a buy-now variant of your account. A variant appears once, so use quantity for more units.
  • quantity is between 1 and 999,999, and above 1 only for a variant that allows multiple quantities. Recurring variants don’t, so a recurring item’s quantity is 1.
  • A recurring variant must belong to a product.
  • Every item uses the same currency.
  • In an invoice with several items, every item that has a product must be a regular product (the standard product type), not an app. Recurring items share one billing period and free trial length, and all items need compatible payment, tax, and access settings. Variants with custom fields or a password can’t be combined. Items without a product, such as a custom fee, can sit beside the rest.
Each line item in the response has a label, which is the product’s title, or the variant’s title for a variant with no product. The email, PDF, and checkout show it. The draft keeps the price each variant had when you saved its items, so a later price change on the variant doesn’t move the invoice. A one-time item is billed at its price, and a recurring item at its renewal price.

Set the customer and billing details

Send member_id or email_address, and billing_address or mailing_address_id, never both of a pair. A billing address needs a customer. A customer is optional on a draft and required to issue it. Whop calculates tax when you issue the invoice, using the billing address to place the customer. When tax applies and Whop can’t tell where the customer is, the issue is refused, so add a billing address whenever you collect tax.

Edit a draft

Update Invoice changes a draft with PATCH /invoices/{id}. Send only the fields you’re changing.
  • items replaces the whole list and reads each variant’s current price. Leave it out to keep the saved items and prices.
  • Changing member_id drops the saved billing address and payment method unless you send new ones. It keeps recipient_emails, so send a new list too, or the previous customer still gets the invoice email.
  • email_address and customer_name aren’t accepted. Send a different member_id to bill someone else.
  • A draft with a scheduled send accepts only a change to recipient_emails until you cancel the send.
  • Once the invoice is issued, the only change you can make is recipient_emails, sent on its own, and only while the invoice is open or uncollectible. Any other edit is refused with a 400.

Choose how the customer pays

There are two ways to get paid for an issued invoice. Pick one before you issue it, because the saved payment method goes on the draft. An issued invoice that bills a product gets a checkout_link even when it has a saved payment method. If the automatic charge fails, the customer can still pay through checkout.

Charge a saved payment method

Put a saved payment method on the draft with payment_method_id. It has to be one of the customer’s saved methods. Save payment methods shows how a customer saves one, and List payment methods finds the ones a member has. When you issue or send the invoice and its total is above zero, Whop:
  1. Runs the same checks Create Payment runs on a saved payment method.
  2. Reserves stock for the invoiced quantities and prepares the payment’s fees.
  3. Returns the invoice as open and charges the method in the background, off session.
The response doesn’t tell you whether the charge succeeded. Fulfill from the payment.succeeded webhook for the invoice’s pay_ ID. A paid invoice completes the membership, and your metadata is on the charge and the membership. Payment rules on invoice payments covers how your payment rules screen the charge.

If the automatic charge is declined

A declined charge leaves the invoice open and returns the reserved stock. Retrieve the invoice as a payment and read failure_message for why the latest attempt failed. When the charge is declined or needs the customer to act, Whop emails the invoice’s pay link to its recipients, so the customer can pay through checkout instead. It’s the one email Whop sends for an invoice you issue without sending it. It needs a checkout_link and at least one entry in recipient_emails, and it doesn’t count toward the send limit.
An off-session charge can’t show the customer a 3D Secure challenge. If their bank asks for one, the automatic charge doesn’t complete, and the invoice stays unpaid until the customer authenticates or pays another way.

Issue the invoice

Issue Invoice turns a draft into an open invoice. Whop checks the invoice again, calculates final tax, and sets status to open and totals_status to final. When the invoice has a saved payment method and a total above zero, Whop then charges it, as described in Charge a saved payment method.
  • Tax becomes final. tax_amount is null on a draft and set once you issue it. total and amount_due include it.
  • Whop creates the customer’s membership when the invoice bills a product. It stays drafted, with no access, until the invoice is paid, and it’s what lets the invoice carry a checkout_link.
  • payment.created fires once for the invoice’s pay_ ID.
  • Issuing again is safe. Issuing an invoice that’s already open runs the same checks again and, if they pass, returns it unchanged: its totals stay, and nothing is charged again.
  • Issuing doesn’t email the invoice, apart from the pay link after a declined charge. To email the invoice, send it instead, or deliver the checkout link yourself.
A refused issue changes nothing and charges nothing. Fix the cause and issue the invoice again. Issue errors lists the reasons. An issued, open invoice that bills a product carries a checkout_link, a signed value in the form pay_xxxxxxxxxxxxx.<signature>. It opens the invoice on Whop’s hosted checkout at its fixed price.
1

Read the link from the issued invoice

checkout_link is null unless the invoice is open, so drafts and paid, void, or uncollectible invoices have none. It’s also null for an invoice with no product, one billed to a business buyer, one whose membership was canceled, and when your credential lacks payment:charge. A business buyer is a customer that’s another Whop account rather than a person.
2

Build the hosted checkout URL

Split the link at its first .: the invoice’s pay_ ID comes before it, and the token after it.
3

Send it to the customer

Send the invoice to have Whop email the link, or put the URL in your own email or invoice page. Only the invoice’s customer can pay through it. Anyone else is refused and asked to sign in as the invoice’s customer. The link stops working once the invoice is paid, voided, or marked uncollectible.
4

Fulfill from the webhook

Listen for payment.succeeded for the invoice’s pay_ ID, the same as for the automatic charge. Webhooks shows a handler.
The checkout shows the invoice’s number and due date beside its items, and the due date turns red once it has passed. The customer pays the items and total on the invoice and can’t add a promo code.

Send, schedule, and remind

Send Invoice emails an invoice with POST /invoices/{id}/send. Sending a draft issues it first, the same as Issue Invoice. Sending an open invoice emails it again.
The email shows your account’s name and logo, the invoice number, the total, the due date, each item, and a button that opens the invoice’s checkout. Whop only emails an invoice the customer can pay from the email. An open invoice needs a checkout_link to be sent, and a draft needs to bill a product to a customer who isn’t a business buyer. A draft that doesn’t can still be sent when it has a saved payment method, because it’s charged instead.

Who gets the email

Each send emails every address once, as a separate email, so recipients never see each other’s addresses. The addresses are:
  • The email on the customer’s Whop account, which the invoice PDF prints under Bill to.
  • Each entry in recipient_emails.
Whop saves recipient_emails in lowercase and de-duplicates the list, so an address in both gets one email. An empty recipient_emails means Whop emails nobody, the customer included. Use it to keep Whop from emailing an invoice:
  • Send Invoice refuses the invoice, unless it charges a saved payment method instead.
  • No pay link goes out after a declined charge.
  • No reminders go out. Clearing recipient_emails on an open invoice stops its remaining reminders.

When Whop charges instead of emailing

A draft with a payment_method_id and a total above zero is charged instead of emailed, the same as when you issue it. If the automatic charge is declined covers what happens when the charge fails. Such a draft can be sent with an empty recipient_emails, because nobody needs the email.

Send limit

Each invoice can be emailed three times in 24 hours, including the send that issued it. A fourth send in that window is refused with a 429 whose error.code is invoice_send_limit_reached. Reminders, the pay link after a declined charge, and a send that charges instead of emailing don’t count toward the limit.

Schedule a send

To send a draft later instead, pass send_at. The draft stays a draft and shows the time in scheduled_send_at. At that time, Whop sends it as if you had called Send Invoice then.
  • Only a draft can be scheduled, and send_at has to be in the future. Whop checks the recipients and the pay link when you schedule, the same as for a send now.
  • Send again with a new send_at to move the time.
  • Editing is locked until the send happens or you cancel it. An update that changes only recipient_emails still works, but it can’t remove the last recipient from a draft that won’t be charged.
  • Cancel Scheduled Send with POST /invoices/{id}/unschedule. scheduled_send_at returns to null, and the draft can be edited again. Canceling an invoice with no scheduled send returns it unchanged.
  • Voiding a scheduled draft stops its scheduled send.
Whop checks for due sends every minute, so a scheduled send goes out within about a minute of send_at. If it’s refused, for example because a variant was deleted since you scheduled it, the invoice stays a draft, scheduled_send_at returns to null, and the API doesn’t return the reason. Any other failure is retried about every 10 minutes, up to 12 attempts in all. Until an attempt succeeds or Whop stops retrying, scheduled_send_at shows the time of the next attempt rather than null. Read the invoice after send_at to confirm it’s open.

Reminders

When Send Invoice emails an invoice that has a due_at, Whop also schedules reminders 3, 7, 14, 30, and 60 days after due_at, skipping any whose date has already passed. Each reminder goes to the same addresses as the invoice and says how many days the invoice is past due. Reminders go out only while the invoice is open, so they stop once it’s paid, voided, or marked uncollectible. A reminder that comes due while a payment is in progress waits until the payment finishes. An invoice you issue without sending, or one that’s charged instead of emailed, gets no reminders.

Download a PDF

Generate Invoice PDF builds the invoice as a PDF and returns a short-lived link to it. It needs payment:basic:read and works at any status.
The response has url and expires_at. The link works for 15 minutes, and anyone holding it can download the file. Generate a new one when it expires. The PDF leads with your business: your account’s name heads the page, and the footer says Whop Inc issues the invoice in your name and on your behalf. It shows the invoice number, its date and due date, and the Bill to details. Each item shows its quantity, unit price, and amount, followed by the subtotal, service fee, tax, and amount due. An invoice with a recurring item also states the renewal amount and how often it renews. A draft’s PDF shows its estimated totals, with tax at zero until you issue it. Some content depends on your credential’s permissions:

Invoice a subscription

Put a recurring variant on an invoice to start a subscription when the customer pays it. Whop then renews it like any other subscription.
  1. The invoice bills the first period. A recurring item must belong to a product, has a quantity of 1, and is billed at the variant’s renewal price. The variant’s free trial and initial fee don’t apply, so add a setup fee as a one-time item beside it.
  2. Paying the invoice starts the subscription, whether Whop charges the saved payment method on issue or the customer pays through the checkout_link. The first period starts at the payment. The checkout opened from the link states the renewal amount, including the invoice’s buyer fee, and how often it renews.
  3. Each renewal charges the payment method that paid the invoice, once per billing period. It bills only the recurring items, with the invoice’s buyer fee, at each variant’s renewal price when you issued the invoice. That can differ from the price saved on the draft if the variant’s price changed after you saved the items. One-time items are billed once.
Each renewal is its own payment, with its own pay_ ID and payment.* webhooks. Renewals aren’t numbered invoices and don’t appear in List Invoices. To find the subscription, retrieve the invoice as a payment and read its membership_id, which needs member:basic:read.

Statuses and lifecycle

status takes its values from payments, so an invoice can also show a payment state such as pending while a payment is in progress. totals_status tells you whether the money fields are final: estimated while the invoice is a draft, final after you issue it. overdue is true for an open or uncollectible invoice whose due_at has passed.

Void or mark uncollectible

Void Invoice cancels a draft, open, or uncollectible invoice. Voiding an invoice that’s already void returns it unchanged, and a paid invoice can’t be voided. Mark an invoice uncollectible to record that you don’t expect to collect it. Only an open invoice can be marked, and marking an uncollectible invoice again returns it unchanged. An uncollectible invoice has no checkout_link and gets no reminders. This example marks an open invoice uncollectible, then voids it:

List and retrieve invoices

List Invoices returns the invoices your credential can read, newest first. An API key sees its own account, and a user token sees every account the user can read payments for. Narrow the list with these query parameters: Sort with order (created_at or due_at) and direction (asc or desc). Sorting by due_at puts invoices without a due date last. The response is { data, page_info }. While page_info.has_next_page is true, pass page_info.end_cursor as after to read the next page.
Retrieve Invoice reads one invoice by its pay_ ID. List Payments leaves drafts out. To export invoices as a CSV file, call Create Export with resource: "receipt_invoices". It needs payment:basic:read and payment:basic:export. The customer columns appear only when your credential also has member:basic:read or member:email:read.

Webhooks

Native invoices send payment webhooks for their pay_ ID. The invoice.* events belong to the legacy invoice API, and native invoices don’t send them. Other payment.* events, such as payment.failed, can also arrive for the invoice’s pay_ ID as its payment attempts change. Subscribe to them with the webhooks guide. To fulfill an order, save the invoice’s id with the order when you create the draft. The data of a payment.succeeded event is the payment, so data.id is the invoice’s pay_ ID. After you verify the event, look up the order by that ID. In this handler, orders stands for your own order store:
Checking fulfilled keeps a redelivered event from fulfilling the order twice. Subscription renewals have their own pay_ IDs, so they don’t match an invoice.

Payment rules on invoice payments

Your payment rules run on invoice payments after Whop’s own fraud screening, as they do on any checkout. What they can do depends on whether the customer is present.
  • The automatic charge on issue is off session. A block rule declines it, and an allow rule exempts it from your other rules. review and enforce_3ds rules are skipped because the charge is off session.
  • A payment through the checkout link is on session. All four actions apply, with the eligibility limits in Manage fraud.
On the automatic charge, card conditions read the saved payment method and billing conditions read the invoice’s billing address. customer_email is the email on the customer’s Whop account. The payment’s payment_rule_matches lists the rule that decided it. A skipped rule isn’t listed.

Invoices in the dashboard

The Invoices page lists, creates, and sends native invoices. Legacy invoices are on its Legacy invoices tab, which Send an Invoice covers.

Permissions

Every write needs payment:charge, the same permission as Create Payment, and every read needs payment:basic:read. The invoice:* permissions belong to the legacy invoice API, and native invoices don’t use them. A create or update that sends an inline plan item also needs plan:create. Some fields need a permission of their own. Without it, the field comes back null, and recipient_emails comes back empty. If your credential loses one of these read permissions after a POST, replaying it with the same Idempotency-Key is refused with a 403 whose error.code is invoice_replay_permission_revoked when the stored response shows those fields. Fetch the invoice with GET instead. Add permissions from the Permissions guide.

Errors

Refusals come back as { error: { type, message, code } }. Every code below is a 400 unless it says otherwise. An unknown plan_id, member_id, mailing_address_id, or payment_method_id is a 404, and a timestamp Whop can’t read is a 400 with invalid_timestamp. These refusals are a 400 with no error.code, so read error.message for the reason:
  • An unknown top-level field.
  • A quantity outside 1 to 999,999.
  • An invalid address in recipient_emails, or more than 50 of them.
  • A broken several-item rule, such as a billing period mismatch.

Create and update errors

Issue errors

Send and schedule errors

Sending a draft issues it, so a send can also be refused for any reason in Issue errors.

Other errors

Voiding a paid invoice is a 400 without a code.

Next steps

Invoices API reference

Every invoice endpoint, parameter, and response field.

Save payment methods

Collect the saved payment method an invoice charges on issue.

Manage fraud

Write the payment rules that screen invoice payments.

Listen to webhooks

Track payment.created and payment.succeeded for your invoices.