Skip to main content
Stripe API emulation with customers, payment methods, setup intents, customer sessions, payment intents, charges, refunds, products, prices, invoices, invoice items, subscriptions, subscription items, test clocks, checkout sessions, events, and webhook endpoints. Includes a hosted checkout page, idempotent requests, and signed webhook delivery. The tests drive it with stripe-node.
stripe-node accepts only host, port, and protocol, not a base path, so point it at Stripe’s own port.

At a glance

Start

Stripe is in the default set and listens on http://localhost:4009, or on http://localhost:4000 when it is the only service. Without a seed config there is one customer ([email protected], “Test Customer”). stripe-node does not read an environment variable for the base URL, so pass the emulator’s host and port when constructing the client:
Plain HTTP clients only need the base URL changed:

Requests

These rules apply to every /v1 endpoint, as they do on Stripe:
  • Idempotency. A POST with an Idempotency-Key header (1 to 255 characters) saves its result for 24 hours. A retry with the same key and the same parameters gets the saved response back with Idempotent-Replayed: true and Original-Request: <first request id>, without running the endpoint again, so no second object or event is created. The same key with other parameters, or on another endpoint, gets a 400 idempotency_error (Stripe.errors.StripeIdempotencyError in stripe-node). A retry while the first request is still running gets a 409 idempotency_key_in_use. A request that fails validation (a 4xx other than 402) is not saved, so the key can be retried with fixed parameters. stripe-node adds a key to every POST on its own.
  • Request IDs. Every response carries a Request-Id: req_... header (lastResponse.requestId in stripe-node).
  • Expand. expand[] works on query strings and request bodies, including create and update calls. Lists take data.<field>, e.g. expand[]=data.customer. A field that cannot be expanded returns 400 This property cannot be expanded (<field>).
  • Metadata. Updates merge metadata into what is stored. Set a key to "" to remove it, or send metadata="" to clear all keys. Metadata values always stay strings.
  • Form values. Booleans sent as true / false are decoded, so active=false stores a boolean.

Customers

  • POST /v1/customers — Create customer
  • GET /v1/customers/:id — Retrieve customer
  • POST /v1/customers/:id — Update customer
  • DELETE /v1/customers/:id — Delete customer
  • GET /v1/customers — List customers
test_clock on create puts the customer on a test clock (at most three per clock). As on Stripe, GET /v1/customers lists customers on a clock only when test_clock is given. balance (create and update) is the customer’s credit (negative) or debt (positive). Finalizing an invoice applies it, as described under Invoices. Deleting a customer cancels their subscriptions first, sending customer.subscription.deleted for each.

Customer balance

  • POST /v1/customers/:id/balance_transactions — Adjust customer balance: by amount (negative is credit) in currency, with description and metadata
  • GET /v1/customers/:id/balance_transactions/:txn — Retrieve balance transaction
  • POST /v1/customers/:id/balance_transactions/:txn — Update balance transaction: description, metadata
  • GET /v1/customers/:id/balance_transactions — List balance transactions: the ledger, newest first (filter by invoice)
Every change to a customer’s balance is recorded with the balance after it (ending_balance): initial for balance on create, adjustment for manual entries and balance on update, applied_to_invoice when finalizing uses it, unapplied_from_invoice when voiding gives it back, and credit_note for credit notes.

Payment methods

  • POST /v1/payment_methods — Create payment method: a card from a test token (type=card, card[token]=tok_visa; billing_details, metadata optional)
  • GET /v1/payment_methods/:id — Retrieve payment method
  • POST /v1/payment_methods/:id — Update payment method: billing_details, metadata, or card[exp_month] / card[exp_year] (attached payment methods only)
  • POST /v1/payment_methods/:id/attach — Attach payment method: to a customer
  • POST /v1/payment_methods/:id/detach — Detach payment method: from its customer
  • GET /v1/payment_methods — List payment methods: customer, type; without customer, unattached ones
  • GET /v1/customers/:id/payment_methods — List a customer’s payment methods
  • GET /v1/customers/:id/payment_methods/:pm — Retrieve a customer’s payment method
Payment methods carry card details (brand, last4, funding, country, exp_month, exp_year) for the test card they stand for. As on Stripe, passing a magic test ID such as pm_card_visa to a payment intent, setup intent, or attach makes a new payment method with its own pm_... ID, and that ID is what responses show. Tokens such as tok_visa and tok_chargeDeclined work with card[token]. Raw card numbers get Stripe’s “sending credit card numbers directly … is generally unsafe” error. An ID that is neither stored nor a test ID returns 400 No such PaymentMethod. Attaching checks the card, so a card that declines (pm_card_visa_chargeDeclined) cannot be saved; pm_card_chargeCustomerFail saves and then declines every charge. A saved card can only pay for its own customer: a payment or setup without that customer, or with another one, is refused. Customers have invoice_settings.default_payment_method, set on create or update to a card attached to them; detaching that card or deleting the customer clears it. Customer create also takes payment_method to attach a card. Events: payment_method.attached, payment_method.detached, payment_method.updated.

