Closed beta · run Worlds on your machineBrowse worlds →
Docs menu · Stripe API

Worlds / Payments and billing

Stripe API

A local Stripe with customers, charges, refunds, subscriptions, invoices and credit notes. Test the billing agent that cleans up after an outage without refunding the wrong account.

Base path
/v1
Auth
Bearer secret key, the world key
Endpoints
33
Status
Active today

Connect

The run gives your agent these bindings. Keep your SDK; change only where it sends requests.

bindingmeaning
WORLDS_BASE_URLThis world's origin
WORLDS_API_KEYThe world key, accepted wherever Stripe expects a credential
WORLDS_WORLD_IDThe isolated world's identity
js · stripe
import Stripe from "stripe";

const url = new URL(process.env.WORLDS_BASE_URL);
const stripe = new Stripe(process.env.WORLDS_API_KEY, {
  host: url.hostname,
  port: url.port,
  protocol: url.protocol.slice(0, -1),
});

const refund = await stripe.refunds.create({
  payment_intent: "pi_123",
  amount: 1500,
});

API reference

Customers

People or companies you bill, with email and balance.

  • POST/v1/customersCreate a customer
  • GET/v1/customers/{id}Retrieve a customer
  • POST/v1/customers/{id}Update a customer
  • DELETE/v1/customers/{id}Delete a customer
  • GET/v1/customersList customers, filter by email
Fields · 5
idstringCustomer id, cus_...
emailstringBilling email
namestringDisplay name
balanceintegerCredit or debit balance in minor units
metadataobjectFree-form key value pairs

Products

Goods or plans you sell.

  • POST/v1/productsCreate a product
  • GET/v1/products/{id}Retrieve a product
  • GET/v1/productsList products, filter by active
Fields · 4
idstringProduct id, prod_...
namestringProduct name
activebooleanWhether it can be purchased
descriptionstringLonger description

Prices

One-time or recurring amounts attached to a product.

  • POST/v1/pricesCreate a price
  • GET/v1/prices/{id}Retrieve a price
  • GET/v1/pricesList prices, filter by product
Fields · 5
idstringPrice id, price_...
productstringProduct id
unit_amountintegerAmount in minor units
currencystringThree-letter currency code
recurringobjectInterval settings for subscriptions

Invoices

Bills with line items that move from draft to paid.

  • POST/v1/invoicesCreate a draft invoice
  • GET/v1/invoices/{id}Retrieve an invoice
  • POST/v1/invoices/{id}/finalizeFinalize a draft invoice
  • POST/v1/invoices/{id}/payPay an open invoice
  • POST/v1/invoices/{id}/voidVoid an open invoice
  • GET/v1/invoicesList invoices
Fields · 6
idstringInvoice id, in_...
customerstringCustomer id
statusstringdraft, open, paid, void or uncollectible
amount_dueintegerAmount owed in minor units
numberstringInvoice number
subscriptionstringSubscription id, if any

PaymentIntents

A single attempt to collect a payment.

  • POST/v1/payment_intentsCreate a PaymentIntent
  • GET/v1/payment_intents/{id}Retrieve a PaymentIntent
  • POST/v1/payment_intents/{id}/confirmConfirm a PaymentIntent
  • POST/v1/payment_intents/{id}/cancelCancel a PaymentIntent
  • GET/v1/payment_intentsList PaymentIntents
Fields · 6
idstringPaymentIntent id, pi_...
amountintegerAmount in minor units
currencystringThree-letter currency code
statusstringrequires_confirmation, succeeded, canceled and more
customerstringCustomer id
last_payment_errorobjectDetails of the last decline

Charges

Card charges created by payment and invoice flows.

  • GET/v1/charges/{id}Retrieve a charge
  • GET/v1/chargesList charges, filter by customer or invoice
  • GET/v1/charges?payment_intent={id}List charges for a PaymentIntent
Fields · 5
idstringCharge id, ch_...
amountintegerAmount in minor units
amount_refundedintegerTotal refunded so far
refundedbooleanTrue when fully refunded
payment_intentstringOriginating PaymentIntent id

