# Zapier → Verae API → Zapier How a timestamp job is submitted, watched, and returned. Lab today: `MOCK_VERAE=true`. Live path is the same HTTP contract with `MOCK_VERAE=false`. ## Rule ```text Zapier cloud --HTTPS only--> zap.zapier.georgelambert.org (access-zapier) | v HTTPS middleware :3100 /zapier/v1 | +---------------+----------------+ | HTTPS | NATS (private cluster) v v api.veraetime.net jobs.watch → poller POST /api/timestamp GET /api/status/{jobId} GET /api/status/{jobId} | POST /api/verify v jobs.events | +--------------------+--------------------+ | wait subscriber | webhook-deliver v v HTTP response to Zap POST hooks.zapier.com ``` Zapier never has `NATS_URL`. Verae never has `NATS_URL`. ## 1. Submit (async create) 1. Zap action **Create Timestamp** (or curl) `POST https://zap.zapier.georgelambert.org/zapier/v1/timestamp` with portal API key `zmw_…` (not a Verae JWT). 2. access-zapier / edge: authz, then `meter()` records cents; if `NATS_URL` is set, `natsPublish(verae.billing.usage.recorded)`. 3. Forwards to middleware `POST /zapier/v1/timestamp`. 4. Middleware `checkEntitlement` (local plan limits today). 5. `veraeClient.createTimestamp`: - **Mock:** in-process job, fake `jobId`. - **Live:** `POST https://api.veraetime.net/api/timestamp` with JWT → **202** `{ jobId }`. 6. Splitter (in-process): hash vs attachments; optional `verae.archive.put` (bytes never go to chain). 7. `enqueueWatch` → JetStream **`verae.zapier.jobs.watch`**. 8. HTTP **202** `{ jobId, sha256, existing }` back to Zapier immediately. Same SHA-256 is idempotent (`existing: true`). ## 2. Monitor (job poller) The poller is **in the middleware Node process** when `NATS_ENABLED=true` (`startJobPollerWorker`). Fleet does **not** spawn a second copy (`job-poller.enabled: false`). 1. Durable consumer `job-poller` on `ZAPIER_JOBS` / `verae.zapier.jobs.watch`. 2. Resolve `tokenRef` → tenant Verae password → `POST` login → JWT (stays in middleware). 3. `GET /api/status/{jobId}` (mock or live timestamp server). 4. `pending` → `nak` after `intervalMs` (retry). 5. `completed` → publish `jobs.events` `{ event: timestamp.completed, status, jobId, tenantId, traceId }`. 6. `failed` → `timestamp.failed`. Too many attempts → `timestamp.timeout`. 7. `ack` the watch message. Live OpenAPI: poll until `completed` / `failed`. Hash lookup is **501** on live (`lookupHash`). Map `certificate`, `timestamp`, `blockIndex`, `sha256` into the receipt Zap samples expect. ## 3. Wait (sync Zap step) **Create Timestamp and Wait** → `POST /zapier/v1/timestamp/wait`. 1. Same as submit (watch is already on NATS). 2. `waitForJobEvent(jobId, WAIT_TIMEOUT_MS)` subscribes to **`verae.zapier.jobs.events`**. 3. On matching `jobId` event: return status JSON (certificate / receipt). 4. On timeout: `{ status: pending, jobId }` so the REST Hook can finish the Zap. `includeAttached`: after seal, aggregator should `verae.archive.query`; bloom hits merge extra receipts. **Simulator does this; live aggregator hook is TBD.** ## 4. Push back to Zapier - **Wait response:** HTTP body on the same Zap step. - **REST Hook:** Zapier subscribes `POST /zapier/v1/hooks`. On `jobs.events`, middleware enqueues **`verae.zapier.webhooks.deliver`**. Worker POSTs `targetUrl` (`hooks.zapier.com`). - **Poll status:** Zap action **Find Job Status** → `GET /zapier/v1/status/{jobId}` → middleware `GET` Verae (or last event). ## 5. Verify `POST /zapier/v1/verify` → middleware → Verae `POST /api/verify`. No job watch. ## 6. Batch Merkle (designed; same bus) From `MESSAGE-FLOWS.md`: 1. `POST /v1/timestamp/batch` — one item per line. 2. Middleware hashes leaves, builds Merkle tree. 3. Chain seals **only the root** (`POST /api/timestamp` on the root). 4. Each leaf proof → `verae.archive.put` `kind: tree`. 5. Central hash lookup of a leaf misses unless `includeTree=true`. 6. Aggregator broadcasts `verae.archive.query` `kinds=["tree"]`. 7. Tree node bloom hit → `archive.reply.` with proof. Live wait path does not yet call the aggregator, so tree-leaf receipts are not merged on NS1. ## Connect live `api.veraetime.net` Still blocked on operator bind credentials. When ready: ```bash # middleware env MOCK_VERAE=false VERAE_API_BASE_URL=https://api.veraetime.net NATS_ENABLED=true NATS_URL=nats://10.10.10.21:4222,nats://10.10.10.22:4222,nats://10.10.10.23:4222 JETSTREAM_REPLICAS=3 ``` Then: working Verae login, one live seal, confirm 202/status/verify, map receipt fields, REST Hook to a private Zap. See `zapier-decisions/STATUS.md` “Once MOCK_VERAE=false”. ## Billing on this path | Hop | Designed | Running | |-----|----------|---------| | Edge meter | `verae.billing.usage.recorded` | **coded**; fires when edge has `NATS_URL` | | Account-balance | subscribe + debit prepaid; 402 if empty | **subscriber running**; 402 from books not proven on every Zap | | Middleware quota | local files via `recordUsage` | **running**; not NATS | | `verae.zapier.usage` | optional JS meter | stream only | ## Connect a new internal process ```bash export NATS_URL=nats://10.10.10.21:4222,nats://10.10.10.22:4222,nats://10.10.10.23:4222 # clone verae-nats-process, rename subjects, register in verae-fleet ``` Jobs stay: HTTP in from Zapier → HTTPS to Verae → NATS watch/events → HTTP back to Zapier.