Setup intents

  • POST /v1/setup_intents — Create setup intent: customer, payment_method, usage default off_session, confirm, return_url, description, metadata
  • GET /v1/setup_intents/:id — Retrieve setup intent
  • POST /v1/setup_intents/:id — Update setup intent: customer, payment_method, description, metadata
  • POST /v1/setup_intents/:id/confirm — Confirm setup intent: payment_method, return_url optional
  • POST /v1/setup_intents/:id/cancel — Cancel setup intent: cancellation_reason optional
  • GET /v1/setup_intents — List setup intents: filter by customer, payment_method
A SetupIntent saves a card without charging it. Confirming attaches the card to the intent’s customer and sends setup_intent.succeeded; with usage: off_session the card is set up for off-session payments. A card that declines fails the setup with a 402 (setup_intent.setup_failed, last_setup_error), and a card that needs authentication goes to requires_action with the same hosted 3D Secure page as payments. After pm_card_authenticationRequiredOnSetup is set up, off-session payments with it succeed without authentication; pm_card_authenticationRequired keeps asking. Payment intents can save the card as they pay with setup_future_usage (on_session or off_session), which attaches it to the intent’s customer once the payment succeeds.

Customer sessions

  • POST /v1/customer_sessions — Create customer session

Payment intents

  • POST /v1/payment_intents — Create payment intent
  • GET /v1/payment_intents/:id — Retrieve payment intent
  • POST /v1/payment_intents/:id — Update payment intent
  • POST /v1/payment_intents/:id/confirm — Confirm payment intent
  • POST /v1/payment_intents/:id/capture — Capture payment intent: an authorized one (amount_to_capture optional)
  • POST /v1/payment_intents/:id/cancel — Cancel payment intent: cancellation_reason optional
  • GET /v1/payment_intents — List payment intents
Confirming captures the payment by default (capture_method: "automatic_async"), creating a captured charge and setting amount_received and latest_charge. Create with confirm: true to create and confirm in one call. With capture_method: "manual", confirming authorizes instead: the intent moves to requires_capture with amount_capturable set, the charge has captured: false, and payment_intent.amount_capturable_updated is sent. Capture all or part of it with /capture; a partial capture releases the rest, which shows in the charge’s amount_refunded. Canceling an authorized intent releases the hold, so its charge reads as fully refunded and charge.refunded is sent. Stripe payment intents and Checkout sessions follow API version 2026-09-30.endive (stripe-node 23): send allowed_payment_method_types, which responses echo, and read the usable list from payment_method_types, which defaults to ["card"]. The older payment_method_types request param is still accepted for clients pinned to earlier versions.

Test cards

Confirming needs a payment method; without one, confirm returns 400 payment_intent_unexpected_state, as on Stripe. Stripe’s test payment method IDs and the matching tok_ tokens behave as they do on Stripe. Other pm_card_ IDs, such as country cards, are cards that succeed. The dispute cards succeed and then open a dispute. A decline returns a 402 card error (Stripe.errors.StripeCardError in stripe-node) with code, decline_code, charge, payment_method, and payment_intent. It leaves a failed charge with failure_code, failure_message, outcome, and payment_method_details, sets the intent’s last_payment_error, returns it to requires_payment_method, and sends charge.failed and payment_intent.payment_failed. Confirm again with another payment_method to retry. With an Idempotency-Key, the 402 is replayed like any other result.

3D Secure

A card that needs authentication moves the intent to requires_action, sends payment_intent.requires_action, and sets next_action to the hosted page at /_stripe/3ds/:id: redirect_to_url when the intent has a return_url, otherwise use_stripe_sdk. The page has Complete and Fail buttons. Completing applies the card’s result (success, requires_capture for manual capture, or a decline); failing returns the intent to requires_payment_method with payment_intent_authentication_failure. With a return_url, the page redirects there with payment_intent, payment_intent_client_secret, and redirect_status (succeeded or failed). Tests can skip the page and POST the outcome; Accept: application/json returns the payment intent:

Charges

  • GET /v1/charges/:id — Retrieve charge
  • GET /v1/charges — List charges

Refunds

  • POST /v1/refunds — Create refund: of a captured payment, by payment_intent or charge (amount, reason, metadata optional)
  • GET /v1/refunds/:id — Retrieve refund
  • POST /v1/refunds/:id — Update refund metadata
  • POST /v1/refunds/:id/cancel — Cancel refund: refunds succeed at once here, so this returns Stripe’s error for a refund that is not pending
  • GET /v1/refunds — List refunds: filter by charge, payment_intent
A refund without amount refunds what is left. Partial refunds add up in the charge’s amount_refunded until refunded is true. Refunding more than is left returns amount_too_large, a fully refunded charge returns charge_already_refunded, and an uncaptured charge returns charge_not_refundable (cancel its payment intent instead). Each refund sends refund.created and charge.refunded. A charge with a chargeback cannot be refunded (charge_disputed); an open inquiry can.

