Zapier Platform CLI app (timestamp/verify/hooks)
  • JavaScript 100%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Andrew Birkholz d42aa4e455 README: proposed Verae chain API changes for hash lookup, receipts, metadata
Answers the developer question "how do I query my original hash, get a
receipt on submission and on lookup, see all timestamp metadata, and store
metadata or links in a timestamp". Lists the required additions to
api.veraetime.net (hash lookup, idempotent create with digest input, full
status on lookup, metadata field), recommended extras (certified retrieval
receipt, JSON errors, completion callback), spec rules for public vs
private metadata, and what the middleware can cover without API changes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 12:34:00 -06:00
creates Bring the Zapier app to parity with the middleware /zapier/v1 contract 2026-09-15 12:16:47 -06:00
lib Bring the Zapier app to parity with the middleware /zapier/v1 contract 2026-09-15 12:16:47 -06:00
searches Bring the Zapier app to parity with the middleware /zapier/v1 contract 2026-09-15 12:16:47 -06:00
test Bring the Zapier app to parity with the middleware /zapier/v1 contract 2026-09-15 12:16:47 -06:00
triggers Bring the Zapier app to parity with the middleware /zapier/v1 contract 2026-09-15 12:16:47 -06:00
.gitignore Bring the Zapier app to parity with the middleware /zapier/v1 contract 2026-09-15 12:16:47 -06:00
authentication.js Bring the Zapier app to parity with the middleware /zapier/v1 contract 2026-09-15 12:16:47 -06:00
index.js Bring the Zapier app to parity with the middleware /zapier/v1 contract 2026-09-15 12:16:47 -06:00
NATS.md Bring the Zapier app to parity with the middleware /zapier/v1 contract 2026-09-15 12:16:47 -06:00
package-lock.json Bring the Zapier app to parity with the middleware /zapier/v1 contract 2026-09-15 12:16:47 -06:00
package.json Bring the Zapier app to parity with the middleware /zapier/v1 contract 2026-09-15 12:16:47 -06:00
README.md README: proposed Verae chain API changes for hash lookup, receipts, metadata 2026-09-15 12:34:00 -06:00
SUMMARY.md Bring the Zapier app to parity with the middleware /zapier/v1 contract 2026-09-15 12:16:47 -06:00

verae-zapier

Zapier Platform CLI app for Verae. Runs on Zapier's servers. Calls only middleware HTTPS (MIDDLEWARE_BASE_URL, /zapier/v1/*), never NATS and never api.veraetime.net.

Forgejo: https://git.georgelambert.org/marchon/verae-zapier-app
Middleware: https://git.georgelambert.org/marchon/verae-middleware
Catalog: https://zapier.georgelambert.org/packages/verae-zapier/README.pdf
Sphinx API sheets: https://zapier.georgelambert.org/sphinx/index.html

Role

Runs on Zapier's servers. Calls only the middleware HTTPS API (MIDDLEWARE_BASE_URL), never NATS and never api.veraetime.net directly. The middleware owns customer API keys (zmw_…), plans and quotas, job watching, REST Hook delivery, and the upstream Verae chain client (POST /auth/login, /api/timestamp, /api/status, /api/verify, … on api.veraetime.net).

Modules