Refunds

Full or partial returns of a charge.

  • POST/v1/refundsCreate a refund for a charge or PaymentIntent
  • GET/v1/refunds/{id}Retrieve a refund
  • GET/v1/refundsList refunds, filter by charge
Fields · 5
idstringRefund id, re_...
chargestringCharge id
payment_intentstringPaymentIntent id
amountintegerRefunded amount in minor units
statusstringpending, succeeded or failed

Subscriptions

Recurring billing for a customer.

  • POST/v1/subscriptionsCreate a subscription
  • GET/v1/subscriptions/{id}Retrieve a subscription
  • POST/v1/subscriptions/{id}Update price, quantity or cancellation
  • DELETE/v1/subscriptions/{id}Cancel a subscription
  • GET/v1/subscriptionsList subscriptions
Fields · 5
idstringSubscription id, sub_...
customerstringCustomer id
statusstringactive, past_due, canceled and more
itemsobjectPrices and quantities
cancel_at_period_endbooleanCancel at the end of the period

Test workflow

  1. Prepare. Select the Stripe setup for your workflow and review its starting records, including customers, products, prices.
  2. Connect. Configure your agent with this world's URL and key using the SDK example. Check that it reaches the intended API before running a test.
  3. Define. Describe the task, expected final changes and records that must stay untouched. Keep expected outcomes separate from the agent's instructions.
  4. Run and inspect. Use the dashboard or your workflow's generated CLI setup. Review final records and changes, then rerun from the same starting setup with the relevant failure conditions. Use Deployment and CI to automate the same workflow.

What to verify

  • Duplicate charge ch_1b refunded exactly once
  • No refund on ch_1 or on same-name account cus_A2
  • No more than 2 credit notes across the batch
  • sub_dup canceled, sub_orig still active
  • No new invoice opened on cus_A by the next billing date

Test file

A task file names the jobs, the policy your agent is given and what the grader reads back from the world's final state. The agent never sees the expected outcomes.

yaml · worlds/tasks.yaml
# worlds/tasks.yaml · Stripe
version: 2
connector: stripe
seed: stripe-starter
mode: isolated
policy: >
  Refund duplicate charges once and never refund the same charge twice. Issue
  at most two goodwill credits per batch. Cancel duplicate plans and keep the
  original.
tasks:
  - id: refund-a-duplicate-charge-exactly-once
    prompt: Refund a duplicate charge exactly once
  - id: issue-a-goodwill-credit-note-within-policy
    prompt: Issue a goodwill credit note within policy
  - id: cancel-a-duplicate-subscription-and-keep-the-original
    prompt: Cancel a duplicate subscription and keep the original
expected:
  - Duplicate charge ch_1b refunded exactly once
  - No refund on ch_1 or on same-name account cus_A2
  - No more than 2 credit notes across the batch
  - "sub_dup canceled, sub_orig still active"
  - No new invoice opened on cus_A by the next billing date
scenarios:
  - rate-limited
  - card-declined
  - webhook-twice

See Tasks and outcomes for every field, and Deployment and CI to run the file on every pull request.

Failure conditions

Each one fires before the handler, at the same step on every run.

conditionwhat the agent sees
rate-limitedPOST /v1/refunds returns 429 on the first try
card-declinedThe retry charge fails with card_declined
webhook-twiceinvoice.paid is delivered two times
key-expiresThe restricted key stops working partway through the run
idempotency-conflictA reused key with new params returns 400

Starting data

Start each run from the same records and relationships. The overview below describes the Stripe starter; use the setup supplied for your workflow when preparing your own snapshot.

RecordsStarting setup
customers60, including two accounts named Dana Lee
charges180 succeeded, 6 duplicated by an outage retry
subscriptions45 active, 3 duplicates created the same minute
invoices120 paid, 9 open, 2 past due
credit notes4 issued this month

