---
title: "Create organization"
method: POST
path: "/organization"
tags: ["Organizations"]
---

# Create organization

`POST /organization`

## Request

- Base URL: `https://api.elaichi.ai`
- URL: `https://api.elaichi.ai/organization`
- Auth: HTTP bearer

Creates an organization and makes the caller its first member, holding the immutable Org Owner role; the predefined role set is seeded at the same time. Needs an interactive browser session: an org API token is refused with 403, because a token scoped to one organization must not create another. Also needs public signup to be enabled for the service; otherwise 403 `signup_disabled`.

## Request body

- object
  - `name` string, required
  - `slug` string: Lowercase alphanumeric with hyphens. Generated from the name when omitted.
  - `region` 'us' | 'eu' | 'apac': Data location, fixed at creation: `eu` and `us` pin the organization's data to that jurisdiction, `apac` is a placement hint. It cannot be changed afterwards.
  - `referral_code` string: Optional referral code, for tracking only. Whitespace is trimmed, a blank value is treated as absent, and it is stored upper-cased. Recorded once at creation; not returned by this API and not editable.
  - `signup_source` object: Optional attribution for where the signup came from, for tracking only. Every field is optional. Malformed input is dropped, never a 400: unknown keys, non-string or blank values, and a `referrer_host` that is not a hostname are ignored. Values are cut to 100 characters; `referrer_host` is reduced to a lowercase hostname (a full URL is accepted and stripped to its host). Recorded once at creation; not returned by this API and not editable.
    - `utm_source` string
    - `utm_medium` string
    - `utm_campaign` string
    - `utm_term` string
    - `utm_content` string
    - `ref` string
    - `referrer_host` string

## Response `201`

Created

- Organization
  - `id` string: Organization id (`org_…`) — the value for `X-Organization-Id`.
  - `name` string
  - `slug` string: Immutable after creation.
  - `plan` string: The stored plan: `gold`, `black`, or `none` when locked. This is not the same as the effective entitlement — read `GET /organization/{id}/entitlements` before gating a feature on it.
  - `region` string, nullable: Data location fixed at creation: `us`/`eu` pin a hard jurisdiction, `apac` is a placement hint.
  - `trial_ends_at` string, date-time, nullable
  - `trial_indefinite_at` string, date-time, nullable: Set when Elaichi granted this organization an indefinite trial, otherwise null. Unlocked with no end date: no countdown, no expiry email, billing still reachable.
  - `settings` object: Free-form org settings. `PATCH /organization/{id}` REPLACES this object wholesale.
  - `logo` string, uri, nullable: Public URL of the logo image, or null.
  - `can_delete` boolean: Whether the caller may delete this organization: the `org:delete` permission, which only the Org Owner role holds. Server-computed, so branch on this rather than inspecting roles. Always `false` on rows that have no member context to compute it from, such as the organization list `GET /organization`. It never overstates: trust it when true, and read `GET /user/me` or `GET /organization/{id}` (both compute it) when you need it for a list row.
  - `can_manage` boolean: Whether the caller may change this organization's settings: the `org:manage` permission that `PATCH /organization/{id}` requires, and the organization is neither suspended nor in its deletion grace period. Server-computed, and `false` wherever `can_delete` is false for lack of a member context; it never overstates.
  - `allow_staff_impersonation` boolean: Whether Elaichi support may sign in as this organization's members to help them. `true` by default. When `false`, every request made through such a support session against this organization is refused with `403 staff_impersonation_blocked`, reads included, and the organization is left out of that session's `GET /user/me` and `GET /organization`. Changed with `PATCH /organization/{id}` (`org:manage`).
  - `mfa_required` boolean: Whether members must hold a second factor to act in this organization. `false` by default. When `true`, a signed-in session that has not shown a second factor (no TOTP challenge at login, no passkey sign-in, no factor enrolled since) is refused on every organization-scoped route, reads included, with `403 mfa_setup_required`; `error.details.has_second_factor` says whether the person already has one and only needs to sign in with it. Exempt: org API tokens, Elaichi support sessions, and a session signed in through this organization's own SSO connection. MCP connections made before it was switched on, or from a session without a factor, are refused until the person connects again. Turning it off only lifts the organization requirement: a member who set up an authenticator app of their own is still challenged for it at every non-passkey sign-in. Changed with `PATCH /organization/{id}` (`org:manage`, interactive session only).
  - `can_require_mfa` boolean: Whether the CALLER may turn `mfa_required` ON right now: `can_manage`, from an interactive session that would itself pass the check it is about to create. `false` — with the reason in `can_require_mfa_reason` — when switching it on would lock the caller out of this organization. The PATCH refuses that regardless with `409 mfa_setup_required`. Turning it OFF is not governed by this field; it needs a step-up.
  - `can_require_mfa_reason` string, nullable: A sentence saying why `can_require_mfa` is `false` for a caller who can otherwise manage the organization. `null` otherwise.
  - `deletion_scheduled_at` string, date-time, nullable: Set when a deletion has been scheduled (`DELETE /organization/{id}`). The organization is read-only until `purge_after`. Null otherwise.
  - `purge_after` string, date-time, nullable: When a scheduled deletion becomes permanent — 30 days after `deletion_scheduled_at`. Null unless scheduled.
  - `can_restore` boolean: Whether the caller may cancel a scheduled deletion: the `org:manage` permission, and only while `purge_after` is still ahead. Server-computed like `can_delete`, and `false` on rows with no member context. Read `purge_after` to tell "too late" from "not yours to undo".
  - `can_authorize_apps` boolean: Whether the CALLER may connect an app (MCP OAuth consent) to this organization right now: `false` while it is suspended, scheduled for deletion, or has no active plan. Server-computed by the same gate `POST /oauth/authorize-request/{id}/approve` calls; present only on member-scoped responses. `authorize_apps_blocked_reason` says which.
  - `authorize_apps_blocked_reason` 'blocked' | 'pending_deletion' | 'no_plan' | 'null', nullable: Why `can_authorize_apps` is `false`, in the gate's own order: `blocked`, then `pending_deletion` (even when the subscription is also gone, since a soft delete cancels it), then `no_plan`. `null` when connecting is open.
  - `created_at` string, date-time
  - `updated_at` string, date-time

## Other responses

- `400`: Bad request
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found
- `429`: Rate limited

---

[API](https://skmtc.dev/elaichi/apis/elaichi-api.md) · [All operations](https://skmtc.dev/elaichi/apis/elaichi-api/llms.txt) · [OpenAPI document](https://skmtc.dev/elaichi/apis/elaichi-api/revisions/b22cfb7b6d15?raw)
