Authentication
CondoPulse’s API is a set of JSON route handlers served by the web app
(http://localhost:3000 in development). There are four credentials, each
scoped to a job:
| Credential | Sent as | Used by | Scope |
|---|---|---|---|
| Session cookie | Cookie (set by sign-in) | The web app, humans | Acts as the signed-in user, with their role |
| Mobile session | Authorization: Bearer <session-token> | The mobile app | Acts as the signed-in user within their organization |
| Cron secret | Authorization: Bearer ${CRON_SECRET} | Schedulers | Only POST /api/cron/sweep and POST /api/cron/digest |
| Inbound email secret | Authorization: Bearer ${INBOUND_EMAIL_SECRET} | Your email provider’s webhook | Only POST /api/inbound-email |
Reads vs. writes
Section titled “Reads vs. writes”Public reads — building status, uptime, and tower names remain available at
GET /api/status, GET /api/assets/:id, and GET /api/towers.
Public asset responses contain no incident history.
Member reads — incident lists/details, unit lists, contacts, and incident
photos require a session belonging to the property’s organization. Missing
credentials return 401; foreign property lists return 403, and foreign
incident/photo identifiers return 404. Invoice attachments additionally require
a staff role. Send the bearer session on mobile image requests too.
Writes require either a session cookie or the mobile bearer session.
Staff-only writes (status changes, merges, admin CRUD, exports) additionally
require the authenticated identity to have role board, manager, or
admin - the docs and reference pages call this staff.
Session cookie (web)
Section titled “Session cookie (web)”Sign in at /login (email/password, or Google SSO when configured) and the
browser carries a session cookie on every request. Server-rendered pages and
fetch calls from the web app authenticate this way automatically.
For quick API poking with a session, sign in via the auth endpoint and reuse the cookie jar:
curl -c jar.txt -X POST http://localhost:3000/api/auth/sign-in/email \ -H "Content-Type: application/json" \ -d '{"email":"manager@demo.test","password":"demo1234"}'
curl -b jar.txt "http://localhost:3000/api/export/incidents.csv?status=resolved"Your role (resident / board / manager / admin) is attached to the session; endpoints enforce it server-side. See Roles & permissions for the capability matrix.
Mobile bearer session
Section titled “Mobile bearer session”Sign in with email/password at POST /api/auth/sign-in/email. The response’s set-auth-token header contains the bearer session. The native app stores it in Expo SecureStore and sends Authorization: Bearer <session-token> on subsequent calls. POST /api/auth/sign-out revokes it.
Reports, updates, and push subscriptions belong to the actual signed-in account. Protected data and writes require membership of the property’s organization; staff roles do not grant access across organizations. Null organization IDs never grant membership.
X-Property-Id can select a property for API requests. Without a selection, signed-in users default to their organization’s first property. Public status data remains public; contacts and invoices do not.
The legacy shared MOBILE_API_KEY is accepted only in a dedicated demo with DEMO_MODE=true. It is disabled by default and is not used by the mobile app’s sign-in flow.
Cron secret
Section titled “Cron secret”CRON_SECRET guards exactly two endpoints - the SLA escalation sweep and the
weekly digest:
curl -X POST -H "Authorization: Bearer ${CRON_SECRET}" \ http://localhost:3000/api/cron/sweep
curl -X POST -H "Authorization: Bearer ${CRON_SECRET}" \ http://localhost:3000/api/cron/digestActions these trigger are audited as the system actor. Scheduling
recipes (crontab and n8n) are in
SLA policy & escalation and
Weekly digest. Both jobs can also run without the
HTTP layer at all: npm run sweep / npm run digest connect to the
database directly.
Inbound email secret
Section titled “Inbound email secret”INBOUND_EMAIL_SECRET guards POST /api/inbound-email, the
provider-agnostic email-ingestion endpoint. Configure your email
provider/forwarder to POST parsed messages there with the bearer header; the
payload format and [INC-<id>] subject behavior are documented in
Admin & ops and
Triage queue & email-in.
Outgoing webhook signatures
Section titled “Outgoing webhook signatures”Strictly the reverse direction - CondoPulse authenticating itself to your systems. Every outgoing webhook delivery is signed with the hook’s secret:
x-condopulse-signature: sha256=<HMAC-SHA256(raw body, secret)>Verification snippet and details: Webhooks.
Failure modes
Section titled “Failure modes”- Missing/wrong bearer key or secret →
401. - Valid identity, insufficient role (e.g. resident calling a staff endpoint,
or staff requesting another organization’s private data) →
403. - The cron and inbound-email secrets are not interchangeable with each other
or with
MOBILE_API_KEY- each bearer value works only on its own endpoints.
Summary of env variables
Section titled “Summary of env variables”DEMO_MODE=false # shared demo-key compatibility disabledCRON_SECRET=dev-cron-secret # /api/cron/*INBOUND_EMAIL_SECRET=... # /api/inbound-emailBETTER_AUTH_SECRET=... # session signing - openssl rand -hex 32