zappier-edge/README.md

134 lines
5.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Zappier
Metered API platform: per-endpoint pricing, customer types with multipliers and
monthly credits, a usage ledger, Stripe metered billing, purchase-order
invoicing, a company admin console, a self-service customer portal, and a
Zapier integration.
## Documentation
- **[docs/USER-MANUAL.md](docs/USER-MANUAL.md)** — operations & usage manual
- **[docs/ACCOUNTING.md](docs/ACCOUNTING.md)** — company accounting: invoices, PO billing, reports, CSV
- **[docs/USER-MANAGEMENT.md](docs/USER-MANAGEMENT.md)** — pricing, customer types, customer accounts
- **[docs/CUSTOMER-PORTAL.md](docs/CUSTOMER-PORTAL.md)** — end-user portal: signup, 2FA, reloads, invoices
- **[docs/DEVELOPER.md](docs/DEVELOPER.md)** — full developer documentation
- **[docs/WALKTHROUGH.md](docs/WALKTHROUGH.md)** — original step-by-step pricing walkthrough
## Surfaces
| Surface | URL | Audience |
|---|---|---|
| Public API | `/v1/*` | API customers (`x-api-key`) |
| Interactive API docs | `/docs` | Integrating developers |
| Admin console | `/admin` | Company ops & accounting |
| Customer portal | `/portal` | End-user customers (signup, 2FA, billing) |
| Zapier app | `zapier-app/` | No-code users via Zapier |
## Pricing model
`openapi.yaml` defines the API surface; each `operationId` is a rate-card key.
Endpoints carry **list prices** (seed: `src/pricing.ts``DEFAULT_RATE_CARD`).
Customer types are **tier configs** (`DEFAULT_TIERS`) with a `multiplier`, a
`monthlyCreditCents` quota, and an optional `defaultRule` for endpoints not on the card.
Individual customers can carry a `multiplierOverride`.
Billed price = `round(list price × multiplier)`; usage up to the monthly credit is free.
Pricing is editable at runtime in the admin console.
### Seed rate card (list prices, cents per call)
| Endpoint | Model | List price |
| -------------- | -------- | -------------------------------------------- |
| `status` | free | 0 |
| `storage-list` | free | 0 |
| `transform` | fixed | 4 |
| `storage` | variable | 10 + 1 per KB metadata + 50 per MB attached |
### Seed customer types
| Tier | Multiplier | Monthly credit | Default rule (unlisted endpoints) |
| ---------- | ---------- | -------------- | --------------------------------- |
| `free` | 1.0 | 100 cents | none — call rejected with 403 |
| `pro` | 0.5 | 1000 cents | fixed 8 list → 4 billed |
| `business` | 0.25 | 10000 cents | fixed 8 list → 2 billed |
Adding a new API call = add it to `openapi.yaml`, then price it in the admin UI.
Adding a customer type = create it in the admin UI. Variable pricing = base per call +
metadata size (rounded up to KB) + attachment size (rounded up to MB), then the multiplier.
## Quickstart
```sh
npm install
npm run dev
```
The server starts on port 3000. API docs at
[http://localhost:3000/docs](http://localhost:3000/docs), admin console at
`/admin`, customer portal at `/portal`.
## Deploying
```sh
npm ci && npm run build
node dist/index.js # runs from ANY working directory
```
All runtime paths (SQLite default, `.env`, OpenAPI spec, static assets)
resolve from the installation root, so the compiled server works under
systemd, Docker, or cron regardless of cwd. `PORT` and `ZAPPIER_DB` remain
environment-overridable.
## Environment variables
| Variable | Default | Purpose |
| ------------------- | -------------- | --------------------------------------------------- |
| `PORT` | `3000` | HTTP port the server listens on |
| `ZAPPIER_DB` | `<root>/zappier.db` | SQLite database file path |
| `ADMIN_KEY` | `admin-dev-key`| Admin UI / admin API key — **set a real secret in production** |
| `ADMIN_USER` | `admin` | Admin UI primary login username |
| `DEMO_ADMIN_USER` | `demo` | Admin UI demo login username |
| `DEMO_ADMIN_PASSWORD` | `$$$Adm1n###` | Demo login password — **override in production** |
| `STRIPE_SECRET_KEY` | _(none)_ | Stripe secret key — billing job and portal reloads |
## Billing
Usage is reported to Stripe by a job (loads `STRIPE_SECRET_KEY` from `.env`):
```sh
npx ts-node src/jobs/report-usage.ts
```
The job sums each customer's usage since the first of the current month (UTC),
applies the tier's monthly credit, and reports only the **delta** above what was
already reported — re-runs are safe. Idempotency comes from three layers: a
`billing_reports` ledger (cumulative cents per customer per month), an atomic
`job_locks` run guard (1 h TTL), and a deterministic Stripe event `identifier`
(`customer:period:billable`) that dedupes crash retries. It requires a Stripe
meter named `zappier.api_cents` with Sum aggregation over the `value` field.
A Kimi cron job ("Zappier billing · report usage to Stripe") runs it daily at
06:17 America/New_York with a completion notification.
Purchase-order customers are invoiced manually from the admin console
(**Invoices** tab); prepaid balances from the customer portal are drawn down
automatically at invoice issue. See `docs/ACCOUNTING.md`.
## Zapier app
The companion Zapier integration lives in `zapier-app/`:
```sh
cd zapier-app
npm install
npm test
```
To deploy it, create a Zapier developer account, run `zapier login`, then
`zapier push` from the `zapier-app/` directory.
## Testing
```sh
npm test # root API/service suite (jest, 165 tests)
cd zapier-app && npm test # Zapier integration suite (mocha, 4 tests)
```