Balance

  • GET /v1/balance — Retrieve balance: available and pending funds per currency
  • GET /v1/balance_transactions/:id — Retrieve balance transaction: expand[]=source
  • GET /v1/balance_transactions — List balance transactions: filter by type, source, currency, created
Captured charges, refunds, and disputes write balance transactions, which charges and refunds link as balance_transaction; expand it, or latest_charge.balance_transaction on a payment intent, to read fees. A charge adds its amount less Stripe’s US card fee (2.9% + 30 cents, in fee_details) and is pending until available_on, two days after midnight UTC. A refund takes its amount out at once, and Stripe keeps the fee. A manual capture records the captured amount when it is captured. The balance is the sum of net amounts, split by whether available_on has passed.

Disputes

  • GET /v1/disputes/:id — Retrieve dispute
  • POST /v1/disputes/:id — Update dispute: stage or submit evidence, update metadata
  • POST /v1/disputes/:id/close — Close dispute: accept it
  • GET /v1/disputes — List disputes: filter by charge, payment_intent
Stripe’s dispute test cards open a dispute once the charge is captured and set the charge’s disputed. A chargeback starts as needs_response and withdraws the amount plus a $15 fee at once (an adjustment balance transaction, charge.dispute.funds_withdrawn). An inquiry starts as warning_needs_response and moves no funds. Evidence is submitted when sent, unless submit=false stages it. Submitting moves the dispute to under_review (warning_under_review for an inquiry), after which evidence cannot change. As on Stripe, evidence[uncategorized_text] decides the outcome: winning_evidence wins (won, the amount and fee come back with charge.dispute.funds_reinstated; an inquiry becomes warning_closed), and losing_evidence loses (lost; a lost inquiry withdraws the funds then). Other evidence stays under review. Closing accepts the dispute: a chargeback is lost, an inquiry warning_closed. Events: charge.dispute.created, .updated, .closed, .funds_withdrawn, .funds_reinstated.

Connect

  • POST /v1/accounts — Create connected account: type (standard, the default, express, or custom) or controller (stripe_dashboard[type] of full, express, or none), country, email, business_type, business_profile, capabilities, tos_acceptance, external_account, metadata
  • GET /v1/accounts/:id — Retrieve connected account
  • POST /v1/accounts/:id — Update connected account
  • DELETE /v1/accounts/:id — Delete connected account
  • GET /v1/accounts — List connected accounts
  • GET /v1/account — Retrieve current account: the account in Stripe-Account, or the platform (acct_baseline) without it
  • POST /v1/account_links — Create account link: hosted onboarding (account_onboarding, or account_update for Custom accounts) with refresh_url and return_url
  • POST /v1/accounts/:id/login_links — Create login link: an Express dashboard link, for onboarded Express accounts
  • POST /v1/transfers — Create transfer
  • GET /v1/transfers/:id — Retrieve transfer
  • POST /v1/transfers/:id — Update transfer: description, metadata
  • GET /v1/transfers — List transfers: filter by destination, transfer_group
  • GET /v1/application_fees/:id — Retrieve application fee
  • GET /v1/application_fees — List application fees: filter by charge
A new account needs business_type, external_account, tos_acceptance.date, and tos_acceptance.ip (requirements.currently_due); until then charges_enabled and payouts_enabled are false and its capabilities are inactive. The account link page fills in what is missing when the user clicks Complete onboarding, then redirects to return_url; a used or expired link (after five minutes) redirects to refresh_url. Tests can POST the link URL with Accept: application/json to get the account back. Custom accounts can also be completed through POST /v1/accounts/:id; any external_account value counts as a payout account. Changes send account.updated. A request with Stripe-Account: acct_... acts on that account’s own data: customers, payment methods, payment intents, charges, refunds, disputes, billing, balance, and events are kept per account. An unknown or deleted account returns 403 account_invalid. The hosted Checkout and 3D Secure pages find a connected account’s sessions and intents on their own. Events on a connected account carry account and go to webhook endpoints created with connect=true (or seeded with connect: true), not the platform’s. Charges move money as on Stripe:
  • Direct charge (with Stripe-Account): the charge is the account’s. application_fee_amount is taken out of its balance transaction (fee_details lists stripe_fee and application_fee) and paid to the platform as an application fee (application_fee.created).
  • Destination charge (transfer_data[destination]): the charge is the platform’s. The amount, or transfer_data[amount], is transferred to the account (transfer.created, the charge’s transfer), and application_fee_amount comes back to the platform out of the account’s payment. The destination must be onboarded with transfers (insufficient_capabilities_for_transfer). application_fee_amount without either returns 400, as does combining it with transfer_data[amount].
  • Separate charges and transfers: POST /v1/transfers needs that much available platform balance (balance_insufficient; pay with pm_card_bypassPending to skip the pending period), or a source_transaction charge, whose funds it waits for.
on_behalf_of and transfer_group are kept and echoed.

Products

  • POST /v1/products — Create product
  • GET /v1/products/:id — Retrieve product
  • GET /v1/products — List products

