UptimeHunt Docs
Organizations

Account Tenancy Model

Explanation of personal workspaces and team organizations, org-only accounts, and landing behavior.

Account Tenancy Model

Overview

UptimeHunt uses an org-centric tenancy model: every resource (service, project, alert rule, integration) lives inside an organization. An organization is a bounded tenant with its own members, roles, audit log, and billing plan.

This page explains:

  • the two org types and when a personal workspace exists
  • which accounts are org-only (no personal workspace)
  • where you land after signing in
  • how to create a personal workspace after the fact
  • the opt-in deprovisioning setting that ties continued membership to an active IdP or GitHub-org mapping

Two Org Types

Personal workspaceTeam organization
Created byAutomatic during personal sign-upExplicitly at sign-up, by conversion, or via POST /api/v1/orgs
MembersYou aloneInvited members with roles
Roles— (you own everything)Owner / Admin / Member
SSOOIDC, SAML 2.0, Google Workspace, GitHub
Audit logYour own activityOrganization-wide, role-gated
Token oversightYour own tokensOwners see all members' org tokens
PlanFree and paid plansTeam / Enterprise plans (14-day trial)

Personal Workspaces Are Optional

Not every UptimeHunt account has a personal workspace. Three creation paths produce accounts that belong to a team organization only, with no personal side-channel:

PathResult
Sign up as a team — choose Team at registrationFounder gets a team org; no personal workspace
Accept an organization invitation as a new userAccount created through the invitation link; no personal workspace
SSO JIT provisioning — first sign-in through an org's identity providerJIT-provisioned realm account; no personal workspace

This is deliberate: in a team setting there is no personal side-channel for resources or API tokens that would escape the organization's roles, audit log, and token oversight. Users who later want a personal workspace can create one on demand — see Creating a Personal Workspace Later.

A user who signed up personally has a personal workspace, and it behaves as a single-user account independent of any team organizations they also belong to.

Where You Land After Signing In

The app resolves landing destination in this order, using the personal_org_slug, last_active_org_slug, and default_org_slug fields from GET /api/v1/auth/me:

ScenarioLanding behavior
Returning user (previously acted in an org)Restores the last-active org context (last_active_org_slug) — you land where you left off
First login (single org)Lands directly in that org (default_org_slug); no chooser needed
First login (multi-org user, no history)Shows the org chooser — one-time until a preference is set
Zero-org userDirected to org creation or the Create your personal workspace flow

The breadcrumb (last_active_org_slug) is updated every time you make an authenticated API request under a specific org context (the X-Org-Slug header). It is cleared to null if the org is no longer one you are a member of — a removed membership or a dissolved org never pins the landing.

Org Chooser for Zero-Org Users

If a new account ends up with no org membership at all (an edge case — for example after all team memberships have been removed and no personal workspace was created), the app presents the first-org chooser: create a personal workspace (POST /api/v1/auth/personal-org) or accept a pending invitation. API clients in this state receive ORG_CONTEXT_REQUIRED on any org-scoped request until at least one org is established.

GET /auth/me Fields

GET /api/v1/auth/me exposes three org-context fields the frontend reads on boot:

FieldTypeMeaning
personal_org_slugstring | nullThe caller's personal workspace slug, or null if they have none
last_active_org_slugstring | nullThe last org the caller acted in; null if unset or no longer a member
default_org_slugstring | nullConvenience: personal org, else sole org, else null (multi-org with no default)
orgs[]arrayEvery membership (team orgs first alphabetically, personal org last)

Example for a personal-only user:

{
  "personal_org_slug": "u-42",
  "last_active_org_slug": null,
  "default_org_slug": "u-42",
  "orgs": [{"slug": "u-42", "org_type": "personal", "role": "owner", ...}]
}

Example for a team-only account that has visited its org:

{
  "personal_org_slug": null,
  "last_active_org_slug": "acme",
  "default_org_slug": "acme",
  "orgs": [{"slug": "acme", "org_type": "team", "role": "member", ...}]
}

Creating a Personal Workspace Later

A team-only or social account can create a personal workspace at any time using the org-switcher's Create personal workspace button, or the API:

POST /api/v1/auth/personal-org
Authorization: Bearer {access_token}

Response (201 Created):

