API
Build on FirmWorks
The public API rolls out one module at a time. Per-org API keys are live; route consumption is in flight. This page lists what’s available today and what’s queued.
Get a key
Sign in to your workspace and open /app/settings/api-keys. Click Create key, give it a name and a scope (read, write, or admin), and copy the full secret immediately — we hash before storing, so we can’t show it again.
Keys can be revoked from the same page at any time. Revoked keys stop authenticating requests within seconds. Each key shows its last-used timestamp so you can spot stale ones.
Key format
fw_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789Authentication
Pass your key as a Bearer token on every request. The API derives your organization from the key — the key is scoped to one workspace, so there’s no per-request org parameter.
curl -H "Authorization: Bearer fw_<your_key>" \
https://firmworks.com/api/v1/contactsAll API responses are JSON. Errors come back as { "error": { "code": "...", "message": "..." } } with a meaningful HTTP status (400 / 401 / 403 / 404 / 429 / 500).
For the full scope semantics — read vs write vs admin, what the wrapper checks before the handler runs, why hashes are rotated on revoke — see the security overview.
Quickstart
Sixty seconds, three commands — create a key, hit /api/v1/me, confirm you’re wired up.
1. Create a key in the UI
Sign in, open
/app/settings/api-keys, click Create key, pick scoperead, copy the fullfw_…secret. You only see it once.2. Export the key locally
export FW_KEY="fw_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789"3. Hit
/api/v1/meto confirm authcurl -s -H "Authorization: Bearer $FW_KEY" \ https://firmworks.com/api/v1/me | jq .Expected response:
{ "data": { "apiKeyId": "akey_…", "orgId": "org_…", "orgName": "Your Workspace", "orgSlug": "your-workspace", "scopes": ["read"] } }Got a non-200? Status
401means the key is invalid or revoked;429means you’ve tripped the per-key rate limit (see below).
Available surface
Live items work today. Coming soonitems are committed for the current quarter — see the roadmap for status.
API keys
- Manage workspace keys at /app/settings/api-keys
- Plaintext shown once on creation; only the SHA-256 hash is stored
- Per-key scope (read · write · admin) and last-used timestamp
Identity
- GET /api/v1/me — returns { apiKeyId, orgId, orgName, orgSlug, scopes }
- Authenticated by withApiKeyAuth — verifies hash, checks revoked_at, updates last_used_at
- Doubles as a smoke test before hitting the real CRUD endpoints
Documents & Tax
- GET /api/v1/contacts — list, filter, paginate
- POST /api/v1/contacts — create with tax ID
- GET /api/v1/documents — invoices, quotations, receipts
- POST /api/v1/documents — issue a new document with VAT/WHT
Outbound webhooks
- Subscribe to invoice.created / project.completed / member.added
- HMAC-SHA256 signed POSTs (X-FirmWorks-Signature header)
- Retry with exponential backoff; manual retry from the UI
Rate limits
Default cap is 100 requests per minute per key across all endpoints. Burst slack: brief spikes above the cap are tolerated; sustained load triggers HTTP 429 with a Retry-After header. Need a higher limit for an integration? Email hello@firmworks.com.
Need help?
Email hello@firmworks.com with what you’re trying to build — we answer within one business day, Bangkok hours, and we’ll tell you honestly whether the endpoint you need is on the path.