Prices

  • POST /v1/prices — Create price: unit_amount required; recurring, lookup_key, nickname optional
  • GET /v1/prices/:id — Retrieve price
  • GET /v1/prices — List prices: filter by product, active, type, lookup_keys[]
recurring[interval] (day, week, month, year) with recurring[interval_count] makes a recurring price, up to Stripe’s three-year limit. Only licensed (per unit) prices are emulated: usage_type=metered returns 400.

Coupons and promotion codes

  • POST /v1/coupons — Create coupon: percent_off or amount_off with currency, duration (once, repeating with duration_in_months, or forever), max_redemptions, redeem_by, applies_to[products][], id (otherwise a random code), name
  • GET /v1/coupons/:id — Retrieve coupon: expand[]=applies_to shows its products
  • POST /v1/coupons/:id — Update coupon: name or metadata (other fields cannot change)
  • DELETE /v1/coupons/:id — Delete coupon: discounts already made from it keep applying
  • GET /v1/coupons — List coupons
  • POST /v1/promotion_codes — Create promotion code: for a coupon (promotion[coupon], or coupon for older clients) with code (otherwise random), customer, expires_at, max_redemptions, active, and restrictions (first_time_transaction, minimum_amount with minimum_amount_currency)
  • GET /v1/promotion_codes/:id — Retrieve promotion code
  • POST /v1/promotion_codes/:id — Update promotion code: active, metadata
  • GET /v1/promotion_codes — List promotion codes: filter by active, code, coupon, customer
A discount is made when a coupon or promotion code is applied, with discounts[0][coupon] or discounts[0][promotion_code] on a subscription, invoice, Checkout session, or preview. Customers take the older coupon and promotion_code params on create and update, and GET or DELETE /v1/customers/:id/discount reads or removes the customer’s discount. DELETE /v1/subscriptions/:id/discount removes a subscription’s. Applying a coupon checks it as Stripe does: an unknown or deleted coupon returns resource_missing, one past redeem_by or max_redemptions returns coupon_expired, and an amount_off coupon in another currency is refused. A promotion code must be active, unexpired, under its max_redemptions, for its customer if it has one, and meet its restrictions: first_time_transaction refuses customers with an earlier payment or paid invoice, and minimum_amount refuses smaller orders. Each redemption raises times_redeemed on the coupon and the code. How discounts bill:
  • A subscription’s discounts apply to each of its invoices. Without its own discounts, a subscription uses the customer’s discount. Invoices take the subscription’s or customer’s discounts unless discounts is given (discounts="" opts out).
  • Percentages come off the discountable lines; amount_off comes off up to their total. Several discounts apply in order. Each invoice line reports its share in discount_amounts, and the invoice reports total_discount_amounts, discounts, and total. Proration lines and invoice items with discountable=false are not discounted, and applies_to limits a coupon to its products.
  • once applies to the first invoice with something to discount. repeating applies to invoices made within duration_in_months of the discount’s start (the end), so on a test clock a three-month coupon covers three monthly renewals. forever never ends. A discount that has ended is removed when the next invoice finalizes, with customer.discount.deleted.
  • Previews (create_preview) apply the same discounts and accept discounts to try one out.
Events: coupon.created, .updated, .deleted; promotion_code.created, .updated; customer.discount.created, .deleted. Seed coupons and codes under coupons and promotion_codes in seed config, and tax rates under tax_rates.

Tax rates

  • POST /v1/tax_rates — Create tax rate: display_name, percentage, inclusive (all required), country, state, jurisdiction, tax_type, description, active
  • GET /v1/tax_rates/:id — Retrieve tax rate
  • POST /v1/tax_rates/:id — Update tax rate: anything but percentage and inclusive; active: false archives it
  • GET /v1/tax_rates — List tax rates: filter by active, inclusive
Tax rates apply by ID: tax_rates on invoice items, subscription items (items[i][tax_rates], add_invoice_items[i][tax_rates]), and Checkout line items, and default_tax_rates on invoices and subscriptions for lines without their own. A subscription’s invoices take its default_tax_rates. Unknown rates return resource_missing, and archived rates cannot be applied anew (they stay where they are used). Taxes follow Stripe’s tax rate rules: each line is taxed on its amount after discounts. An exclusive rate adds its percentage of that amount, rounded per line. An inclusive rate is already in the amount: the taxable amount is the amount divided by one plus the inclusive rates, and the tax is the rest. Lines report taxes (amount, tax_behavior, tax_rate_details, taxable_amount), and invoices report total_taxes per rate, total_excluding_tax, and subtotal_excluding_tax; total and amount_due include exclusive tax. Previews and Checkout (total_details.amount_tax) use the same rules. Events: tax_rate.created, tax_rate.updated.

