Skip to Content
SimulatorsLob changelog

Simulators

Lob changelog

What changed in each Lob simulator release, newest first.

v0.0.11 — 2026-11-06

  1. The docs gather errors and limits in one section. It shows Lob’s error body, the fixed rate-limit headers a create returns, how the address and postcard lists page, and how an idempotency key replays a postcard.

v0.0.10 — 2026-11-06

  1. The docs link the shared pages for the live feed and boot files. The connect section names the lob_python library, the scenario limits point to the boot seed file, and the seeding example reads the address back by its id.

v0.0.9 — 2026-11-05

  1. Setting the clock never stamps records before the timeline events it fired. When POST /_rystic/clock {"time": ...} fires timeline events due at its target, the next request’s records stamp after theirs instead of reusing their instant; a set past every event still stamps one millisecond past its target, as before.

  2. A paced clock keeps wall time, and rystic run --speed 0 stops it. Under RYSTIC_PACE, each tick moves the clock by the wall time that passed since the last, so a tick missed while the twin was busy no longer makes timeline events fire late. rystic run hands the twin only the pace it resolved, so --speed 0, a scenario without events, --tape and --live keep the clock manual even when your shell exports RYSTIC_PACE.

  3. Indexed list query parameters. include and size sent as include[0]=…&include[1]=…, as some SDKs encode arrays, read the same as the repeated parameter on GET /addresses and GET /postcards.

  4. Swagger UI to read and call the twin from the browser. /_rystic/swagger, linked from the state UI header, is Swagger UI over the published API trimmed to the operations this twin serves; Try it out sends each request to the twin.

  5. The twin documents its own control plane in Swagger. /_rystic/swagger now lists a Rystic control plane spec, served at /_rystic/swagger/spec/rystic-control-plane.json, describing the /_rystic/* routes this twin serves (its scenarios, inspect views and seedable registers by name) and its /_tracking_event route for recording a postal scan on a postcard.

  6. A state dump seeds back as it was, and the next record gets a fresh id. GET /_rystic/inspect posted to POST /_rystic/seed after a reset restores every record where it was, per-caller records included, which used to land under a doubled key. The next record created after any seed passes every uuid and event id the simulator minted that the seed holds. GET /_rystic/inspect?view=sequences lists every id counter, and a world document’s new sequences key restores them, so ids the simulator renders from a counter continue where the dump left off.

  7. A slow client no longer holds up the others. The simulator reads a request’s whole body before it starts serving it and sends the answer after, so a client that uploads slowly, or reads a large answer slowly, no longer delays other clients’ requests. Answers are unchanged; one larger than 4 MiB still makes other requests wait while it is sent.

  8. The call log shows what each call asked for and whether it succeeded. Each GET /_rystic/calls entry now carries args, the call’s query and body arguments with credentials redacted (long values cut, file contents left out), and ok, false for an answer that is not 2xx. A path the simulator does not serve is marked unknown_route: true. ?tail=N returns only the last N calls, and dropped counts the calls the log no longer keeps.

  9. The docs are one page, with a page per feature. The archive ships README.md in place of HOWTO.md, with the same sections in the same order as every Rystic simulator’s page, and CHANGELOG.md beside it. The Dashboard and the mail stream (tracking scans and the cancellation window) each have their own page under docs/features/, and every example on these pages runs as written against a fresh simulator.

  10. The docs page links the admin token section. The page now sends you to the admin token’s section of the shared-simulators page.

  11. The Scenarios table is easier to scan. Each row says in a line what the scenario is for, and a list below the table says what each scenario holds.

  12. Pro starts with a 14-day trial. Free has ended, so every simulator is on Pro and Enterprise, and the docs name only those plans.

  13. Limitations is shorter to read. It opens with the known issues and one sentence counting the rest. The detailed lists follow, and the website folds them behind “Show all limitations”.

  14. The page has no Plans section. The Quickstart names the plans that run the simulator, and the pricing page lists what each plan includes.

  15. The docs read more plainly, mark each difference from Lob and keep to the simulator’s own facts. Sentences are shorter, and each behavior that differs from Lob is marked “Differs from Lob” where it’s described, such as how long a postcard stays cancelable, the Lob-Version header and how ids are made. Rules with several outcomes, such as what the scan route refuses, are tables, and the known issues use the same plain wording. Auth, seeding and reset keep only what is particular to Lob and link the shared pages for the rest.

v0.0.8 — 2026-10-12

  1. The scripts in this archive are MIT-licensed. check.sh and the archive’s other scripts are published under the MIT License at https://github.com/Rystic-Inc/rystic-scripts , so you can copy, change and share them. LICENSE.txt no longer covers them; it still governs the twin itself.

  2. A world can carry its own timeline. A --seed file, or a complete world document posted to POST /_rystic/seed, may add credentials, binding presented credentials to actors, and events: calls the twin makes on its own API at set sim times as one of those actors, through the same handlers and egress as your requests. A top-level credentials or events key the twin used to ignore now takes effect. RYSTIC_PACE=<speed> plays the clock, and the timeline, in real time, and an event’s at may be +<duration>. /_rystic/inject, --inject and RYSTIC_INJECT now need state_set instead of scenarios.

  3. A timeline call can hand what it minted to later events. A call event may add bind, {"<name>": "<JSON Pointer>"}, saving fields of its answer, such as an ID the twin minted; a later event uses one as ${name} in a call’s path, headers or body or in a seed event’s record keys and values (a register name cannot be bound). A schedule naming a ${name} no earlier call binds is refused at load, and an event whose binding call failed records an error instead of firing.

  4. design_studio carries a timeline. As the clock moves past 2026-03-01T00:00:00Z, the world saves Harborview Dental’s second office, sends a spring-checkin postcard to Redwood Realty under an idempotency key, schedules one to Atlas Moving and cancels it a day later, then lists the campaign, each a call on the twin’s own API under the world’s bound test key. A test that advances the clock past +1h now sees these records.

  5. A world can move a postcard through the mail. POST /_tracking_event records a USPS scan (In Transit, Delivered, Returned to Sender and Lob’s other documented names) on a postcard’s tracking_events, so a world’s timeline can carry a piece from mailing to delivery.

  6. A clock jump fires each timeline event at its own time. When one POST /_rystic/clock or paced tick passes several events, each fires with the clock at its at, so the records it creates carry that time, and the clock then lands where the move asked. An event already due when the clock moves fires at the current time.

  7. A clock jump stamps each timeline event at its exact instant. When a clock move passes timeline events, the records each one makes carry exactly its at, to the millisecond, whatever traffic came before it, so one jump leaves the same records as stepping the clock to each event in turn.

  8. A timeline call can carry raw bytes, XML or a JSON array. A call event may send body_text or body_base64 instead of body; the twin sends it as written, under the route’s own content type (XML, bytes, JSON or a form), so a scheduled call can upload an object’s bytes or post an XML document, and ${name} fills in body_text. A body may be a JSON array where the route reads one. A call that answers XML binds from its elements by JSON Pointer, such as /InitiateMultipartUploadResult/UploadId.

v0.0.7 — 2026-10-11

  1. check.sh fails fast on a port that never answers. Its port check and boot wait give each /_rystic/version probe two seconds, so an address where something accepts connections but never replies fails the check with a message instead of hanging.

  2. The test fixtures seed Lob’s render status on postcards. In lob_mailroom, lob_returned_mail and lob_campaign_blast under sim/testdata/scenarios/, a mailed postcard’s status was a mail state (delivered, in_transit, returned_to_sender, …) or canceled. It is now rendered, a value of Lob’s PDF render status; the mail’s progress stays in tracking_events, and the canceled card is still marked deleted: true.

  3. Deleting an address marks the postcards sent with it, as Lob does. After DELETE /addresses/{adr_id}, a postcard sent from or to that address is still served, and its embedded from or to address answers deleted: true. Measured against Lob’s test environment.

  4. A list’s next_url and previous_url lead back to the twin, and /v1 paths answer. Both list cursors name the host your client reached the twin on, under /v1, where they named api.lob.com, so a client that follows one stays on the twin. Every Lob path now answers with and without the /v1 prefix, so an SDK’s host override may keep Lob’s https://api.lob.com/v1 form with the twin’s host. A twin started with rystic run --intercept keeps api.lob.com in those URLs, which its gateway routes to the twin.

  5. A request without credentials answers Lob’s Missing authentication. Every route answers a request that presents no Basic credential, with no Authorization header or one under another scheme such as Bearer, with Lob’s 401 {"error":{"message":"Missing authentication","status_code":401,"code":"unauthorized"}} before it reads anything else, so a refused create writes nothing; it answered “Your API key is not valid…” or “The request isn’t authorized.” before. A Basic header with an empty username, or with a payload that is not base64, still answers the route’s own 401.

  6. A seeded postcard renders as a created one. In design_studio and any world you seed, a postcard’s to and from answer the address objects a created postcard embeds, where they answered bare address IDs, and a record missing object (or a postcard missing carrier) takes Lob’s value. design_studio’s postcards also carry raw_url, print_speed, to_be_expunged_date, qr_code, completed_at, is_creative_proof, is_dashboard, usps_campaign_id and lob_credits_funding_status, and its addresses recipient_moved, as created ones do.

  7. The guide’s Coverage and Limitations are generated from the twin. Coverage lists every route the twin serves at its /v1 path, and Limitations end with the twin’s open issues and the spec operations, unmeasured branches and unmodeled behaviors it leaves out, regenerated whenever the twin changes. The postcard list’s total count is requested as include[]=total_count, the form Lob takes.

  8. lob-assets.com render URLs are declared Lob’s asset host. A postcard’s url, raw_url and thumbnail URLs on lob-assets.com, seeded or answered, are handed out as written and never rebased onto the twin, by declaration (pipeline_policy.json host_roles) rather than by omission, so a seed carrying Lob’s own render URLs passes RYSTIC_SEED_GATE=reject. The twin serves no render behind them.

v0.0.6 — 2026-09-30

  1. Rate-limit headers keep their lowercase spelling. ratelimit-limit, ratelimit-remaining and ratelimit-reset on address and postcard creation now reach the wire lowercase instead of as Ratelimit-Limit and friends, so a client that keys on the received header name reads the names as written.

  2. The state UI shows the account as the Lob dashboard. On Pro and Enterprise, /_rystic/ui opens a live Lob-style view of the twin — postcards with their proof, addresses and tracking, and the address book — where you can send and cancel postcards and add or delete addresses through the API, beside Mail stream and Calls cards.

v0.0.5 — 2026-09-26

  1. The guide starts from an API key and says what each plan reaches. HOWTO.md now opens with rystic login rys_…, says what Free, Pro and Enterprise boot and which control-plane routes each answers, and states that DELETE /addresses/{adr_id} is the one route that checks no credential; SCENARIOS.md says what each plan boots. The rystic features example that filtered on a status no postcard carries now filters design_studio by client, the idempotent-send example mails between two of design_studio’s own addresses, and the features and commands that need Pro or Enterprise say so. check.sh finds the API key rystic login stored as well as a license file, looked up in the CLI’s order.

v0.0.4 — 2026-09-26

  1. Only design_studio ships as a named scenario. The older scenarios are now test-only fixtures and are no longer accepted by --scenario, RYSTIC_SCENARIO, or POST /_rystic/scenario. Tests and evaluations can load their unchanged register maps and clocks from sim/testdata/scenarios/ through the control plane.

v0.0.3 — 2026-09-25

  1. Self-serve on the metered edition replaces the free edition. A Rystic account’s token runs the metered build: rystic login rys_… once, then rystic run or docker run -e RYSTIC_TOKEN=…. It carries every scenario and control-plane route your plan includes, and bills active 30-second blocks against the plan’s allowance. The fixed-world free download and its usage report are gone.

  2. A new world, design_studio, now the default. --scenario design_studio boots Website Design Contractors’ account: the studio’s return address, 38 client addresses and 63 first-class postcards, a holiday campaign and a thank-you card after each site launch, each tagged metadata[campaign] and metadata[client], with one holiday card returned to sender. The clients are the same ones the studio’s Slack, Stripe and Resend worlds hold. A lease that grants no scenarios boots it instead of lob_mailroom.

  3. Every probe matches live Lob. The last full-suite round scored 21 of 21 probes on 2026-09-24 against real api.lob.com, and the four differences the first release listed are repaired: a last page answers next_url: null, an inline to or from carries email and phone as null and a postcard read answers deleted: false, a canceled postcard reads back as the canceled piece with deleted: true, and the captured 210 King St address reads back with Lob’s ZIP+4. One known difference remains, disclosed under Limitations: a failed postcard can still be canceled inside its send window where Lob answers 422 not_deletable.

v0.0.2 — 2026-09-24

  1. Postcard cancellation and response consistency. Canceled postcards remain retrievable with deleted: true and disappear from collection lists; created postcards retain front/back thumbnail links. Scheduled filtering compares send and creation dates, and omitted send dates use the documented default cancellation window. Address creation stores its standardized response, while reads preserve stored address values. Lob API keys resolve through the shared HTTP Basic authentication layer, including regenerated testbooks and shape checks. Collection probes isolate their seeded postcards with a fresh metadata filter and exercise pagination with two records.

  2. Updated coverage guidance. The Lob guides now describe repaired cancellation, pagination, thumbnail and address responses, clarify HTTP Basic authentication, and distinguish seeded mail stages from unverified asynchronous rendering and failure transitions. The guides also disclose the failed-postcard cancellation mismatch.

v0.0.1 — 2026-09-24

  1. A local Lob for your mail-sending code’s tests. The first release of the Lob twin: 8 routes over addresses (create, retrieve, delete, list) and postcards (create, retrieve, cancel, list), served from a model validated against real api.lob.com with a test key. Point your Lob SDK’s host at the twin — http://localhost:8080, no /v1 — and your existing calls run against it unchanged, with form-encoded or JSON bodies.

  2. A retried send is the same postcard. POST /postcards honors an Idempotency-Key header, or an idempotency_key query parameter, as Lob does: a replay under the same key answers the original postcard instead of sending a second one, so the retry loop around your send is testable without mailing anything twice.

  3. Four named worlds, baked in. --scenario lob_mailroom boots five postcards standing at every healthy stage from rendered through delivered, with their tracking histories; lob_returned_mail the returned, failed, re-routed and canceled pieces with their failure_reason; lob_campaign_blast six sends under one campaign_id for paging and filters; lob_quiet_desk one of everything. SCENARIOS.md says what each is for.

  4. Seed the mailroom your test needs, and get the same one twice. POST /_rystic/seed lands addresses, postcards and idempotency keys directly; GET /_rystic/registers lists every field it accepts. POST /_rystic/reset rewinds the ID sequence and the clock, so two identical runs give byte-identical postcards down to id, send_date and expected_delivery_date.

  5. It never reaches the network, and it needs no key. Any key is accepted as the Basic username and the twin dials nothing: nothing you send renders a PDF, counts against a rate limit, or could ever print or mail a piece. Not in this release: letters, checks, self-mailers, templates, campaigns as a resource, address verification and webhooks; a postcard does not move through the mail stream on its own — boot a world or seed the stage you need.

  6. What is measured, and what is not. Fidelity is scored differentially against real api.lob.com with a test key, per response field, not by eyeball: the last full-suite round was 13 of 21 probes on 2026-09-24. That is an early number and this release ships it as-is. The misses are four known differences, listed in HOWTO.md under Limitations: next_url is a link on the last page where Lob sends null; an inline to or from address omits email and phone, and a postcard read answers deleted: false, where Lob sends null; a canceled postcard reads back as 404 where Lob answers the canceled piece; and an address reads back with the ZIP you sent, not Lob’s ZIP+4. Rounds are re-run daily and each release states its own number.

Last updated on