Tenants vs accounts
TryMellon’s data model has three nested concepts: account → tenants → applications. Understanding this shape is critical for reading the dashboard, interpreting 404 errors on admin endpoints, and wiring S2S integrations correctly.
Core concepts
Account (your billing entity)
├── Tenant A ← isolates users, credentials, audit logs
│ └── Application A1
├── Tenant B
│ ├── Application B1
│ └── Application B2
└── Tenant C
└── Application C1
- Account — the top-level entity that owns billing, plan limits, and member users. One account per customer.
- Tenant — an isolation boundary for runtime data (users, credentials, sessions, audit logs). Customer A’s users cannot authenticate against Customer B’s tenant by construction.
- Application — a client/app inside a tenant. Holds the OAuth2 credentials, webhook configuration, WebAuthn RP ID, etc.
When does a new tenant get created?
Every call to POST /v1/applications from a dashboard user session creates a new tenant + the application + an owner membership for the creator in that new tenant.
Why: this keeps users, audit logs, and credentials isolated per application even inside the same account. A breach of one application’s credentials cannot pivot into another application’s users.
Consequence: your dashboard account will often have many tenants (one per app), and the “Applications” list you see spans them. A tenant name like "Tenant: 7f3a-…-auto" is an auto-generated label for an application’s isolation boundary — not an error.
Authorization model
TryMellon distinguishes two callers:
| Caller | Credential | Scope | Can list apps cross-tenant? |
|---|---|---|---|
| Service principal | OAuth2 client credentials (client_id + client_secret) | One tenant (the tenant the credentials are issued for) | No — tenant-scoped only |
| User principal | Session cookie / JWT obtained via passkey login | The account the user belongs to, plus every tenant they are a member of | Yes — with ?scope=account |
Matrix: who can do what
| Action | Service principal (same tenant) | User creator | User tenant owner | User tenant admin | User tenant viewer |
|---|---|---|---|---|---|
GET /v1/applications/:id | ✅ | ✅ | ✅ | ❌ (404) | ❌ (404) |
PATCH /v1/applications/:id | ✅ | ✅ | ✅ | ❌ (404) | ❌ (404) |
POST /v1/applications/:id/rotate-secret | ✅ | ✅ | ✅ | ❌ (404) | ❌ (404) |
DELETE /v1/applications/:id (future) | ✅ | ❌ (404) | ✅ | ❌ (404) | ❌ (404) |
GET /v1/applications?scope=account | ❌ (403) | ✅ | ✅ | ✅ | ✅ |
admin and developer roles do not grant edit/rotate/delete on applications today — only the owner role does. This is a conservative default; if your team needs a finer role × action matrix, reach out.
Denials return 404, not 403
For per-resource endpoints (:id), TryMellon returns 404 application_not_found on every denial — regardless of whether the app exists. This is a deliberate info-leak mitigation: an attacker probing your tenant cannot distinguish “app does not exist” from “app exists but I’m not authorized”. Audit logs internal to TryMellon capture the reason (authorization.denied) with the principal identity and the resource’s tenant so operators can reconcile.
See Admin REST API → Error responses for the full shape.
Dashboard segmentation
The TryMellon dashboard reflects this model in two sections:
- Account — Overview, Billing, Team, Applications (cross-tenant list with a Tenant column).
- Tenant — Users, Credentials, Audit logs, Settings — scoped to the selected tenant via a tenant switcher.
This keeps the “one app = one tenant” reality visible without requiring you to know the tenant ID of each app.
Related
- Admin REST API — endpoints and error responses.
- Data locality — where TryMellon stores each bounded context.