Invoices

  • POST /v1/invoices — Create invoice: a draft for a customer. pending_invoice_items_behavior=include takes the customer’s pending items (the default, as on Stripe, is exclude). With subscription, it takes only that subscription’s pending items; without it, only items not tied to a subscription
  • POST /v1/invoices/create_preview — Preview invoice: without making it (see Invoice previews)
  • GET /v1/invoices/:id — Retrieve invoice
  • POST /v1/invoices/:id — Update invoice: a draft (description, metadata, auto_advance, collection_method, days_until_due, due_date, default_payment_method). Open invoices take only metadata and auto_advance; others only metadata
  • DELETE /v1/invoices/:id — Delete draft invoice: its items go back to pending
  • POST /v1/invoices/:id/finalize — Finalize invoice: a draft
  • POST /v1/invoices/:id/pay — Pay invoice: an open, uncollectible, or draft one (payment_method, off_session, paid_out_of_band)
  • POST /v1/invoices/:id/send — Send invoice: a send_invoice invoice (finalizes a draft first)
  • POST /v1/invoices/:id/void — Void invoice: an open or uncollectible one
  • POST /v1/invoices/:id/mark_uncollectible — Mark invoice uncollectible: an open one
  • GET /v1/invoices/:id/lines — List invoice lines: paginated
  • GET /v1/invoices — List invoices: filter by customer, subscription, status, collection_method, created
Statuses follow Stripe’s transition table. Finalizing numbers the invoice (<customer invoice_prefix>-0001), applies the customer balance (credit lowers amount_due; a negative total becomes credit; starting_balance and ending_balance show the change), and creates the payment intent that collects it. An invoice with nothing to pay is paid at once. send_invoice invoices get due_date from days_until_due (30 days when unset). Paying charges payment_method, else the invoice’s default_payment_method, else the customer’s invoice_settings.default_payment_method; with none, it returns 400. Payment follows the test cards: a decline returns the 402 card error, keeps the invoice open, raises attempt_count, and sends invoice.payment_failed. off_session defaults to true, so a card that needs authentication declines with authentication_required; with off_session=false it returns 402 invoice_payment_intent_requires_action and sends invoice.payment_action_required, and the invoice is paid once the 3D Secure page completes. paid_out_of_band=true marks it paid without a charge (amount_paid_off_stripe). Voiding cancels the payment intent and gives back the customer balance the invoice used. To collect on the client, expand confirmation_secret and confirm its client_secret with Stripe.js. However the invoice’s payment intent succeeds, the invoice becomes paid and sends invoice.paid, invoice.payment_succeeded, and invoice_payment.paid. expand[]=payments lists the invoice payment. The payment intent cannot be canceled directly while the invoice is open; void the invoice instead.

Invoice items

  • POST /v1/invoiceitems — Create invoice item: for a customer, priced by amount (negative for a credit), unit_amount_decimal with quantity, pricing[price], or price_data. invoice puts it on a draft invoice; otherwise it waits as pending
  • GET /v1/invoiceitems/:id — Retrieve invoice item
  • POST /v1/invoiceitems/:id — Update invoice item: amount, quantity, pricing, period, description, or metadata
  • DELETE /v1/invoiceitems/:id — Delete invoice item
  • GET /v1/invoiceitems — List invoice items: filter by customer, invoice, pending
subscription on create ties a pending item to one of the customer’s subscriptions, so it is billed on that subscription’s next invoice. tax_rates taxes the item (see Tax rates); invoices take default_tax_rates for items without their own. Items on a finalized invoice cannot change or be deleted (invoice_not_editable). price_data makes an inactive price, as on Stripe. The currency must match the invoice’s.

Automatic collection

With auto_advance: true, invoices move on their own as billing time passes:
  • A draft finalizes an hour after it was created (automatically_finalizes_at), and a charge_automatically invoice is charged off-session right away.
  • After a manual finalize, the first charge is an hour later (next_payment_attempt).
  • A failed charge (including no payment method) sends invoice.payment_failed and retries 3, 5, and 7 days after the previous attempt, four attempts in all. After the last, next_payment_attempt is null and the invoice stays open. The retry days can be changed in seed config (billing.retry_days).
  • A send_invoice invoice sends invoice.overdue once its due_date passes.
  • auto_advance: false on an open invoice stops collection; setting it back schedules a charge an hour later.
Billing time is the wall clock, or the customer’s test clock. Nothing runs on a timer: wall-clock customers catch up at the start of the next /v1 request (so actions take that request’s time), and a test clock runs each action at the time it falls due while it advances. Events from automatic actions have request.id: null.

Invoice previews

POST /v1/invoices/create_preview returns the invoice Stripe would make, without creating anything. Previews use the same billing rules as the real invoices, so a preview and the invoice that follows it match. The ID starts with upcoming_in_, lines start with il_tmp_, and billing_reason is upcoming.
  • subscription alone previews the next renewal, with the pending items it would take.
  • subscription with subscription_details (items, proration_behavior, proration_date, billing_cycle_anchor, trial_end, cancel_at, cancel_at_period_end, cancel_now) previews a change. With always_invoice, a new cycle, or cancel_now, it is the invoice made at once; otherwise it is the next renewal with the change’s prorations on it.
  • customer with subscription_details[items] previews the first invoice of a new subscription.
  • customer alone previews an invoice of the customer’s pending items.
