Skip to Content

Simulators

Lob simulator

lob-twin is a local, stateful simulator of Lob’s print-and-mail API — addresses and postcards with real validation, cancellation windows and idempotent replay. Nothing it answers can ever print or mail a piece.

Free edition

No license needed: download the free edition, untar, run ./lob-twin. One fixed world (lob_mailroom: six addresses and five postcards from rendered through delivered), reads and writes, no control plane beyond POST /_rystic/reset — the free edition section says exactly what is in and out.

./lob-twin                      # port taken? ./lob-twin --addr :9000
curl -s -u test_anything: 'http://localhost:8080/postcards?status=delivered'
Quickstart

Builds are in limited release — request access for a download link.

curl -fsSL https://www.rystic.ai/install.sh | sh
rystic login ./rystic-license.json
rystic run lob-twin -d --scenario lob_mailroom
curl -fsS http://localhost:8080/_rystic/version

Expect product to be lob-twin and version to be 0.0.2. An older version means an older install: rystic pull lob-twin upgrades it.

Lob’s auth is the API key as the Basic username with an empty password; the twin accepts any key:

curl -s -u test_anything: 'http://localhost:8080/postcards?status=delivered'

Point the SDK at it — host only, code unchanged. The twin serves /addresses, not /v1/addresses:

configuration = lob_python.Configuration(
    username="test_anything",
    host="http://localhost:8080",
)
Coverage

8 routes: addresses (create, retrieve, delete, list) and postcards (create, retrieve, cancel, list), plus the /_rystic/ control plane. POST /postcards honours Idempotency-Key, so a retried send returns the original piece. The postcard list filters on status, mail_type, scheduled, campaign_id, send_date, date_created and metadata, and pages with limit, before and after.

Not modelled: letters, checks, self-mailers, templates, campaigns as a resource, address verification, webhooks.

Worlds

Four named worlds are baked into the binary — --scenario <name>, or POST /_rystic/scenario to switch without restarting:

worldfor
lob_quiet_deskthe smallest complete account: one recipient, one freshly rendered postcard
lob_mailroomevery healthy stage of the mail stream, rendered through delivered, with tracking histories
lob_returned_mailreturned to sender, failed verification, re-routed and canceled pieces, each with its failure_reason
lob_campaign_blastsix sends under one campaign_id on staggered dates, for paging and campaign filters
Seeding, determinism, reset

POST /_rystic/seed takes a register map (state_address, state_postcard, state_idempotency_key) and lands it directly; GET /_rystic/registers lists every field. 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.

curl -X POST localhost:8080/_rystic/reset
curl -X POST localhost:8080/_rystic/seed -d '{"state_address":{"adr_seed1":{"id":"adr_seed1","object":"address","name":"SEED","address_line1":"185 BERRY ST","address_city":"SAN FRANCISCO","address_state":"CA","address_zip":"94107"}}}'
curl -s -u x: localhost:8080/addresses/adr_seed1
Limitations
  1. Nothing is rendered: url, raw_url and front/back thumbnail links are placeholders; the twin generates no PDFs or images.

  2. A postcard does not progress on its own. A create answers processed and stays there; boot a world or seed the stage you need.

  3. No general address verification: the captured 210 King St, San Francisco case receives ZIP+4 94107-1702, but other addresses have no USPS lookup or deliverability check. Reads preserve stored address values.

  4. No webhooks; the twin never makes an outbound call.

  5. Ids are a sequence (psc_0000000000000001), not Lob’s random hex.

  6. Populated asynchronous rendered and failed results, including provider-driven transitions and failure_reason, remain outside verified probe coverage (RYS-1084; residual dispositions in RYS-623). Seeded worlds do not verify Lob’s rendering process.

  7. Failed postcards: Lob rejects cancellation with 422 not_deletable; the twin currently allows it inside its send window. This refusal path is not supported in this release (RYS-1090).

Canceled postcards remain retrievable with deleted: true and disappear from lists. Terminal pages return next_url: null, inline postcard addresses include nullable contact fields, and created postcards retain front/back thumbnail links.

The full guide ships in the release as HOWTO.md.

Release notes

Newest first.

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. (RYS-1067)

  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. (RYS-1085) The guides also disclose the failed-postcard cancellation mismatch, with repair tracked in RYS-1090. (RYS-1085)

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. (RYS-1023)

  2. A retried send is the same postcard. POST /postcards honours 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. (RYS-1023)

  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. (RYS-1023)

  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. (RYS-1023)

  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. (RYS-1023)

  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. (RYS-1023)

Last updated on