How Strimz fits together
A 90-second tour of the six processes and what each one does.
Strimz runs as six processes. You only ever call one of them directly, the API. Knowing what the other five do is mostly useful when something breaks and you need to pick the right log file.
Architecture at a glance
Requests hit apps/api. It writes to Postgres and pushes background
work onto Redis. When a payer submits an EIP-3009 or EIP-2612
signature, the relayer module inside apps/api signs and broadcasts
the on-chain transaction with a KMS-managed key. The scheduler runs
the cron side of the system: subscription charges, retry sweeps, and
the webhook delivery queue. The indexer watches Arc and writes the
events it sees back to Postgres. The agent reads from Postgres but
never signs anything.
The six processes
apps/api
Nest + Fastify. Validates input with Zod, writes to Postgres, fires events. Also hosts the relayer that broadcasts payer-signed meta-transactions. The relayer's key is held by a KMS provider; the API process never sees raw bytes.
apps/indexer
Go process polling Arc RPC. Decodes contract events, projects them into Postgres tables. Re-derives state from chain history.
apps/scheduler
Nest + BullMQ. Runs cron jobs (subscription charges and retry sweeps) and the webhook delivery queue. On startup it decrypts the webhook signing secrets out of Postgres so the delivery worker can read them from Redis.
apps/agent
The AutoPay Agent. Hourly + daily crons. Recovery emails, cashflow digests, anomaly detection. Cross-chain routing is coming; payers pay on Arc only today. Holds no key.
apps/web
Next.js. Marketing site, merchant dashboard, hosted checkout, public storefronts, and these docs all live in the same app.
packages/contracts
Foundry workspace. Payments and Subscriptions are immutable. Registry, FeeCollector, and TokenWhitelist are UUPS-upgradeable. Every contract uses ERC-7201 namespaced storage.
Repository layout
Where to look when something is wrong
Each process owns a well-defined slice of behaviour. Knowing which slice covers the symptom is usually enough to pick the right log.
| Symptom | Process to check |
|---|---|
| API returning 5xx | apps/api logs |
| Subscriptions not charging on schedule | apps/scheduler (sweeper tick + worker) |
| Webhooks failing or stuck retrying | apps/scheduler (delivery worker) |
| New on-chain payment not in dashboard | apps/indexer (cursor caught up?) |
| Merchant not getting daily digest | apps/agent (cashflow cron logs) |
What you don't need to think about
Built-in guarantees
- On-chain idempotency. The contract's
usedAttempts[id]mapping makes a charge attempt safe to replay: a reused id is skipped with outcomeDuplicate, never charged twice. - Webhook delivery retries. The scheduler makes 5 attempts, waiting 1m, 5m, 30m and 2h between them. If all five fail, you get an email, and 5 permanent failures in 24 hours auto-disable the endpoint.
- Finality. Arc has deterministic sub-second finality, so there are no reorgs to wait out. The indexer's
CONFIRMATIONSsetting is required, and 1 is the recommended value.
Want to dig deeper?
Resources
The objects you'll interact with: what they mean, when they exist, what their lifecycles look like.
Idempotency
How Strimz keeps retries safe at every layer: the API, the scheduler, and the contracts.
On-chain architecture
The contract suite. What's immutable, what's upgradeable, why we drew the line where we did, and how to verify the deployment yourself.
Modes of failure
What can go wrong, what you'll see when it does, and what Strimz does about it automatically.