{
  "data": {
    "id": 17,
    "slug": "u-42",
    "name": "My Workspace",
    "org_type": "personal",
    "role": "owner",
    "capabilities": ["service.view", "service.edit", ...]
  }
}

Rules:

  • At most one personal workspace per account — a second call returns 409 Conflict ("You already have a personal organization"). The UI hides the button once a personal org exists.
  • Creating a personal workspace mints no new login method — the account is already authenticated; only the org row and its owner membership are created.
  • The new personal workspace starts empty — no resources are migrated from the team org(s).
  • A personal workspace created this way can later be converted to a team organization if needed.

Personal sign-up users already have a workspace

This endpoint is for accounts that were created without one (team sign-up, invitation, JIT). If you signed up personally, you already have a personal workspace — calling this returns 409.

Deprovisioning: Membership Requires Continued IdP or GitHub-Org Membership

By default, a team-org membership persists until an Owner manually removes the member. The "Membership requires continued IdP/GitHub-org membership" org setting (membership_requires_idp) changes this: the organization opts into automatic deprovisioning — a member whose IdP role-mapping or GitHub-org membership lapses is removed automatically at their next sign-in.

Default: off

membership_requires_idp is false by default. Without it, a lapsed IdP mapping or GitHub-org removal only affects the member's role (updated by role re-sync), not their membership. Enable it only when you want the IdP or GitHub-org to be the authoritative source of who belongs to the organization.

How It Works

TriggerDeprovisioning action
OIDC / SAML / Google Workspace sign-in — role-mapping evaluation returns deny for the memberIf the org has membership_requires_idp: true, the membership is revoked automatically. Without the setting, the sign-in is refused but the membership is kept.
GitHub (platform app) sign-in — GitHub-org membership is confirmed false for the member's GitHub loginIf the org has membership_requires_idp: true and github_org is set, the membership is revoked automatically.
GitHub team-role rule evaluation returns denySame as above — membership is revoked if opted in.

Deprovisioning is best-effort on transient errors: a GitHub API outage or an unreadable OIDC groups claim never auto-removes a member. The member is offboarded only when non-membership is definitively confirmed. This prevents false offboarding during provider downtime.

The sole Owner is never auto-deprovisioned — the last-owner invariant applies: a deprovisioning decision that would leave the organization without any Owner is skipped, and the Owner keeps their membership and can sign in. Promote another Owner before removing the last one.

Audit Trail

Every automatic deprovisioning emits a member.deprovisioned audit event (visible in Organization → Audit Log) with the reason (sso_role_mapping_denied, github_org_membership_lapsed, github_team_role_denied). Manually-removed members appear as member.removed regardless of this setting.

Configuring the Setting

Not yet self-service

membership_requires_idp is implemented at the backend level and enforced at sign-in, but it is not yet exposed via the REST API or the Settings UI. The GET /api/v1/orgs/{slug} response never includes this field, and a PATCH body containing it is silently ignored (Django REST Framework drops unknown fields on partial update). There is also no frontend form control for it. To enable automatic deprovisioning for your organization, contact UptimeHunt support.

Enabling this setting is irreversible in practice

Once enabled, any member whose IdP group or GitHub-org mapping lapses at their next sign-in will be deprovisioned silently. Verify that every current member has a valid mapping before enabling, or you may lose members unexpectedly at their next login. Deprovisionings are audited but cannot be undone automatically — removed members must be re-invited.

Interaction with Enforced SSO

membership_requires_idp and enforced_sso are orthogonal:

  • Enforced SSO gates the login method — members must authenticate through the org IdP (interactive sign-in only; API tokens keep working).
  • membership_requires_idp gates membership itself — a member whose mapping lapses is removed, not just refused a sign-in.

Both can be active at the same time. An organization with both enabled gets the strongest lifecycle guarantee: only active IdP members can sign in AND membership is revoked the moment the IdP mapping lapses.

Summary

FeatureAPI field / endpoint
Check if you have a personal workspacepersonal_org_slug in GET /api/v1/auth/me
Restore landing contextlast_active_org_slug in GET /api/v1/auth/me
Create a personal workspace on demandPOST /api/v1/auth/personal-org
Opt org into automatic deprovisioningContact UptimeHunt support (not yet self-service via API or UI)

On this page