A REST API for everything Temora knows about your buildings: residents, charges, maintenance, meetings, votes, inspections, status certificates and more. Multi-tenant, JWT-secured, JSON in/out.
Across 40+ route groups: buildings, units, residents, billing, governance, inspections, packages, ARC.
15-minute access tokens, 7-day refresh tokens with rotation. Resident, manager and super-admin roles.
Stripe, WhatsApp (Meta), and Resend webhooks — signature verification documented below.
Tenant scope is automatic from your JWT. You cannot read another tenant's rows even by guessing IDs.
Login, fetch yourself, list your buildings.
# 1. Login (gets you an access + refresh token)
curl -X POST https://temorasolutions.com/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"demo@temorasolutions.com","password":"DemoPassword123!","tenant_slug":"demo"}'
# Response: { "ok": true, "data": { "user": {...}, "tokens": { "access_token": "eyJ...", ... } } }
ACCESS=eyJhbGciOi...
# 2. Who am I?
curl https://temorasolutions.com/api/v1/me \
-H "Authorization: Bearer $ACCESS"
# 3. List buildings in my tenant
curl https://temorasolutions.com/api/v1/buildings \
-H "Authorization: Bearer $ACCESS"
# 4. Refresh when access_token expires (15 min)
curl -X POST https://temorasolutions.com/api/v1/auth/refresh \
-H 'Content-Type: application/json' \
-d '{"refresh_token":""}'
Every authed request carries Authorization: Bearer <jwt>. There are three login flows that mint
tokens with different role claims:
| Endpoint | Who | What you get |
|---|---|---|
POST /auth/login | Manager / owner / staff | Tenant-scoped access + refresh |
POST /auth/resident/login | Resident portal | Resident-role access + refresh; HTTP 300 if email exists in multiple tenants (chooser required) |
POST /auth/super-admin/login | Temora team only | Super-admin token (separate JWT scheme) |
Access tokens expire in 15 minutes. Refresh tokens last 7 days and are rotated on every refresh — the old one is revoked.
Logout via POST /auth/logout with your refresh token to revoke it server-side.
All limits are enforced by the API and return 429 RATE_LIMITED with a JSON envelope. Rate-limit headers
follow the IETF RateLimit-* standard.
| Bucket | Limit | Window | Key |
|---|---|---|---|
| Login (manager) | 10 | 15 min | IP |
| Login (resident, super-admin) | 10 | 15 min | IP |
| Tenant register | 5 | 1 hour | IP |
| Authenticated writes (POST/PATCH/PUT/DELETE) | 200 | 1 minute | User |
| Public reads (healthz, readyz, metrics) | 30 | 1 minute | IP |
Webhooks are public endpoints that bypass auth/rate-limit but verify a provider signature. They are mounted before JSON body parsing so signatures stay valid.
| Endpoint | Provider | Header | Verify with |
|---|---|---|---|
POST /api/v1/billing/webhooks/stripe | Stripe | Stripe-Signature | STRIPE_WEBHOOK_SECRET via stripe.webhooks.constructEvent |
GET /api/v1/whatsapp/webhooks/meta | Meta WhatsApp (verify) | — | Returns hub.challenge when hub.verify_token matches tenant secret |
POST /api/v1/whatsapp/webhooks/meta | Meta WhatsApp (events) | X-Hub-Signature-256 | HMAC-SHA256 of raw body with app secret |
POST /api/v1/billing/webhooks/resend | Resend (email) | svix-signature | Resend signing secret |
Always reject the request if signature verification fails. The raw body is available because webhook routes are mounted before express.json().
Full Swagger UI rendering of openapi.yaml:
Importable Postman v2.1 collection mirroring the OpenAPI spec, with login auto-store of access_token
in your Postman environment.
# Or import directly via Postman CLI
curl -o temora.postman_collection.json https://temorasolutions.com/api/v1/postman
# In Postman: File → Import → pick the file
Official SDKs are in design — JavaScript / TypeScript first, then Python and Go. In the meantime the Postman
collection plus the OpenAPI spec are sufficient to generate clients with openapi-generator-cli.