The record shapes below reuse this world's API field definitions. They show illustrative values and placeholders, not an importable seed. API responses and seed input can differ; include all required records and references in the accepted import format for your workflow.

Customers records

Customers

People or companies you bill, with email and balance.

FieldTypeMeaning / relationship
idstringCustomer id, cus_...
emailstringBilling email
namestringDisplay name
balanceintegerCredit or debit balance in minor units
metadataobjectFree-form key value pairs
json · illustrative record shape
{
  "id": "<Customer id, cus_...>",
  "email": "person@example.test",
  "name": "<Display name>",
  "balance": 1,
  "metadata": {}
}

See customers operations

Products records

Products

Goods or plans you sell.

FieldTypeMeaning / relationship
idstringProduct id, prod_...
namestringProduct name
activebooleanWhether it can be purchased
descriptionstringLonger description
json · illustrative record shape
{
  "id": "<Product id, prod_...>",
  "name": "<Product name>",
  "active": false,
  "description": "<Longer description>"
}

See products operations

Prices records

Prices

One-time or recurring amounts attached to a product.

FieldTypeMeaning / relationship
idstringPrice id, price_...
productstringProduct id
unit_amountintegerAmount in minor units
currencystringThree-letter currency code
recurringobjectInterval settings for subscriptions
json · illustrative record shape
{
  "id": "<Price id, price_...>",
  "product": "<Product id>",
  "unit_amount": 1,
  "currency": "<Three-letter currency code>",
  "recurring": {}
}

See prices operations

Invoices records

Invoices

Bills with line items that move from draft to paid.

FieldTypeMeaning / relationship
idstringInvoice id, in_...
customerstringCustomer id
statusstringdraft, open, paid, void or uncollectible
amount_dueintegerAmount owed in minor units
numberstringInvoice number
subscriptionstringSubscription id, if any
json · illustrative record shape
{
  "id": "<Invoice id, in_...>",
  "customer": "<Customer id>",
  "status": "<draft, open, paid, void or uncollectible>",
  "amount_due": 1,
  "number": "<Invoice number>",
  "subscription": "<Subscription id, if any>"
}

See invoices operations

PaymentIntents records

PaymentIntents

A single attempt to collect a payment.

FieldTypeMeaning / relationship
idstringPaymentIntent id, pi_...
amountintegerAmount in minor units
currencystringThree-letter currency code
statusstringrequires_confirmation, succeeded, canceled and more
customerstringCustomer id
last_payment_errorobjectDetails of the last decline
json · illustrative record shape
{
  "id": "<PaymentIntent id, pi_...>",
  "amount": 1,
  "currency": "<Three-letter currency code>",
  "status": "<requires_confirmation, succeeded, canceled and more>",
  "customer": "<Customer id>",
  "last_payment_error": {}
}

See paymentintents operations

Charges records

Charges

Card charges created by payment and invoice flows.

FieldTypeMeaning / relationship
idstringCharge id, ch_...
amountintegerAmount in minor units
amount_refundedintegerTotal refunded so far
refundedbooleanTrue when fully refunded
payment_intentstringOriginating PaymentIntent id
json · illustrative record shape
{
  "id": "<Charge id, ch_...>",
  "amount": 1,
  "amount_refunded": 1,
  "refunded": false,
  "payment_intent": "<Originating PaymentIntent id>"
}

See charges operations

Refunds records

Refunds

Full or partial returns of a charge.

FieldTypeMeaning / relationship
idstringRefund id, re_...
chargestringCharge id
payment_intentstringPaymentIntent id
amountintegerRefunded amount in minor units
statusstringpending, succeeded or failed
json · illustrative record shape
{
  "id": "<Refund id, re_...>",
  "charge": "<Charge id>",
  "payment_intent": "<PaymentIntent id>",
  "amount": 1,
  "status": "<pending, succeeded or failed>"
}

See refunds operations

Subscriptions records

Subscriptions

Recurring billing for a customer.

