Skip to content

Latest commit

 

History

History
105 lines (79 loc) · 4.25 KB

File metadata and controls

105 lines (79 loc) · 4.25 KB

ExpressForms API Reference

Base URL (dev): http://localhost:4000

Authentication

Two mechanisms:

1. Session (JWT + refresh cookie) — for the web app

  • POST /api/auth/login { email, password }{ user, accessToken } and sets an httpOnly refresh cookie.
  • POST /api/auth/refresh (cookie) → a fresh { user, accessToken }.
  • POST /api/auth/logout — revokes the refresh token.
  • Send the access token as Authorization: Bearer <accessToken> on authenticated routes.

2. API keys — for external/programmatic access (/api/v1)

  • Create keys in the app (API keys page) or POST /api/me/api-keys { name }.
  • The full key (efk_…) is shown once. Send it as X-API-Key: efk_… (or Authorization: Bearer efk_…).
  • A key can only read what its owning user can (same per-form permissions). Read-only.

Roles & permissions

Roles: ROOT, STAFF, USER. Per-form permission keys (granted to STAFF; ROOT and the form's creator have all implicitly):

form.view, form.edit, form.delete, response.view, response.export, response.delete, analytics.view, webhook.manage.

Access rule: a staff member with no access to a form gets 404; with access but missing the specific key, 403.

External API (/api/v1, read-only, API-key auth, 120 req/min per key)

Endpoint Requires Returns
GET /api/v1/forms — (owner's forms) accessible forms
GET /api/v1/forms/:id form.view form meta + latest published schema
GET /api/v1/forms/:id/responses response.view paginated responses
GET /api/v1/forms/:id/responses/:responseId response.view one response
GET /api/v1/forms/:id/analytics?range=7|30|90 analytics.view analytics payload

Example:

curl -H "X-API-Key: efk_…" http://localhost:4000/api/v1/forms

Public form endpoints (no auth)

  • GET /api/public/forms/:slug — a published form's title/description + schema.
  • POST /api/public/forms/:slug/submit { answers, completionTime? } — submit. Include a bearer token to attribute the submission to a logged-in user.
  • POST /api/public/forms/:slug/view — record a view (used by the form page beacon).

Webhooks

Configure per form (needs webhook.manage). On submission.created / submission.updated each active subscribed webhook receives a POST with headers:

  • X-ExpressForms-Event — the event name.
  • X-ExpressForms-Delivery — the delivery id.
  • X-ExpressForms-Signaturesha256=<hex>, an HMAC of the raw body keyed by the webhook secret.

Verify in Node:

import crypto from 'node:crypto';
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));

Payload: { event, form: { id, title, slug }, response: { id, answers, submittedAt, submittedBy }, occurredAt }. Failed deliveries retry with exponential backoff (up to 5 attempts).

Monitoring

  • GET /api/health — liveness (public).
  • GET /api/health/ready — readiness incl. DB check (503 if the DB is down).
  • GET /api/health/metrics — request counts, status classes, avg latency, uptime (ROOT).

Audit log

  • GET /api/audit-logs?action=&actor=&page=&limit= — ROOT only. Records auth, user, form, permission, response, webhook, and API-key actions.

Environment variables (backend/.env)

Var Required Notes
DATABASE_URL yes PostgreSQL connection string
JWT_ACCESS_SECRET yes ≥32 chars (openssl rand -hex 32)
PORT no default 4000
NODE_ENV no development | test | production
ACCESS_TOKEN_TTL no default 15m
REFRESH_TOKEN_TTL_DAYS no default 7
ROOT_EMAIL / ROOT_PASSWORD no seeded root account
TRUST_PROXY no false (dev) / hop count / IP list behind a proxy
SMTP_URL no when unset, emails are logged to EmailLog only
MAIL_FROM no From address for notification emails

Backups

npm run backup (in backend/) dumps the database to backups/expressforms-<timestamp>.sql via pg_dump (requires the PostgreSQL client tools on PATH). Restore into an empty database with psql "$DATABASE_URL" -f backups/<file>.sql.