With nothing to invoice it returns 404 invoice_upcoming_none, as Stripe does.

Credit notes

  • POST /v1/credit_notes — Create credit note: credit a finalized invoice by lines or amount, with refund_amount, credit_amount, out_of_band_amount, refunds, reason, memo, metadata
  • GET /v1/credit_notes/preview — Preview credit note: the same, without issuing it
  • GET /v1/credit_notes/preview/lines — Preview credit note lines
  • GET /v1/credit_notes/:id — Retrieve credit note
  • GET /v1/credit_notes/:id/lines — List credit note lines
  • POST /v1/credit_notes/:id — Update credit note: memo or metadata
  • POST /v1/credit_notes/:id/void — Void credit note
  • GET /v1/credit_notes — List credit notes: filter by invoice, customer
A line of type invoice_line_item credits part of an invoice line, by quantity or amount (all that is left when neither is given), with its share of the line’s discounts and the line’s tax rates. A custom_line_item credits unit_amount × quantity with its own tax_rates. A credit note cannot take more than is left of a line, or of the invoice total. The total first comes off what is left to pay on an open invoice (pre_payment_amount): amount_due drops, the payment intent follows, and an invoice with nothing left is paid. The rest (post_payment_amount) must equal refund_amount (a refund of the invoice’s charge) plus credit_amount (customer balance credit, which the next invoice uses) plus out_of_band_amount plus linked refunds; otherwise it returns 400. The invoice adds them up in pre_payment_credit_notes_amount and post_payment_credit_notes_amount. Numbers follow the invoice’s (<invoice number>-CN-01). Voiding gives back the balance credit and raises amount_due again. A credit note with a refund, or one that paid its invoice, cannot be voided. Events: credit_note.created, .updated, .voided.

Subscriptions

  • POST /v1/subscriptions — Create subscription: for a customer with items (each a recurring price or price_data, with quantity)
  • GET /v1/subscriptions/:id — Retrieve subscription
  • POST /v1/subscriptions/:id — Update subscription: change items, quantities, trial, anchor, cancellation, collection, payment method, or metadata
  • DELETE /v1/subscriptions/:id — Cancel subscription: now (invoice_now, prorate, cancellation_details)
  • POST /v1/subscriptions/:id/pause — Pause subscription: type=subscription
  • POST /v1/subscriptions/:id/resume — Resume subscription: a paused one (billing_cycle_anchor, proration_behavior, payment_behavior)
  • GET /v1/subscriptions — List subscriptions: filter by customer, price, status, collection_method, test_clock
Subscriptions run on automatic collection and test clocks: each renewal, trial end, retry, and expiry happens at its billing time. Creating. Items must share a currency and billing interval, with no price twice (1 to 20 items). The first invoice (billing_reason: subscription_create) is finalized and collected at once, according to payment_behavior:
  • allow_incomplete (the default) charges the default card. On success the subscription is active; on a decline it stays incomplete with the invoice open.
  • error_if_incomplete returns the card’s 402 error and creates nothing when the charge would fail.
  • default_incomplete leaves the subscription incomplete so the client pays it: expand latest_invoice.confirmation_secret and confirm it with Stripe.js.
An incomplete subscription becomes active when its first invoice is paid. If it is not paid within 23 hours, it becomes incomplete_expired, its invoice is voided, and customer.subscription.deleted is sent. The first invoice is never retried. payment_settings[save_default_payment_method]=on_subscription saves the card that paid it as the subscription’s default. The subscription’s card is its default_payment_method, else the customer’s invoice_settings.default_payment_method. collection_method=send_invoice (with days_until_due) sends invoices instead of charging them. add_invoice_items adds one-time prices to the first invoice. Pending invoice items for the customer go on the first invoice too. Periods. Billing periods follow the price’s interval from billing_cycle_anchor. Month periods keep the anchor day and clamp to the month’s end (an anchor on 31 January renews on 28 February, then 31 March). A future billing_cycle_anchor bills a prorated first period up to it (proration_behavior=none makes it free). Each item reports current_period_start and current_period_end. Three days before each renewal, invoice.upcoming is sent with a preview of the invoice (not for send_invoice). At renewal, a draft invoice (billing_reason: subscription_cycle) is made with the subscription’s pending items, then finalized and charged an hour later. Trials. trial_end (a timestamp) or trial_period_days starts a trialing subscription with a $0 invoice. customer.subscription.trial_will_end is sent three days before the trial ends (at once for shorter trials). When it ends, the first paid period starts. trial_settings[end_behavior][missing_payment_method] decides what happens without a card: create_invoice (the default) bills anyway, pause pauses the subscription, and cancel cancels it. An update with trial_end=now ends the trial at once; a later trial_end extends it. Failed payments. A failed renewal makes the subscription past_due and retries on the invoice schedule. After the last retry the subscription is canceled, by default. billing.after_final_retry in seed config can make it unpaid (later invoices stay drafts) or leave it past_due. Paying or marking uncollectible the latest invoice makes the subscription active again. Changes and proration. Updates take items (id to change or with deleted=true to remove; price alone to add), with proration_behavior:
  • create_prorations (the default) creates pending invoice items: a credit for the unused time on what was last billed and a charge for the remaining time on the new items, prorated to the second (proration_date sets the time). They are billed on the next invoice.
  • always_invoice invoices the prorations at once and collects them.
  • none changes the items without prorations.
