Skip to content

Logging In

Iron uses a single shared identity per person across every app, backed by Supabase Auth at your-domain.example. This is the one sanctioned cross-app dependency: one login, everywhere. CRM access is still gated per-tenant by membership (see below).

Log in to the dashboard at your-domain.example. Your Iron identity is your Supabase user; once authenticated, the app resolves which subaccount(s) you can act in from your crm.org_members rows. Clients see a curated portal into their own subaccount.

Every authenticated /api/v1 request carries a Supabase JWT as a bearer token:

Authorization: Bearer <supabase-jwt>

The token is validated as HS256 with audience authenticated. An invalid or expired token returns 401 Unauthorized with a WWW-Authenticate: Bearer header.

Once the user is resolved, the request also resolves an acting org. Most endpoints expect an X-Org-Id header to select the subaccount you are acting in:

Authorization: Bearer <supabase-jwt>
X-Org-Id: <org-uuid>

If X-Org-Id is omitted, the API falls back to your earliest-joined org. See Subaccounts & X-Org-Id and Authentication for the full rules.

Some internal seams are called by trusted services (for example, the telephony engine booking on the native scheduler, or inbound webhooks) rather than a logged-in user. Those endpoints authenticate with a shared secret header (X-Iron-CRM-Secret) instead of a user JWT, and pass the org explicitly in the request body. These are not part of the public user-facing API; see Webhooks.

On the first authenticated request for a brand-new user, the API auto-provisions a fresh org, an owner membership, a CRM tenant, and a default pipeline. This bootstrap runs under a system context (it cannot be org-scoped yet, because the org does not exist), then the rest of the request runs org-scoped. You do not need to do anything to trigger this — it happens on first authenticated access.