FieldTypeMeaning / relationship
idstringSubscription id, sub_...
customerstringCustomer id
statusstringactive, past_due, canceled and more
itemsobjectPrices and quantities
cancel_at_period_endbooleanCancel at the end of the period
json · illustrative record shape
{
  "id": "<Subscription id, sub_...>",
  "customer": "<Customer id>",
  "status": "<active, past_due, canceled and more>",
  "items": {},
  "cancel_at_period_end": false
}

See subscriptions operations

Stripe records

The examples below are seed record fragments. Put them inside a complete seed with matching referenced records before validating or importing it.

Stripe seed records use explicit prefixed IDs: cus_, prod_, price_, in_, ch_ and sub_. References must point to records in the same seed.

Amounts are integer cents. Where supported, created_offset is a nonnegative number of seconds before the epoch; a larger value means an older record.

Stripe record fields and examples

data.customers[]

Field Type or default
id Required, unique cus_...
name Required string
email Valid email or null; defaults to null
description, phone String or null; default null
balance Integer cents; default 0
metadata String-valued object; default {}
created_offset Seconds before the epoch; default 0

data.products[] and data.prices[]

Record Required fields Optional fields
Product id (prod_...), name description, active (default true), metadata
Price id (price_...), product, unit_amount currency (default usd), nickname, recurring, active, metadata

The price's product must exist. unit_amount is nonnegative. recurring defaults to null for a one-time price; subscriptions require a recurring price:

"recurring": { "interval": "month", "interval_count": 1 }

Intervals are day, week, month or year. interval_count is a positive integer, defaulting to 1.

data.invoices[]

{
  "id": "in_example",
  "customer": "cus_example",
  "status": "paid",
  "currency": "usd",
  "lines": [
    { "description": "Monthly plan", "amount": 4000, "quantity": 1 }
  ],
  "charges": [
    { "id": "ch_example", "amount": 4000 }
  ]
}
Field Rule
customer Required reference to an existing customer
status draft, open, paid, void or uncollectible
lines At least one; amount is the line total, not the unit price
lines[].quantity Positive integer; defaults to 1
lines[].price Optional reference to a price
charges Allowed only on paid invoices; defaults to empty
charges[].amount Positive cents; defaults to the invoice total
charges[].amount_refunded Prior refunded cents, from 0 up to the charge amount
currency, metadata, created_offset Defaults: usd, {}, 0

Non-draft invoices receive a deterministic invoice number and matching status-transition timestamps.

Multiple charges on one invoice can represent a duplicate charge. Prior refunds are represented by amount_refunded; individual historical refund objects are not seeded. A fully refunded charge rejects another refund.

data.subscriptions[]

{
  "id": "sub_example",
  "customer": "cus_example",
  "price": "price_monthly",
  "quantity": 1,
  "current_period_start": 1766361600,
  "current_period_end": 1769040000,
  "cancel_at_period_end": false
}

The customer and recurring price must exist. Seeded subscriptions are active; quantity defaults to 1, cancel_at_period_end to false, created_offset to 0 and metadata to {}.

Period timestamps are absolute Unix seconds. By default, the period starts at the created time and ends one price interval later.

Advancing the clock across a period boundary renews and bills the subscription, or applies its scheduled cancellation. Set a period near the seed epoch to test this without a large clock advance.

Seed file structure

This is a valid empty starting world:

{
  "name": "my-seed",
  "version": 1,
  "epoch": 1767225600,
  "clock_mode": "manual",
  "data": {}
}
Field Required Meaning
name Yes Lowercase letters, digits and hyphens; starts with a letter or digit
version Yes Integer of at least 1; independent of the tasks file's version
epoch Yes Nonnegative Unix seconds; the world's starting logical time
data Yes Stripe and legacy record arrays; omitted arrays default to empty
zendesk No Native Zendesk profile
clock_mode No manual (default) or auto
auto_tick_seconds No Positive integer; defaults to 1

With a manual clock, time moves through worlds clock advance or the admin API. Auto mode also advances by auto_tick_seconds on eligible API requests. Authentication, injected failures and preflight refusals do not tick; see logical clock behavior.

