Authentication & Tenancy
This page covers the two things every embed call depends on: how your customers map to Monad sub-orgs, and how your backend turns a long-lived API key into the short-lived session token the browser uses. Understand the tenancy model first — the sub-org id shows up in almost every API call.
Tenancy and sub-orgs
Vocabulary
| Term | What it means |
|---|---|
| Sub-org (sub-organization) | A Monad organization nested underneath your (the vendor's) root org, that owns a customer's connectors and pipelines. Identified by an organizationId. |
| Tenant | Your customer. Your backend maps a tenant to its Monad sub-org. |
| Session token | A short-lived (about 30 minutes) credential scoped to one sub-org, minted from your API key. The browser passes it into the iframe. |
The recommended model: one sub-org per account
Give each account on your side its own Monad sub-org. "Account" here means whatever your isolation boundary is — usually one of your customers. Several users inside the same customer still share that one customer's sub-org.
You hold a single Monad API key. Under it you create one sub-org per account. Your backend owns the tenant-to-sub-org mapping, which in the simplest case is a column on your accounts table. The browser only ever names a tenant; your backend resolves it to the Monad sub-org id and never sends that id to the client.
Resolve, don't trust
Don't accept an org id from the browser as authority. Resolve it server-side from the tenant. The same rule, applied to pipeline and connector ids, comes back as "resolve-then-act" in Connector Lifecycle.
Why one sub-org per account
The sub-org is Monad's isolation boundary between your accounts, and the sub-org id is load-bearing:
- The session token is scoped to one sub-org. The API rejects any call outside its
orgclaim, so a token minted for one account can't touch another's data. - Every catalog and pipeline call is org-scoped:
/v1/{org}/inputs,/v2/{org}/pipelines/, and so on. Listing connectors returns everything in that sub-org and nothing outside it.
One sub-org per account means each customer's connectors, pipelines, and secrets are
isolated by the API itself, and the short-lived token you hand the browser can only ever
reach that one customer's resources. Wherever you see org or organizationId in these
docs, read it as "the sub-org this tenant maps to."
The risk of sharing a sub-org across accounts
You can put multiple accounts on one sub-org, but that erases the boundary the model depends on:
- No isolation. A token minted for account A can read and act on account B's connectors and pipelines — to the API they're the same org.
- Listing leaks.
GET /v1/{org}/{kind}sreturns every connector in the sub-org, exposing one account's names and ids to another. - Mutations can hit the wrong account. Build, pause, and delete act on ids in the shared sub-org. Resolve-then-act helps, but can't restore a boundary a separate sub-org would give you.
- Wider blast radius. One leaked token or bad deploy affects every account on the sub-org, not one.
- Messy offboarding. Deleting an account means hand-picking its resources out of a shared sub-org instead of just deleting the sub-org.
Sharing is only fine when the accounts aren't separate security principals (say, environments of one internal app). Distinct customers get distinct sub-orgs.
Provisioning sub-orgs
Create a sub-org per account with POST /v3/{yourOrgId}/organizations (body
{ name, friendly_name }), and store the returned id against your tenant when you
onboard them. If you're embedding for a single account, this collapses to one sub-org you
resolve to server-side — no mapping table needed.
Authentication
Authentication is a chain: API key on your server → session token in the browser → into the iframe.
The API key
A long-lived JWT you issue in Monad (Settings → API Keys, the eyJ… value). It
authenticates backend calls as Authorization: ApiKey <key> and stays on your server,
for the reason in the architecture overview. See the
API Keys guide for issuing and rotating keys.
Minting a session token
Your backend trades the key for a token scoped to one sub-org, then hands it to the browser.
It's a plain POST /v3/sessions:
Code
Response:
Code
Passing it to the iframe
The frontend fetches a token from your endpoint, not Monad directly, and passes it to
createConnectorFrame. Watch the casing: the mint response is snake_case
(session_token) but createConnectorFrame wants camelCase (sessionToken). Remap at
the call site:
Code
Scope, lifetime, and reuse
- Scope is one sub-org. The API rejects any call outside the token's
orgclaim. - Lifetime is whatever
ttl_secondsyou set. Keep it short; about 30 minutes is typical. - A token is scoped to a sub-org and a time window, not to a single iframe. You can reuse one token across several mounts while it's valid. Mint per tenant, not per frame.
- The form reads the token once at mount. An expired token means minting a fresh one
and remounting. Re-mint on expiry or on a
401.
Next
- Connector Lifecycle — build pipelines and manage integrations.
- Best Practices — default inputs/outputs and deployment.
Support
For additional support contact support@monad.com