Connector scope
Worlds uses the same workflow across connectors: create a world, run an agent, then check which records changed. Each connector has its own supported API and limitations. A catalog listing does not mean an API is implemented.
| Connector | Available behavior | Verification |
|---|---|---|
| Stripe | Billing, customers, payments and subscriptions, within the scope below | Tested against real Stripe; see the dated fidelity results |
| Native Zendesk | The ticket, directory and workflow subset in the Zendesk API reference | Local conformance plus narrow historical source-bound provider observations; broader/current-artifact live qualification remains pending, as recorded in STATUS |
| Stripe + native Zendesk | Combined workflows with explicit ticket-to-billing bindings | Inherits each connector's limits; see test files |
| Legacy ticket desk | A simplified inbox used by older billing examples | Demo-grade; not a full Zendesk implementation |
Start with the dashboard guide to explore a supported world. Check the relevant connector reference before writing tests for a new workflow.
Experimental beta.4 profiles
The public beta.4 release contains the subsets below; STATUS and the publication reconciliation bind registry/artifact evidence and distinguish it from website activation and installed qualification. These profiles are absent from the older beta.3 archive. Local implementation and package inclusion do not establish provider compatibility, complete dashboard authoring, generic task-suite support or a complete provider API. Each profile retains its own remaining qualification obligations.
| Profile | Included boundary |
|---|---|
| Agentforce Runtime | Bounded sessions, messages, actions and feedback, dedicated principal credentials, local SSE behavior and an intake workflow; an intake guide ships in the runtime |
| SAP S/4HANA | Finite Business Partner/address maintenance and selected queries, sales-order/item reads and conditional reference writes; wider lifecycle remains in the SAP plan |
| Dynamics 365 Dataverse | Implemented Sales/Customer Service records, opportunity/case lifecycle, queues, opportunity assignment and lead discovery; notes and wider actions remain separate in the Dataverse plan |
| Confluence | Permission-aware pages/history, comments/labels and trash lifecycle within the finite contract |
| Freshservice | Native requester, incident and note lifecycles behind configured reserved-authority ingress; deployment settings and the finite contract retain SDK/provider obligations |
| JSM | Seed/state and client foundation; no native JSM endpoints. The native contract is a separate pending delivery |
The browser/storefront workflow uses its own commerce profile, worker/MCP execution, state checks and deterministic recovery scenarios. It does not add real payment, fulfillment or arbitrary external-site parity. Customer Desk, Team Inbox and Digital Shop remain guided starter experiences with their own checks and demo-grade boundaries; Digital Shop's unpaid invoice proposals are separate from browser commerce. The release limitations keep these experimental boundaries explicit. Unimplemented catalog entries continue to collect interest. Generic airline, Gmail, Shopify, Google Workspace and Linear runtime work is outside this public artifact; the inventory records the boundary. Generic airline does not claim Delta parity.
Stripe
The sections below describe the Stripe-compatible surface. Unimplemented routes return a connector-shaped error; parameters that would silently change an unsupported financial behavior are refused.
Implemented (/v1/*)
| resource | operations | depth notes |
|---|---|---|
| Customers | create, retrieve, update, delete, list (email filter) |
deleted customers stay retrievable as {deleted: true} stubs (HTTP 200, like real Stripe); deletes hide them from lists and block updates; deleting a customer does not cancel its subscriptions (Known gaps) |
| Customer balance transactions | create, retrieve, list | single-currency customer credits and debits; applicable credit is consumed when an invoice is finalized |
| Products | create, retrieve, list (active) |
minimal fields, enough for prices/invoices |
| Prices | create, retrieve, list (product, active) |
one-time and recurring; recurring backs subscriptions; unit_amount ≥ 0 |
| Invoice items | create, retrieve, list (customer, pending) |
pending items sweep into the next invoice; quantity ≥ 0 |
| Invoices | create, retrieve, delete (drafts — the items it swept are stranded, Known gaps), list (customer, status, subscription), lines, finalize, pay, void, mark_uncollectible |
full draft → open → paid/void/uncollectible lifecycle with status-transition timestamps, deterministic numbers, guards with Stripe error codes |
| PaymentIntents | create (confirm supported), retrieve, list (customer), confirm, cancel |
test tokens pm_card_visa and pm_card_chargeDeclined; declines record the failed charge + last_payment_error |
| Charges | retrieve, list (customer, payment_intent, invoice) |
born only from flows (invoice pay, PI confirm, subscription billing); amount_refunded/refunded maintained |
| Refunds | create (charge or payment_intent), retrieve, list (charge) |
partial refunds accumulate; over-refund rejected with Stripe's message; full re-refund → charge_already_refunded; a seeded amount_refunded lists one refund for that total, its id derived from the seed hash and the charge id |
| Subscriptions | create, retrieve, update (cancellation, single-item price and quantity), cancel (DELETE), list (customer, status, price) |
creation bills the first period; price/quantity changes support create_prorations or none; clock advance bills each elapsed period |
| Events | retrieve, list (type) |
every mutation emits the correct evt_; pending_webhooks reflects the delivery queue |
Plus, everywhere: Stripe's exact error envelope (type/code/message/param, correct statuses), cursor pagination (limit, starting_after, ending_before, has_more), idempotency keys (replay + idempotency_error; a request rejected by parameter validation reserves no key, an executed outcome — errors and 500s included — is replayed), Bearer/Basic auth with Stripe-shaped 401s, form-encoded bracket syntax bodies (the real SDKs' wire format), and the Stripe-Should-Retry header on injected errors where Stripe sends it (lock_timeout), which is what the official SDKs' retry logic reads. Official stripe-node 16 (the demo agent) and 22 (the cold agent, stock defaults: two retries, an idempotency key on every POST, API version 2026-08-26.dahlia) run against the twin in the test suite; the twin ignores Stripe-Version and answers in its own pinned shapes.
Request representation has Worlds-specific safety limits: bracket paths allow at most 64 segments, nested JSON objects/arrays allow at most 64 levels below the parameter root, and arrays allow at most 1,000 entries (indices 0–999). A request may materialize at most 100,000 entries across object keys and array slots, including sparse holes; parsing and merged JSON are both checked. Invalid percent-encoded UTF-8 and excess structure return connector-shaped validation errors before handler execution or idempotency storage. These guards are not a claim about the real provider's limits. Prototype-named keys remain literal data and cannot change another request's parameters.
Deliberately not implemented
Where one of these is reachable by a parameter on an implemented route, the request is refused with worlds_unsupported_parameter (below, "Refused parameters") — never accepted and silently ignored.
Payment plumbing — PaymentMethods as objects (only the two test tokens), SetupIntents, 3DS/requires_action, captures/holds (capture_method: manual), multi-currency conversion, account balance/payouts/transfers, Connect, disputes.
Billing breadth — coupons/discounts/promotion codes, tax (rates, automatic tax), credit notes, quotes, usage-based/metered billing, tiered prices, trials, custom proration dates, subscription schedules, multi-item subscriptions (one item per subscription), collection_method: send_invoice (auto-charge only), invoice auto_advance timers (finalize/pay are explicit or subscription-driven).
Platform surface — Checkout, Payment Links, Billing Portal, Sources/Tokens (legacy), files, search endpoints (use list filters or the admin state API), expand[] (objects embed what the twin considers primary; expansion params are accepted and ignored).
Field-level omissions on implemented objects (returned as null/absent rather than half-implemented): address/shipping on customers, default_payment_method, application_fee_amount, transfer_data, on_behalf_of, statement_descriptor overrides, receipt_url contents, balance_transaction objects, invoice PDFs/hosted_invoice_url, webhook endpoint objects via /v1/webhook_endpoints (the twin's endpoint is configured via the admin API instead — one per world).
Currency and lifecycle limits
livemodeis alwaysfalse. The account's default currency is USD.- Each invoice and subscription uses one currency. Creating an invoice with several pending currencies requires an explicit currency; renewals collect only items in the subscription's currency.
- A customer's balance uses one currency. Credit is applied only to invoices in that currency. The runtime does not model Stripe's separate credit balance for every currency.
- Requests that would create credit in a conflicting currency are refused. This includes negative invoice items, affected prorations and attempts to pin a balance beside incompatible pending credits.
- Charges are created through payment and invoice flows; direct
POST /v1/chargesis not supported. - Rate limits occur only when a scenario injects them.
Recovery from an incompatible renewal
Normal API writes reject the currency combinations described above before a renewal can encounter them. If state supplied outside that API still produces an incompatible renewal, the transaction and clock movement roll back.
A twin request returns a logged api_error 500; explicit clock advance returns 409. In automatic clock mode, requests continue to encounter that failure until the world is reset. In manual mode, adding a positive line in the invoice's currency or scheduling the subscription to cancel can allow the next advance.
Refused parameters
The reference below is for checking a specific Stripe request. Expand it when a run reports an unsupported parameter.
Stripe parameter compatibility
Real Stripe accepts every parameter below and changes how money moves; the twin does not model it. Rather than return a normal success for a request whose real outcome would differ — a manual capture that never captures, a coupon that never discounts, a trial that never defers — the twin refuses the request before the handler runs: 400 invalid_request_error with the twin's own code worlds_unsupported_parameter (not a Stripe code, so a test can tell "the twin cannot grade this" from "Stripe rejected this"), the parameter in param, no idempotency result (a corrected retry under the same key runs; a key already holding a stored result keeps replaying it, and a request under that key that carries a refused parameter is refused rather than answered idempotency_error), and a row in the request log whose unsupported column names the parameter — GET /admin/worlds/:id/requests?unmatched=true lists these beside the un-twinned routes, one row per route and parameter. The request has been authenticated and counted by then: it uses one of the key's requests_used and advances the scenario counters it matched, exactly as an injected or unmatched request does; what does not happen is the handler, the auto-clock tick and the idempotency store. The drift battery pins the gap: pi-capture-manual-refused and price-trial-period-days expect real Stripe to accept what the twin refuses.
How to read the table:
- A listed allowed value passes through. Other values for that parameter are refused.
- An absent, null or empty value is treated as not supplied. Fields that only affect echoed output may still be accepted and ignored.
- This is a list of recognized spellings, not a complete Stripe parameter validator. Unknown parameters, case variants and some unsupported parameter shapes are ignored.
- Some refusals depend on customer state, such as incompatible credit currencies. Those happen during execution. If the request has an idempotency key, the outcome is cached; changing the request while reusing that key returns an idempotency error.
- Validation refusals before execution do not reserve an idempotency key. Injected scenario errors also occur before idempotency lookup and storage.
| route | parameter | allowed | why |
|---|---|---|---|
POST /v1/payment_intents |
capture_method |
automatic, automatic_async |
a manual capture holds funds without moving them; the twin captures on confirm |
POST /v1/payment_intents |
payment_method_options[card][capture_method] |
— | the card-level capture_method overrides the top-level one: manual holds the funds instead of capturing them |
POST /v1/payment_intents |
application_fee_amount |
— | a Connect fee splits the charge |
POST /v1/payment_intents |
transfer_data |
— | a Connect transfer moves money to another account |
POST /v1/payment_intents |
on_behalf_of |
— | Connect settlement on another account |
POST /v1/payment_intents/:id/confirm |
capture_method |
automatic, automatic_async |
a manual capture holds funds without moving them; the twin captures on confirm |
POST /v1/payment_intents/:id/confirm |
payment_method_options[card][capture_method] |
— | the card-level capture_method overrides the top-level one: manual holds the funds instead of capturing them |
POST /v1/subscriptions |
trial_period_days |
— | a trial defers the first charge |
POST /v1/subscriptions |
trial_end |
— | a trial defers the first charge |
POST /v1/subscriptions |
trial_from_plan |
false |
takes the trial from the price's recurring[trial_period_days]; a trial defers the first charge |
POST /v1/subscriptions |
coupon |
— | a discount changes every invoice total |
POST /v1/subscriptions |
discounts |
— | a discount changes every invoice total |
POST /v1/subscriptions |
promotion_code |
— | a discount changes every invoice total |
POST /v1/subscriptions |
default_tax_rates |
— | tax changes invoice totals |
POST /v1/subscriptions |
automatic_tax[enabled] |
false |
tax changes invoice totals |
POST /v1/subscriptions |
billing_cycle_anchor |
— | moves the first period boundary and prorates the first invoice |
POST /v1/subscriptions |
billing_cycle_anchor_config |
— | moves the first period boundary and prorates the first invoice |
POST /v1/subscriptions |
backdate_start_date |
— | bills past periods at creation |
POST /v1/subscriptions |
collection_method |
charge_automatically |
send_invoice leaves the invoice open instead of charging it |
POST /v1/subscriptions |
days_until_due |
— | only meaningful with send_invoice |
POST /v1/subscriptions |
payment_behavior |
allow_incomplete, error_if_incomplete |
default_incomplete and pending_if_incomplete leave the invoice unpaid; the twin charges at once |
POST /v1/subscriptions |
cancel_at |
— | the twin models cancel_at_period_end only |
POST /v1/subscriptions |
add_invoice_items |
— | one-time lines change the first invoice total |
POST /v1/subscriptions |
items[0][price_data] |
— | an inline price the twin cannot read; create the price, then pass items[0][price] |
POST /v1/subscriptions |
items[0][tax_rates] |
— | tax changes the line total |
POST /v1/subscriptions |
items[0][discounts] |
— | a discount changes the line total |
POST /v1/subscriptions |
application_fee_percent |
— | a Connect fee splits every invoice's charge |
POST /v1/subscriptions |
transfer_data |
— | a Connect transfer moves every invoice's charge to another account |
POST /v1/subscriptions |
on_behalf_of |
— | Connect settlement on another account |
POST /v1/subscriptions |
billing_thresholds |
— | bills when the amount crosses a threshold, not at the period boundary |
POST /v1/subscriptions/:id |
trial_end |
— | a trial defers the next charge |
POST /v1/subscriptions/:id |
coupon |
— | a discount changes every invoice total |
POST /v1/subscriptions/:id |
discounts |
— | a discount changes every invoice total |
POST /v1/subscriptions/:id |
promotion_code |
— | a discount changes every invoice total |
POST /v1/subscriptions/:id |
default_tax_rates |
— | tax changes invoice totals |
POST /v1/subscriptions/:id |
automatic_tax[enabled] |
false |
tax changes invoice totals |
POST /v1/subscriptions/:id |
billing_cycle_anchor |
unchanged |
resetting the anchor prorates and moves every renewal |
POST /v1/subscriptions/:id |
proration_date |
— | changes the proration amounts |
POST /v1/subscriptions/:id |
cancel_at |
— | the twin models cancel_at_period_end only |
POST /v1/subscriptions/:id |
pause_collection |
— | pauses charging while the subscription stays active |
POST /v1/subscriptions/:id |
collection_method |
charge_automatically |
send_invoice leaves the invoice open instead of charging it |
POST /v1/subscriptions/:id |
days_until_due |
— | only meaningful with send_invoice |
POST /v1/subscriptions/:id |
payment_behavior |
allow_incomplete, error_if_incomplete |
default_incomplete and pending_if_incomplete leave the invoice unpaid; the twin charges at once |
POST /v1/subscriptions/:id |
add_invoice_items |
— | one-time lines change the next invoice total |
POST /v1/subscriptions/:id |
items[0][price_data] |
— | an inline price the twin cannot read; create the price, then pass items[0][price] |
POST /v1/subscriptions/:id |
items[0][tax_rates] |
— | tax changes the line total |
POST /v1/subscriptions/:id |
items[0][discounts] |
— | a discount changes the line total |
POST /v1/subscriptions/:id |
items[0][deleted] |
false |
deletes the one item — Stripe 400s unless another item replaces it, and the twin models exactly one |
POST /v1/subscriptions/:id |
application_fee_percent |
— | a Connect fee splits every invoice's charge |
POST /v1/subscriptions/:id |
transfer_data |
— | a Connect transfer moves every invoice's charge to another account |
POST /v1/subscriptions/:id |
on_behalf_of |
— | Connect settlement on another account |
POST /v1/subscriptions/:id |
billing_thresholds |
— | bills when the amount crosses a threshold, not at the period boundary |
DELETE /v1/subscriptions/:id |
invoice_now |
false |
invoices pending prorations at once |
DELETE /v1/subscriptions/:id |
prorate |
false |
credits the unused time on cancel |
POST /v1/invoices |
collection_method |
charge_automatically |
send_invoice leaves the invoice open instead of charging it |
POST /v1/invoices |
days_until_due |
— | only meaningful with send_invoice |
POST /v1/invoices |
due_date |
— | only meaningful with send_invoice |
POST /v1/invoices |
auto_advance |
false |
Stripe finalizes and collects on its own about an hour later; the twin finalizes and pays only when told to |
POST /v1/invoices |
discounts |
— | a discount changes the invoice total |
POST /v1/invoices |
default_tax_rates |
— | tax changes invoice totals |
POST /v1/invoices |
automatic_tax[enabled] |
false |
tax changes invoice totals |
POST /v1/invoices |
pending_invoice_items_behavior |
include |
exclude leaves the pending items off the invoice; the twin always sweeps them |
POST /v1/invoices |
subscription |
— | includes only that subscription's pending items; the twin sweeps every pending item of the customer |
POST /v1/invoices |
application_fee_amount |
— | a Connect fee splits the charge |
POST /v1/invoices |
transfer_data |
— | a Connect transfer moves money to another account |
POST /v1/invoices |
on_behalf_of |
— | Connect settlement on another account |
POST /v1/invoiceitems |
price_data |
— | an inline price the twin cannot read; pass amount + currency, or price |
POST /v1/invoiceitems |
unit_amount |
— | the twin reads amount (a total), or price × quantity |
POST /v1/invoiceitems |
unit_amount_decimal |
— | the twin reads amount (a total), or price × quantity |
POST /v1/invoiceitems |
discounts |
— | a discount changes the line total |
POST /v1/invoiceitems |
tax_rates |
— | tax changes the line total |
POST /v1/invoiceitems |
invoice |
— | adds the line to a specific draft invoice; the twin sweeps pending items only |
POST /v1/invoices/:id/pay |
paid_out_of_band |
false |
marks the invoice paid with no charge |
POST /v1/invoices/:id/pay |
forgive |
false |
writes off the remaining amount |
POST /v1/refunds |
refund_application_fee |
false |
a Connect fee refund |
POST /v1/refunds |
reverse_transfer |
false |
a Connect transfer reversal |
POST /v1/customers |
coupon |
— | a discount changes every future invoice |
POST /v1/customers |
promotion_code |
— | a discount changes every future invoice |
POST /v1/customers |
cash_balance |
— | a second balance the twin does not model |
POST /v1/customers/:id |
coupon |
— | a discount changes every future invoice |
POST /v1/customers/:id |
promotion_code |
— | a discount changes every future invoice |
POST /v1/customers/:id |
cash_balance |
— | a second balance the twin does not model |
POST /v1/prices |
billing_scheme |
per_unit |
tiered pricing changes the amount per quantity |
POST /v1/prices |
tiers |
— | tiered pricing changes the amount per quantity |
POST /v1/prices |
tiers_mode |
— | tiered pricing changes the amount per quantity |
POST /v1/prices |
transform_quantity |
— | changes the billed quantity |
POST /v1/prices |
recurring[usage_type] |
licensed |
metered billing bills reported usage, not the quantity |
POST /v1/prices |
recurring[aggregate_usage] |
— | metered billing; a Plans-era parameter Stripe's current API no longer accepts on a price, refused rather than echoed |
POST /v1/prices |
recurring[meter] |
— | metered billing |
POST /v1/prices |
recurring[trial_period_days] |
— | a trial every subscription created with trial_from_plan takes; a trial defers the first charge |
POST /v1/prices |
unit_amount_decimal |
— | the twin reads unit_amount |
POST /v1/prices |
custom_unit_amount |
— | the customer chooses the amount |
POST /v1/prices |
currency_options |
— | multi-currency prices |
POST /v1/prices |
product_data |
— | an inline product the twin cannot create; create the product, then pass product |
Known gaps
Behavior on the implemented surface that is known to differ from Stripe and is written down here rather than fixed yet; each row names its follow-up.
| gap | today | Stripe | follow-up |
|---|---|---|---|
DELETE /v1/customers/:id leaves the customer's subscriptions active |
the stub is deleted: true; the subscriptions keep renewing on every clock advance, billing a deleted customer |
"Also immediately cancels any active subscriptions on the customer" | a drift probe (customer-delete-cancels-subscriptions) and the fix, under C2 (whatever live drift flags) |
every recurring price echoes recurring.aggregate_usage: null |
priceShape in packages/server/src/twin/build.ts still emits the field, though POST /v1/prices refuses the parameter |
the current API neither accepts nor returns it | dropped when the price shape is next re-pinned against the drift battery |
| deleting a draft invoice strands the items it swept | the items keep invoice: <the deleted id> — not pending (?pending=true misses them), on no invoice; the next POST /v1/invoices answers Nothing to invoice |
unverified: returned to pending, or deleted with the draft | a drift probe decides; until then, void instead of delete, or create the items after deleting the draft |
The legacy tickets surface (/api/v2/*)
The following describes existing seeds without a top-level zendesk profile. The opt-in native Zendesk profile has a separate resource model and wider supported API; neither profile is generally certified against the real Zendesk API.
Worlds also expose a Zendesk-shaped support-desk surface so composite ticket+billing agent runs can be graded by one diff: GET/POST /api/v2/tickets.json, GET/PUT /api/v2/tickets/{id}.json, GET /api/v2/tickets/{id}/comments.json, with Zendesk's envelopes, ISO timestamps (from the logical clock), status/priority lifecycles (closed tickets are immutable), and Zendesk-shaped errors.
This surface is demo-grade: faithful enough for realistic agent traffic, deterministic like everything else, but — unlike the Stripe surface — not drift-verified against the real Zendesk API, and deliberately simplified (numeric-string ids, comments stored on the ticket, no users/orgs/macros/search, no page-based pagination). Authenticated tool-mediated observations inform selected regressions, but do not establish full raw-API conformance; treat "the twin behaves exactly like Zendesk" as out of scope. See conformance and adversarial checks.
Supported ticket writes validate nested objects and field types before allocation or mutation. Creation honors status, updates can change subject, tags remove exact duplicates and empty entries, and string/null external IDs persist. Omitted comment privacy inherits the preceding comment; a new ticket retains the public default. Seed export/load preserves the opening comment's privacy. Malformed supported inputs and recognized unsupported native mutations fail explicitly, without partial ticket/comment/event changes.
Native users, requester/assignee IDs, groups, organizations, custom fields/statuses, follower/CC relations, uploads/HTML comments, protected concurrency and native idempotency remain outside this subset. They require the native-record and lifecycle milestones in the implementation plan. Priority clearing, provider text/HTML normalization and size limits, full tag normalization, configurable closed-ticket permissions, audits and pagination are also unqualified. A subset error is not a claim that Zendesk itself rejects that operation.