- JavaScript 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
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> |
||
| creates | ||
| lib | ||
| searches | ||
| test | ||
| triggers | ||
| .gitignore | ||
| authentication.js | ||
| index.js | ||
| NATS.md | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| SUMMARY.md | ||
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/privateMetadataandsha256-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
performListon the hook triggers returns nothing and Zapier uses the staticsample.
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/v1surface 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 fortimestamp.completedandtimestamp.failed. - The middleware already has the live chain client (
src/clients/veraeClient.js) forPOST /api/timestamp,/api/batch/timestamp,/api/verify,/api/batch/verify,GET /api/status/{jobId},GET /api/verify/{jobId},POST /api/batch/status, andPOST /auth/loginon https://api.veraetime.net. No Zapier-app change is needed to go live. - Verified end to end through
zapier-platform-coreagainst 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. |
Strongly recommended
- Certified retrieval receipt —
GET /api/receipts/{jobId}?format=json|pdf, where the chain seals adocument.retrievedevent 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
callbackUrlon create, orGET /api/jobs?since=…. Today every consumer pollsGET /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
dataas "bytes to hash" and state whether blocks retain it. The/admin/*examples show blocks carrying a base64datafield 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.