A change of billing interval, or billing_cycle_anchor=now, starts a new period at once, with credit for the unused time. During a trial, item changes cost nothing. Line descriptions follow Stripe’s (Unused time on 2 × Pro after 11 Jan 2026, Remaining time on Pro after 11 Jan 2026, 1 × Pro (at $20.00 / month)). billing_mode is flexible. incomplete and paused subscriptions take no billing changes; canceled and expired ones take only metadata and cancellation_details. Canceling. cancel_at_period_end=true or cancel_at (a timestamp, or min_period_end / max_period_end) schedules the end. A cancel_at within the period credits the time after it. DELETE cancels at once: prorate=true credits the unused time, invoice_now=true bills what is pending on a final invoice, and without either, pending prorations for the subscription are dropped. Canceling stops automatic collection on its open invoices and sends customer.subscription.deleted. Canceled subscriptions are left out of the list unless status=canceled, ended, or all is given. Pausing. pause_collection[behavior] keeps the subscription running but changes its renewal invoices: keep_as_draft leaves them as drafts, mark_uncollectible and void finalize and then mark or void them. pause_collection[resumes_at] ends the pause at that time. /pause stops the subscription itself (paused, no invoices), and /resume restarts it, either keeping the cycle (billing_cycle_anchor=unchanged, billing the rest of the current period) or starting a new one now. With payment_behavior=resume_on_payment_success it resumes only once that invoice is paid. Both send customer.subscription.paused and customer.subscription.resumed.
Discounts are covered in Coupons and promotion codes, and tax rates in Tax rates.

Subscription items

  • POST /v1/subscription_items — Add subscription item: to a subscription (price or price_data, quantity, proration_behavior)
  • GET /v1/subscription_items/:id — Retrieve subscription item
  • POST /v1/subscription_items/:id — Update subscription item: change its price or quantity
  • DELETE /v1/subscription_items/:id — Delete subscription item: a subscription keeps at least one item
  • GET /v1/subscription_items?subscription= — List subscription items: a subscription’s items
These prorate like the same change through POST /v1/subscriptions/:id.

Test clocks

  • POST /v1/test_helpers/test_clocks — Create test clock: at frozen_time (name optional; customer moves an existing customer onto it, and then the time cannot be in the past)
  • GET /v1/test_helpers/test_clocks/:id — Retrieve test clock
  • POST /v1/test_helpers/test_clocks/:id/advance — Advance test clock: move it forward to frozen_time
  • DELETE /v1/test_helpers/test_clocks/:id — Delete test clock: with its customers and their invoices
  • GET /v1/test_helpers/test_clocks — List test clocks
Customers created with test_clock bill in the clock’s time: invoices and invoice items record it in created and date and carry test_clock. Advancing runs everything that falls due on the way, in order, each at its own time, so one advance across three weeks runs every payment retry. A clock only moves forward, at most two years per advance. When its customers have subscriptions, an advance can cover at most two billing periods of the shortest one, as on Stripe. The advance finishes before the response returns. The response still says status: "advancing", as Stripe’s does, and the clock reads ready from then on, so code that polls for ready works unchanged. Each advance sends test_helpers.test_clock.advancing and test_helpers.test_clock.ready. As on Stripe, unfiltered invoice and invoice item lists leave out clock customers’ objects; filter by customer to see them.

Checkout

  • POST /v1/checkout/sessions — Create Checkout session
  • GET /v1/checkout/sessions/:id — Retrieve Checkout session
  • POST /v1/checkout/sessions/:id/expire — Expire Checkout session
  • GET /v1/checkout/sessions — List Checkout sessions: filter by customer, status, payment_status
  • GET /checkout/:id — Hosted checkout page: HTML
  • POST /checkout/:id/complete — Complete payment flow
mode=subscription takes recurring prices (one-time prices go on the first invoice) and subscription_data (trial_period_days, metadata). Completing the page creates the customer if there is none, saves the test card for off-session payments, and starts the subscription, so the session reports subscription and invoice. A session with a trial reports payment_status: no_payment_required. mode=payment refuses recurring prices. discounts (one coupon or promotion code) applies a discount, or allow_promotion_codes=true shows a promotion code field on the hosted page (not both). The session reports amount_subtotal, amount_total, discounts, and total_details.amount_discount. A code typed on the page is checked like the API’s; an invalid one shows “This code is invalid.” In mode=subscription the discount goes on the subscription.

Events

  • GET /v1/events — List events: newest first. Filter by type (exact, or a wildcard such as customer.*), types[] (up to 20 exact types, not with type), created (exact or gt, gte, lt, lte), and delivery_success
  • GET /v1/events/:id — Retrieve event