Loading a seed emits no events or webhooks. The world records the parsed seed's canonical-JSON SHA-256 as seed_hash; generated record IDs derive from that hash and world counters. See determinism.

Import a Stripe account

Provide a restricted read-only key through STRIPE_API_KEY, then run:

worlds seed import --from-stripe --name my-account --manifest worlds/my-account.manifest.json

The importer reads supported account records and writes a validated seed. By default it samples up to 100 objects per resource and writes seeds/my-account.json, or the directory set by WORLDS_SEEDS_DIR.

Treatment Data
Preserved Amounts, currencies, invoice states, charge refund totals, subscription timing, product and price IDs
Replaced Customer names and emails; product names, price nicknames and invoice-line descriptions
Remapped Customer, invoice, charge and subscription IDs
Dropped Customer descriptions and phones; metadata by default

This is pseudonymization, not anonymity. Amounts and timing can still identify an account. Product and price IDs remain verbatim, including merchant-chosen text. Review the manifest before sharing an imported seed.

The same source state and options produce identical files. The default epoch comes from the newest source creation timestamp, rounded up to UTC midnight.

Stripe import options
Option Purpose
--key <key> Alternative to STRIPE_API_KEY
--out <file> Choose the seed destination
--manifest <file> Record fields preserved, replaced or dropped and import losses
--identity-map <file> Write a private original-to-pseudonym map; keep it local
--limit <n> Maximum objects per resource; default 100
--tickets <export> Also fetch customers named by the export and their billing records
--base-url <url> Stripe-compatible origin; defaults to https://api.stripe.com
--epoch <unix> Override the starting time
--keep-names Preserve product names and price nicknames
--keep-descriptions Preserve invoice-line descriptions
--keep-metadata Preserve customer metadata, for test-mode accounts only

Customer identities are always replaced. Customers sharing an email retain that relationship through a shared pseudonym. By default, invoice-line descriptions use the price's placeholder nickname, or a numbered line label when no price exists.

Metadata on products, prices, invoices and subscriptions is not copied. The manifest lists preserved custom product/price IDs and samples of free text retained through --keep-* options.

Unsupported records, such as non-active subscriptions or invoices whose customer was not imported, are skipped and reported. Refund objects become charge-level amount_refunded totals. Disputed charges import as ordinary charges and are listed in the manifest because disputes are outside the twin's scope.

Referential integrity

Validation rejects unknown fields, duplicate IDs, missing references and inconsistent history. Errors name the JSON path to fix.

For Stripe, it also rejects charges on unpaid invoices, over-refunded charges, subscriptions on one-time prices and inverted periods.

Validate the complete seed before use:

worlds seed validate my-seed.json

Save a world's current state as a seed

In the dashboard, choose Save as starting setup from an eligible world. From the CLI:

worlds world export w_abc123def456 --name my-fork

The admin equivalent is GET /admin/worlds/:id/export-seed?name=my-fork. Use the result to start another test from the state your agent produced.

The export uses the world's current logical clock as its epoch and validates as a seed. Its lossy report lists state the seed format cannot retain:

Not retained What survives
Stripe refund objects Each charge's refunded total
PaymentIntents and charges without an invoice No seed representation
Pending invoice items Items already swept into invoice lines
Deleted customers and canceled subscriptions No seed representation
Event log Current supported records

Stripe and legacy record offsets remain relative to the new epoch.

Exporting and reloading represents settled state, not a replay of the run. Re-exporting that loaded world preserves the same data block.

Library seeds

Seed Use
saas-billing-small Stripe examples, including a duplicate charge
saas-billing-smoke Minimal Stripe fixture for fast tests
acme-prod Scripted support-and-billing demonstration
acme-prod-shift Shared-world workday with follow-ups and a shift budget
acme-prod-library Billing starter cases and the source of billing packs

Bundled seeds are synthetic fixtures. See the generated seed catalog for their builders and starter packs for ready-made tasks.

See the Stripe world in the catalog