Compose-ready workspace: packages/zappier (rate card, portal, Stripe), packages/verae-zapier-middleware (timestamp + NATS), packages/verae-zapier (CLI app), vendor/zapier-platform, and research/zapier vendor corpus. Gate 0 structure checks pass. Product code and research are not yet wired.
15 KiB
Zappier — Operations & Usage Manual
Version: 0.2.0 · Last updated: 2026-07-28
Zappier is a metered API platform: every API call your customers make is priced
per endpoint, adjusted by their customer type, tracked in a usage ledger, and
billed through Stripe once a day — or invoiced manually by purchase order.
Customers self-serve through the portal at /portal. This manual covers
running and operating the system. For internals, see
DEVELOPER.md; for accounting procedures see
ACCOUNTING.md; for the end-user view see
CUSTOMER-PORTAL.md.
Table of contents
- Quick start
- The three surfaces
- How pricing works
- Operating the Pricing Admin UI
- Using the public API
- Billing operations (Stripe)
- The Zapier integration
- Day-to-day runbook
- Troubleshooting
1. Quick start
cd /Users/marchon/zappier
npm install
npm run dev # starts the API on http://localhost:3000
Environment variables (all optional except STRIPE_SECRET_KEY for billing):
| Variable | Default | Purpose |
|---|---|---|
PORT |
3000 |
HTTP port for the API server |
ZAPPIER_DB |
<install root>/zappier.db |
SQLite database file location |
ADMIN_KEY |
admin-dev-key |
Key for the admin console and admin API |
ADMIN_USER |
admin |
Admin console primary username |
DEMO_ADMIN_USER / DEMO_ADMIN_PASSWORD |
demo / $$$Adm1n### |
Demo sign-in — override in production |
STRIPE_SECRET_KEY |
— (required for billing) | Billing job + portal reloads, loaded from .env |
All runtime paths (database default, .env, OpenAPI spec, static assets)
resolve from the installation root — the compiled server (node dist/index.js) runs from any working directory, under systemd, Docker, or cron.
The .env file at the repo root holds STRIPE_SECRET_KEY. It is gitignored
and owner-only (chmod 600). A template is in .env.example.
On first start the database is created and seeded with:
- Rate card:
status(free),storage-list(free),transform(fixed 4¢),storage(variable: 10¢ base + 1¢/KB metadata + 50¢/MB attachments) - Customer types: Free (×1.0, 100¢/month credit), Pro (×0.5, 1000¢ credit), Business (×0.25, 10000¢ credit)
- Demo customers:
key-ada(Free),key-grace(Pro),key-linus(Business)
Seeding only happens into an empty database. Existing data is never overwritten on restart.
2. The three surfaces
| Surface | URL / location | Who it's for |
|---|---|---|
| Public API | http://localhost:3000/v1/* |
Your API customers |
| Interactive API docs | http://localhost:3000/docs |
Developers integrating with you |
| Admin console | http://localhost:3000/admin |
You (operations & accounting) |
| Customer portal | http://localhost:3000/portal |
End-user customers (self-service) |
| Zapier app | zapier-app/ directory |
No-code users via Zapier |
3. How pricing works
Every priced call returns its quote in the response, so customers always know what a call cost:
{
"quote": {
"endpointId": "storage",
"listCents": 62,
"totalCents": 31,
"breakdown": { "baseCents": 10, "metadataCents": 2, "attachmentCents": 50 }
}
}
The price of a call is computed in three steps:
- Endpoint rule (from the rate card):
free— always 0¢fixed— a flatfixedCentsper callvariable—baseCents+perKbCents× ceil(metadata bytes / 1024)perMbCents× ceil(attachment bytes / 1 MB)
- Customer-type multiplier —
totalCents = round(listCents × multiplier). A per-customer multiplier override (set in the Customers tab) wins over the type multiplier — use it for negotiated enterprise deals. - Monthly credit — at billing time, each customer's type credit (e.g. Pro = 1000¢) is subtracted from their month-to-date total. Only the excess is billed.
Worked example. A Pro customer (×0.5) uploads a 1 MB file with 2 KB of
metadata to storage:
- list = 10¢ base + 2¢ metadata + 50¢ attachment = 62¢
- Pro multiplier: 62 × 0.5 = 31¢ charged to their usage ledger
- If their month-to-date is 1500¢ and the Pro credit is 1000¢, the daily billing job reports 500¢ to Stripe.
4. Operating the Pricing Admin UI
Open http://localhost:3000/admin and sign in. Two accounts are available:
| Username | Password | Purpose |
|---|---|---|
admin |
the ADMIN_KEY env value (default admin-dev-key) |
Primary operator |
demo |
$$$Adm1n### (env DEMO_ADMIN_PASSWORD) |
Demo / stakeholder access |
Sessions are token-based and remembered in browser local storage until you
click Sign out or the server restarts (tokens are in-memory — just sign in
again). The legacy x-admin-key header still works for scripts and curl.
4.1 Rate card tab
One row per API endpoint (matched by OpenAPI operationId).
- Change a price: edit the kind (
free/fixed/variable) and the cent fields, then click Save. Takes effect on the next API call — no restart needed. - Add an endpoint: enter the
operationId(must matchopenapi.yaml), pick a kind, click Add. - Delete removes the rule. If an endpoint has no rule and the customer's type has no default rule, calls to it are rejected with 403 — deletion is how you turn an endpoint off.
4.2 Customer types tab
Types are your pricing tiers.
- Multiplier scales every price for that type (0.5 = 50% of list).
- Monthly credit (cents) is the free included usage per month.
- Add customer type: id, name, multiplier. New types start with 0 credit; edit after adding.
4.3 Customers tab
- Create a customer: name + type. The API key is shown once in the status line at the bottom — copy it immediately and send it to the customer.
- Change type with the dropdown, then Save.
- Email — used for portal sign-in/claiming and email invoicing.
- Billing —
Stripe(metered by the daily job) orPurchase order(manual invoicing with PO numbers; see ACCOUNTING.md). - Multiplier override: a number here replaces the type multiplier for this customer only. Leave blank to inherit from the type.
- Stripe customer IDs are attached via the admin API
(
PUT /admin/api/customers/:idwith{"stripeCustomerId": "cus_..."}) — see section 6.
4.4 Invoices, Reports, System tabs
The Invoices tab generates monthly invoices from metered usage (per period, optionally per customer, with optional PO number), walks them draft → issued → paid, and opens print-ready invoice pages. The Reports tab produces date-ranged billing reports (all customers, one customer, or one billing type) with CSV download, plus daily/weekly usage-trend charts. The System tab shows Zapier integration health and the current-period billing snapshot. Full procedures: ACCOUNTING.md.
4A. Customer portal
Your customers self-serve at http://localhost:3000/portal: signup (or
claiming an account you created, by email), sign-in with optional TOTP
two-factor authentication, month-to-date usage, invoice history with print
view, prepaid balance reloads (drawn down automatically at invoice issue),
email-invoicing preferences, API-key regeneration, and live pricing. The full
end-user guide is CUSTOMER-PORTAL.md.
5. Using the public API
All calls need the customer's API key in the x-api-key header. Interactive
docs with a "Try it out" console are at /docs.
# Free status check
curl -H 'x-api-key: key-ada' http://localhost:3000/v1/status
# Fixed-price call (4¢ list)
curl -X POST -H 'x-api-key: key-grace' -H 'content-type: application/json' \
-d '{"text":"hello"}' http://localhost:3000/v1/transform
# Variable-price call: metadata + attachments
curl -X POST -H 'x-api-key: key-grace' \
-F 'metadata={"title":"Q3 report"}' \
-F 'attachments=@report.pdf' \
http://localhost:3000/v1/storage
# List your stored items (free)
curl -H 'x-api-key: key-grace' http://localhost:3000/v1/storage
# Your month-to-date usage, with credit applied
curl -H 'x-api-key: key-grace' http://localhost:3000/v1/usage
Limits & validation: requests are validated against openapi.yaml (bad
requests get 400); attachments are capped at 25 MB per file; metadata
must be valid JSON (400 otherwise).
6. Billing operations (Stripe)
6.1 How it works
A scheduled job runs the billing reporter daily at 06:17 America/New_York (Kimi cron job "Zappier billing · report usage to Stripe"). For each customer with a Stripe ID it:
- Sums their usage since the 1st of the month, subtracts their type's monthly credit → billable cents.
- Reports only the delta above what was already reported this month to
Stripe as a meter event (
zappier.api_cents, value = cents). - Records the new cumulative total in the
billing_reportsledger.
Re-running is always safe: the ledger makes repeats no-ops, a per-run lock
prevents overlapping executions, and a deterministic Stripe identifier
(customer:period:billable) dedupes crash retries.
6.2 One-time Stripe setup (test mode)
-
Dashboard (test mode ON) → Billing → Meters → Create meter: event name
zappier.api_cents, aggregation Sum ofvalue, customer mappingstripe_customer_id. -
Product catalog → Add product "Zappier API usage" → price: recurring, monthly, metered against that meter, $0.01 per unit (1 unit = 1 cent).
-
For each billable customer: create the Stripe Customer, attach a payment method, and add a subscription with the metered price. Meter events for customers without a metered subscription are recorded but never invoiced.
-
Put the
sk_test_...key into.env(replace the placeholder). -
Attach Stripe IDs to Zappier customers:
curl -X PUT -H 'x-admin-key: admin-dev-key' -H 'content-type: application/json' \ -d '{"stripeCustomerId":"cus_..."}' \ http://localhost:3000/admin/api/customers/cust_2
6.3 Verifying a run
npx ts-node src/jobs/report-usage.ts
Expected output per customer:
skip <id> <period> (nothing to report)— no billable usage yetskip <id> <period> (already reported Nc)— no new usage since last run<id>: reported N billable cents to Stripe— delta sentreport-usage: another run holds the lock, abort run— safe concurrent abort
Then check the meter's Events tab in the Stripe dashboard and the test customer's upcoming invoice.
6.4 Going live
Repeat 6.2 steps 1–3 in live mode, replace .env with the sk_live_... key
from the same Stripe account, and keep the same cron. The live product
prod_Uxv9SAeIOZzyx1 (currently deactivated) can be reactivated or recreated.
7. The Zapier integration
The zapier-app/ directory contains the Zapier Platform app:
- Authentication: API key — the user pastes their API base URL
(e.g.
http://localhost:3000) and theirkey-...customer key; the connection is tested against/v1/status. - Trigger "New Item": polls
GET /v1/storagefor newly stored items. - Action "Store Data": calls
POST /v1/storagewith metadata and optional file attachments — billed per the rate card.
To publish: create a Zapier developer account, cd zapier-app && npm install && zapier login && zapier push, then share the app or submit it to the Zapier
marketplace.
8. Day-to-day runbook
| Task | How |
|---|---|
| Change a price | Admin console → Rate card → Save |
| Add a customer | Admin console → Customers → Create → copy the one-time API key |
| Set a customer's billing type/email | Customers tab → Billing dropdown / Email field → Save |
| Invoice a period | Invoices tab → Generate → Issue → Mark paid (ACCOUNTING.md) |
| Export accounting data | Reports tab → filters → Download CSV |
| Give a customer a deal | Customers tab → multiplier override, or a new customer type |
| Turn an endpoint off | Rate card → Delete (calls get 403) |
| Check a customer's usage | Their portal dashboard, GET /v1/usage with their key, or query zappier.db |
| Check billing ran | Kimi notification after each 06:17 run; or run the job manually |
| Backup | Copy zappier.db (SQLite, single file) |
| Update dependencies | npm outdated, then npm test must stay green (165 tests) |
9. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
401 invalid or missing API key |
Wrong/absent x-api-key |
Re-issue key from Customers tab |
403 No price rule for ... |
Endpoint deleted from rate card and tier has no default rule | Re-add the rule, or set a tier defaultRule |
403 Unknown tier |
Customer's tierId doesn't exist |
Fix the customer's type in the admin UI |
400 invalid metadata JSON |
metadata form field isn't valid JSON |
Send e.g. {"key":"value"} |
413 ... file too large |
Attachment over 25 MB | Split or compress the file |
| Billing run: Stripe auth error | Placeholder/wrong key in .env, or key from a different Stripe account |
Use the sk_test/sk_live key from the account that holds the meter |
Stripe shows METER_NOT_FOUND invalid events |
Meter missing or event name mismatch | Meter event name must be exactly zappier.api_cents |
another run holds the lock, abort run |
Overlapping runs, or a crash left a stale lock | Safe by design; stale locks expire after 1 hour |
| Admin UI: "invalid username or password" | Wrong credentials, or env overrides changed them | Check ADMIN_KEY / DEMO_ADMIN_PASSWORD; sign in again |
| Admin UI: "Session expired" | Server restarted (admin tokens are in-memory) | Sign in again |
Portal login: totp_required |
2FA is enabled on the account | Enter the current 6-digit authenticator code |
| Portal: "invalid or expired session" | 7-day session expired | Sign in again |
| Portal reload didn't credit | Real STRIPE_SECRET_KEY configured → PaymentIntent awaits confirmation |
Balance credits when the payment confirms; in dev (placeholder key) credit is instant |
Invoice went straight to paid on Issue |
Customer's prepaid balance fully covered the amount due | Working as designed — balance was drawn down |




