Seed format reference
A seed is a JSON file containing a world's starting data and logical time. Reuse a seed to run the same test from the same starting point.
Create starting data from a template or import in the dashboard, or write a seed file directly. Validate a file before using it:
worlds seed validate my-seed.json
Choose the connector profile
| Profile | Where records live | Task format |
|---|---|---|
| Stripe | data.customers, products, prices, invoices and subscriptions |
Version 1 billing tasks |
| Native Zendesk | Top-level zendesk object |
Version 2 support tasks |
| Stripe and native Zendesk together | Both of the above | Version 2 with explicit billing authority |
| Legacy ticket desk | data.tickets, usually beside Stripe records |
Version 1 |
Native Zendesk and the legacy desk cannot coexist in one seed. Stripe's implemented subset has real-API verification; native Zendesk has local conformance coverage, with live qualification pending. See connector scope.
Top level
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.
Native Zendesk records
The zendesk object contains version: 1, principal_id and arrays for users, groups, organizations, memberships, tickets, comments and audits. Start from the bundled zendesk-support-v1 example in the dashboard, or an offline import. To inspect the bundled JSON, use GET /admin/seeds/zendesk-support-v1 on your local server; see the Admin API.
Native IDs and references are positive safe integers. The principal must be an active, nonsuspended agent or admin. Directory membership, assignment, comments and audit history are validated together; copying ticket rows alone is not sufficient.
In the beta.4 candidate and current source checkout, native timestamp chronology uses the full accepted ISO instant, including fractional digits beyond milliseconds and equivalent timezone spellings. The frozen published 0.9.0-beta.3 and published beta.1/beta.2 archives predate this validation fix and compare native seed timestamps at millisecond precision. Supplied creation and update times must be at or before the epoch; update must not precede creation. Omitted creation defaults to the epoch and omitted update to creation. A comment and its owning audit must have equal instants. Original timestamp strings survive export and reload.
The native scope documents supported behavior. Source checkouts also include the complete native seed schema for contributors.
Logical closure and follow-up metadata
Optional zendesk.lifecycle.closure enables simulated closure:
| Field | Accepted value |
|---|---|
after_hours |
Integer from 1 to 672 |
sweep_phase_seconds |
Integer from 0 to 3599 |
author_id |
Existing native agent or admin |
Omitting closure adds no defaults. Tickets may declare solved_at (ISO instant or null) and via_followup_source_id (native ticket ID). Audits may declare via_channel as api or rule.
For closure-enabled solved tickets, created_at <= solved_at <= updated_at <= epoch. Ticket, comment and audit history must not extend beyond the derived closure boundary. Active unsolved tickets require absent or null solved_at; closed tickets may retain the solve episode.
A follow-up source must exist, be closed and be no newer than its child. Equal creation times are valid; self-references and ancestry cycles are rejected.
These fields survive export and reload. Read closure timing and limits before using them to model a workflow.
Stripe records
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.
Legacy ticket records
data.tickets[] serves the demo-grade, Zendesk-shaped desk. It is separate from native Zendesk. IDs are assigned in file order as "1", "2" and so on.
Legacy ticket fields
| Field | Type or default |
|---|---|
subject, body |
Required nonempty strings |
requester |
Required {name, email}; nonempty name and valid email |
status |
new, open, pending, hold, solved or closed; default open |
priority |
low, normal, high or urgent; default normal |
tags |
String array; default [] |
external_id |
Optional string or null |
first_comment_public |
Optional boolean; defaults in behavior to true |
created_offset |
Nonnegative seconds before the epoch; default 0 |
Billing references in ticket text are not foreign keys. Version 1 tasks compile into this profile, with one ticket per task and an answer key kept separately.
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. For native Zendesk, it checks directory relationships, principal authority, comment/audit links and lifecycle metadata.
Imported native IDs set allocator floors so runtime-created records cannot reuse them. Native wire IDs are numeric; admin state keys remain strings.
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.
Offline native Zendesk import
worlds seed import --from-zendesk --input support-export.json --name my-support
This CLI accepts a complete native seed or the explicit worlds-zendesk-native-export format below. It does not fetch a live Zendesk account or accept arbitrary vendor exports. The dashboard offers guided data preparation, including supported offline Zendesk Full JSON conversion.
The import remaps identities, replaces supported identifying text and preserves valid relationships, privacy, timestamps and audit history. Attachments and unsupported fields are omitted and counted. Review the generated manifest.
Defaults are seeds/my-support.json and worlds/my-support.manifest.json. An optional --identity-map is private. See native import workflows for destination rules and composition with Stripe.
Accepted native input contracts
An ordinary native seed must have a top-level zendesk profile and empty Stripe/legacy data collections. Alternatively, the wrapper has:
format: "worlds-zendesk-native-export"version: 1epochzendeskcontaining the full native profile
Both forms require the principal, directory and membership records, tickets, comments and audits needed to satisfy all references. Optional lifecycle metadata must be coherent.
A populated composite seed is not an input to this importer. Import each source separately, then compose sanitized seeds with matching epochs. A native seed's clock configuration is preserved; the wrapper uses seed defaults.
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 |
Native Zendesk export keeps supported directory records, tickets, comments, full audit history and lifecycle metadata. 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 |
|---|---|
zendesk-support-v1 |
Explore native directory, ticket, comment and audit records |
zendesk-outcomes-support-v1 |
Native support tasks; used by init --connector zendesk |
zendesk-outcomes-billing-v1 |
Combined support and Stripe billing tasks; used by init --connector composite |
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 version 1 packs |
Bundled seeds are synthetic fixtures. See the generated seed catalog for their builders and starter packs for ready-made tasks.