File Zapier type Middleware call
authentication.js custom API key GET /zapier/v1/auth/me → connection label tenantId (plan)
index.js app definition beforeRequest sends the key as Authorization: Bearer and x-api-key; afterResponse maps { error, code, details } (402/403 billing → upgrade URL, 429 → ThrottledError)
lib/middleware.js helpers base URL, error parsing, 404-as-no-match search helper, shared samples
lib/hookTrigger.js helpers REST Hook factory (/webhooks/subscribe, /webhooks/unsubscribe)
creates/timestamp_and_wait.js action POST /timestamp/wait (primary)
creates/create_timestamp.js action POST /timestamp → 202 { jobId }
creates/batch_timestamp.js action POST /timestamp/batch (paid plans)
creates/verify_timestamp.js action POST /verify
creates/batch_verify.js action POST /verify/batch
creates/batch_status.js action POST /status/batch
creates/get_receipt.js action GET /receipts/{jobId} (JSON; optional PDF via z.stashFile)
searches/job_status.js search GET /status/{jobId} (404 → no match)
searches/job_verification.js search GET /status/{jobId}/verification (404 → no match)
searches/hash_lookup.js search GET /hashes/{sha256} (404 → no match)
searches/tree_lookup.js search GET /hashes/{sha256}?includeAttached=true&includeTree=true
searches/usage.js search GET /auth/me → plan + month-to-date usage vs limits (Find Usage)
triggers/timestamp_completed.js REST Hook event timestamp.completed
triggers/timestamp_failed.js REST Hook event timestamp.failed

Hook payload delivered by the middleware: { event, jobId, tenantId, status }; the trigger adds id = jobId so Zapier can dedupe.

Create Timestamp / Create Timestamp and Wait take Data or SHA256 (64 hex; hash large files outside Zapier and send only the digest, per the user guide), plus optional public / private metadata dictionaries. A digest that was sealed before returns the original job with existing: true and no second chain write.

I/O contracts: https://zapier.georgelambert.org/docs/developer/modules/function-reference.pdf

Known gaps upstream (middleware / chain), not in this app

  • GET /hashes/{sha256} returns 501 NOT_IMPLEMENTED when the middleware runs against the live chain (MOCK_VERAE=false); the Verae OpenAPI has no hash lookup. The two hash searches surface that message verbatim.
  • publicMetadata / privateMetadata and sha256-only creates are accepted by the middleware but only honoured by its mock chain, so this app does not expose them.
  • The middleware has no "list recent events" endpoint, so performList on the hook triggers returns nothing and Zapier uses the static sample.

Env

Variable Default Purpose
MIDDLEWARE_BASE_URL http://127.0.0.1:3100 Origin the app calls. Set per Zapier version: zapier env:set 1.1.0 MIDDLEWARE_BASE_URL=https://mw.zapier.georgelambert.org
MIDDLEWARE_PATH_PREFIX /zapier/v1 Route prefix at that origin. Leave the default for the middleware or the access planes; change it only if a fronting edge mounts the same routes elsewhere.

Where to point it (lab, 2026-09-14)

The catalog at https://zapier.georgelambert.org/ documents the live lab on NS1:

Origin What it is Usable by this app today?
https://mw.zapier.georgelambert.org verae-middleware (MOCK_VERAE=true, NATS on) Yes — full /zapier/v1/*; accepts Bearer or x-api-key. Use this until the Zap plane proxies every route.
https://zap.zapier.georgelambert.org verae-access-zapier, the intended Zapier door (STATUS.md / dev-overview) Not yet — it only proxies POST /v1/timestamp and does not forward the API key, so auth/me, wait, verify, status, hooks all 404/401.
https://api.zapier.georgelambert.org verae-access-api (direct customer API plane) Forwards x-api-key for POST /v1/timestamp only; not for Zapier.
zappier-edge /v1/* billing edge (composition target; loopback :13000, tunnel only) Not yet — covers timestamp/hashes/receipts/usage only, no /zapier/v1 prefix.

Keys: connect with a middleware zmw_… key (portal / POST /zapier/v1/signup / admin provisioning). Never a Verae username, password, or JWT. After the zappier composition lands, the same field takes the portal x-api-key; the app already sends that header.

Develop

npm install
npm test                                   # unit tests, no network
npx zapier-platform-cli validate --without-style

End-to-end against a running middleware (mock chain shown; a live-chain middleware works the same). Against the lab middleware add E2E_SKIP_HOOK_DELIVERY=1 (it cannot POST back to your laptop):

# in verae-middleware
PORT=3100 MOCK_VERAE=true NATS_ENABLED=false ADMIN_SECRET=dev-admin npm start
curl -s -X POST http://127.0.0.1:3100/zapier/v1/admin/tenants \
  -H 'x-admin-secret: dev-admin' -H 'content-type: application/json' \
  -d '{"name":"E2E","plan":"pro","veraeUsername":"e2e","veraePassword":"x"}'   # → apiKey

# in verae-zapier-app
MIDDLEWARE_BASE_URL=http://127.0.0.1:3100 MIDDLEWARE_API_KEY=zmw_... npm run test:e2e

# against the live lab (key from dev-overview/05-developer-credentials or /zapier/v1/signup)
MIDDLEWARE_BASE_URL=https://mw.zapier.georgelambert.org MIDDLEWARE_API_KEY=zmw_... \
  E2E_SKIP_HOOK_DELIVERY=1 npm run test:e2e

Gate

npm run gate:11   # from monorepo root, after Phase 11 implementation

Status of the API integration (2026-09-15)

Where the Zapier ↔ Verae path stands, and what is still open. Sources: this repo, verae-middleware, and the catalog (zapier-decisions/STATUS, dev-overview/03…05).

Done

  • This app covers the whole middleware /zapier/v1 surface that Zapier needs: async / wait / batch create, single and batch verify, job status / verification / batch status, hash and tree-node lookup, usage, retrieval receipt, and REST Hooks for timestamp.completed and timestamp.failed.
  • The middleware already has the live chain client (src/clients/veraeClient.js) for POST /api/timestamp, /api/batch/timestamp, /api/verify, /api/batch/verify, GET /api/status/{jobId}, GET /api/verify/{jobId}, POST /api/batch/status, and POST /auth/login on https://api.veraetime.net. No Zapier-app change is needed to go live.
  • Verified end to end through zapier-platform-core against a mock-chain middleware (npm run test:e2e), on both a pro tenant (batch, hook delivery) and a free tenant (402/403 upgrade-URL mapping).

Open — blocked on people, not code

Item Owner What is needed
Zapier Platform push operator zapier login, zapier register (or link), zapier env:set <version> MIDDLEWARE_BASE_URL=…, zapier push as a private app, then test Create Timestamp and Wait, Create Timestamp, Find Job Status, Timestamp Completed.
Live chain (MOCK_VERAE=false) Verae / operator A Verae username + password for the middleware (VERAE_API_BASE_URL=https://api.veraetime.net). Then prove one real seal, confirm 202 { jobId } → status → verify, and map the live status JSON into the receipt shape this app samples (certificate, timestamp, blockIndex, sha256).
Live-lab e2e run anyone with the lab key MIDDLEWARE_BASE_URL=https://mw.zapier.georgelambert.org MIDDLEWARE_API_KEY=zmw_… E2E_SKIP_HOOK_DELIVERY=1 npm run test:e2e. Exercises the NATS-enabled wait / poller path that the local run does not.

Open — code in other repos

Item Repo Notes
Zapier plane does not proxy the app verae-access-zapier zap.zapier.georgelambert.org handles only POST /v1/timestamp and drops the API key. STATUS says to point the app there; until it proxies all /zapier/v1/* with the key intact, use mw.zapier.georgelambert.org.
Hash lookup on live chain Verae API or middleware store GET /hashes/{sha256} is mock-only; live returns 501 NOT_IMPLEMENTED. Needs a Verae API gap fill or a middleware-side index.
sha256-only create and metadata on live chain verae-middleware Accepted by the middleware and honoured by the mock; the live client sends only data + hashAlg (adapter rule). Decide how the live path handles digest-only registration and public/private metadata.
Tree-leaf receipts on wait/status verae-middleware / verae-archive-aggregator The user guide describes receipts[], itemizedOnMainChain, merkleRoot, proofOk; the live wait path does not call the aggregator yet, so those fields are absent. This app's tree_lookup sample already names them.
Billing-edge composition zappier-edge Target is Zapier → zappier /v1/* (x-api-key, metered) → middleware. The edge is loopback-only today and covers only timestamp / hashes / receipts / usage without the /zapier/v1 prefix. This app already sends x-api-key and has MIDDLEWARE_PATH_PREFIX for when it lands.
Webhook performList verae-middleware No "recent events" endpoint, so the hook triggers return [] from performList and Zapier uses the static sample. A small list route would give real sample data in the editor.

Not planned in this app

  • Verae admin HTML, user CRUD, and get-block-by-hash (/admin/*, /auth/users*).
  • Encrypted object storage and shares (/objects*, /shares/*) — mock-only today; add creates here once the middleware contract is final.
  • Anything that talks to NATS or api.veraetime.net directly.

Proposed changes to the Verae chain API

Context: a developer asked how to query an original hash, get a receipt back from both the original submission and later lookups, see every timestamp field and its metadata, and store metadata (or links to it) in a blockchain timestamp. Against the live OpenAPI (docs/00-sources/veraetime-openapi.yaml) none of that is possible today: lookups are by job ID only, POST /api/timestamp returns just { jobId }, and the request has no metadata field. The middleware mocks all of it (MOCK_VERAE=true) but its live client may only call paths in the snapshot spec.

Required on api.veraetime.net

Ask Change to the Verae API Why the chain, not the middleware
Query by original hash New GET /api/hashes/{sha256} → { exists, jobId, sha256, timestamp, blockIndex, certificate }. The chain already records dataHash per transaction; it needs an index on it. Only the chain can find hashes sealed by any submitter. A middleware index only knows what passed through it.
Receipt on original submission Make POST /api/timestamp idempotent on the digest: if the hash already exists return 200 { jobId, existing: true, certificate, timestamp, blockIndex } instead of 202. Accept a pre-hashed input { sha256 } alongside { data } so clients never ship the document. Idempotency has to live where the seal lives. Accepting a digest also stops raw data landing in blocks.
Receipt on lookup Have the hash lookup and GET /api/verify/{jobId} return the full StatusResponse (certificate + block metadata). The shape exists; the lookup just needs to reuse it. No new data, a consistent response.
Metadata in the timestamp Optional metadata object on TimestampRequest with a documented size cap, stored with the transaction and returned on status, verify, and hash lookup. Add metadataHash as an alternative for callers who want to commit to metadata without publishing it. Anything the seal must prove existed at that time has to be inside what the chain hashes. Everything else stays off chain.
  • Certified retrieval receipt — GET /api/receipts/{jobId}?format=json|pdf, where the chain seals a document.retrieved event referencing the original job and returns both. The middleware mocks this; a chain-signed receipt is what makes it certified.
  • JSON error bodies everywhere — the spec promises { error, code } but the live service returns plain text on 401 (Unauthorized: token is missing). Every client special-cases it.
  • Completion callback or job list — an optional callbackUrl on create, or GET /api/jobs?since=…. Today every consumer polls GET /api/status/{jobId} per job.

Rules to write into the spec

  • Public metadata on chain is immutable and readable by anyone. Private metadata never goes on chain; only a hash commitment does.
  • Define data as "bytes to hash" and state whether blocks retain it. The /admin/* examples show blocks carrying a base64 data field that decodes to the sample input, which suggests they do — that matters for privacy and should be confirmed on a live seal.

What can be done without touching the API

The middleware can index SHA-256 → job ID at submission, store public/private metadata, and merge both back into /status/{jobId} and /hashes/{sha256} on the live path (it already does so in mock). That covers the developer's flow for anything submitted through Verae's own middleware. It cannot deliver chain-wide lookup, chain-level idempotency, or a chain-signed receipt — those are the real API asks in the table above.

This Zapier app already sends sha256, publicMetadata, and privateMetadata to the middleware and exposes Find Timestamp by SHA256, Find Job Verification Details, and Get Retrieval Receipt, so no app change is needed once either side lands.