Every event the emulator sends is kept for 30 days, with data.object as it was when the event happened. Use this to reconcile after a missed webhook. pending_webhooks counts the endpoints the event has not reached yet, so it drops when a retry succeeds; delivery_success=false lists events with a failed or pending delivery.

Webhooks

Events are delivered to webhook URLs from seed config (stripe.webhooks: url, events, optional secret, and connect: true for connected account events) and to endpoints created with /v1/webhook_endpoints.

Webhook endpoints

  • POST /v1/webhook_endpoints — Create webhook endpoint: url and enabled_events required; description, metadata, api_version optional
  • GET /v1/webhook_endpoints/:id — Retrieve webhook endpoint
  • POST /v1/webhook_endpoints/:id — Update webhook endpoint: url, enabled_events, description, metadata, or disabled
  • DELETE /v1/webhook_endpoints/:id — Delete webhook endpoint
  • GET /v1/webhook_endpoints — List webhook endpoints
Create returns a whsec_... secret, the only time it is returned, and deliveries to that endpoint are signed with it. disabled: true pauses deliveries and sets status: "disabled". Webhooks from seed config are listed here too. enabled_events takes * or exact event types; http and https URLs, including localhost, are accepted.

Deliveries and event types

  • Webhooks configured with a secret include Stripe-Signature: t=<timestamp>,v1=<signature>, signed over <timestamp>.<raw body>
  • Each event is a full Stripe event object: id (evt_...), object: "event", api_version (2026-09-30.endive), created, livemode: false, pending_webhooks, type, data.object, and request with the id and idempotency_key of the API call that caused it. *.updated events include data.previous_attributes with the old values of the fields that changed.
  • customer.created, customer.updated, customer.deleted
  • product.created, price.created
  • payment_method.attached, payment_method.detached, payment_method.updated
  • setup_intent.created, setup_intent.requires_action, setup_intent.succeeded, setup_intent.setup_failed, setup_intent.canceled
  • payment_intent.created, payment_intent.requires_action, payment_intent.amount_capturable_updated, payment_intent.succeeded, payment_intent.payment_failed, payment_intent.canceled
  • charge.succeeded (the full charge object) when a payment intent is confirmed, charge.failed, charge.captured, charge.refunded
  • refund.created, refund.updated
  • charge.dispute.created, charge.dispute.updated, charge.dispute.closed, charge.dispute.funds_withdrawn, charge.dispute.funds_reinstated
  • account.updated (to Connect endpoints, with account), application_fee.created, transfer.created, transfer.updated
  • invoiceitem.created, invoiceitem.deleted
  • invoice.created, invoice.updated, invoice.deleted, invoice.finalized, invoice.sent, invoice.paid, invoice.payment_succeeded, invoice.payment_failed, invoice.payment_action_required, invoice.voided, invoice.marked_uncollectible, invoice_payment.paid
  • invoice.overdue when a send_invoice invoice passes its due date
  • invoice.upcoming three days before a subscription renews
  • customer.subscription.created, customer.subscription.updated, customer.subscription.deleted (when a subscription is canceled or expires), customer.subscription.trial_will_end, customer.subscription.paused, customer.subscription.resumed
  • test_helpers.test_clock.created, test_helpers.test_clock.advancing, test_helpers.test_clock.ready, test_helpers.test_clock.deleted
  • customer.updated also fires when finalizing or voiding an invoice changes the customer balance
  • checkout.session.completed when a checkout session is completed
  • checkout.session.expired when a checkout session expires

Seed config

  • recurring: optional; omit for a one-time price
  • coupons: optional; percent_off or amount_off (with currency)
  • duration: once (the default), repeating, or forever
  • promotion_codes: optional; customer-facing codes for seeded coupons
  • first_time_transaction: optional restrictions
  • tax_rates: optional; id, display_name, percentage, inclusive
  • accounts: optional; connected accounts, onboarded unless onboarded: false
  • type: standard (the default), express, or custom
  • billing: optional; Stripe’s automatic collection settings
  • retry_days: days between payment retries (the default)
  • after_final_retry: subscription after the last retry: cancel, unpaid, or past_due

Limits

  • Connect: transfer reversals, refunds of application fees (refund_application_fee, reverse_transfer), payouts, persons and external account objects, and automatic billing (auto_advance, wall clock renewals) for connected accounts outside their own requests.
  • Coupons: discounts on single invoice items or invoice lines (400 parameter_unknown) and currency_options.
  • Tax: Stripe Tax (automatic_tax[enabled]=true returns 400) and flat amount rates.
  • Credit notes: shipping_cost and line tax_amounts.
  • Invoices: hosted invoice pages and PDFs (hosted_invoice_url and invoice_pdf are null).
  • Subscriptions: Stripe Tax, metered and tiered prices, subscription schedules, billing thresholds, pending_if_incomplete, trial_from_plan, and backdate_start_date. They return 400 parameter_unknown rather than being ignored.

Conformance

See CONFORMANCE.md for every provider.