# Organizations (/docs/api/concepts/organizations)



Your Layers **organization** is the root of everything you do with the API integration — it holds your API keys, it's the billing principal for credits and ads spend, and every [project](/docs/api/concepts/projects) you create nests beneath it (directly, or under a child org). Your org is provisioned when you're onboarded as a partner; from that point forward every request resolves back to it.

There are two ways to model your customers, and you can mix them:

* **Flat** — every customer is a [project](/docs/api/concepts/projects) directly under your org. Simple, and the right shape for a handful of customers. Nothing here has changed.
* **Sub-organizations** — each customer gets its own **child organization** with an isolated wallet, audit trail, and dedicated API keys. Your top-level org becomes the **control plane** that creates, funds, suspends, and offboards them. This is the Stripe Connect / Twilio Subaccounts shape, and the right fit once you're managing customers at scale.

<Callout>
  Sub-organizations are **additive and non-breaking**. If the flat model works for you, keep using it — nothing about it changes. Adopt child orgs per customer when you want hard isolation and clean offboarding.
</Callout>

## The flat model [#the-flat-model]

One Layers org hosts many projects; each project represents a single end-customer:

```text
your org  ──┬── project A  (customer "acme-prod")
            ├── project B  (customer "wayne-labs")
            └── project C  (customer "stark-industries")
```

A single org-scoped key can touch any project inside the org. You keep customers isolated by path-scoping — every project-scoped route lives under `/v1/projects/:projectId/...` and the server returns `404 NOT_FOUND` for any project that doesn't belong to your org (we don't leak existence with a `403`).

<Callout>
  For belt-and-suspenders verification, read the project first via `GET /v1/projects/:projectId` and assert `customerExternalId` matches what your code expects before issuing follow-up calls.
</Callout>

## The sub-organization model [#the-sub-organization-model]

A **parent** org (yours) holds **child** orgs (one per customer). Each child is an isolation boundary: its projects, credits, audit log, and API keys are invisible to its siblings at the data layer — not by convention, but because a child key (or a parent acting on a child) can only ever resolve rows inside that one child.

```text
your org (parent · control plane)
  │   credit card · master wallet · org:admin key
  ├── Customer A  (org_cust_a · child)   wallet · audit · projects
  ├── Customer B  (org_cust_b · child)   wallet · audit · projects
  └── Customer C  (org_cust_c · child)   wallet · audit · projects
```

You create and manage children with the [organization endpoints](/docs/api/reference/organizations/create-organization), all gated by the [`org:admin`](/docs/api/concepts/api-keys#scopes) scope. The hierarchy is exactly **one level deep** — a child cannot itself have children (the API rejects it). Offboarding a customer is a single [`DELETE /v1/organizations/:orgId`](/docs/api/reference/organizations/archive-organization) that archives the child and revokes its keys atomically.

### Lifecycle [#lifecycle]

Every child org has a `status`:

```text
                 POST /organizations
                          │
                          ▼
   POST /:id/suspend  ┌─────────┐
   ◄───────────────── │ active  │ ──────────────┐
   ┌─────────┐  ─────► │         │               │ DELETE /:id
   │suspended│ ◄────── └─────────┘               │
   └─────────┘  POST /:id/resume                 ▼
        │                                  ┌──────────┐
        └────────────  DELETE /:id ───────►│ archived │ (terminal)
                                           └──────────┘
```

* **active** — normal operation.
* **suspended** — kill switch. The child's own keys get `503 KILL_SWITCH`; no generation, no spend. Reversible with [resume](/docs/api/reference/organizations/resume-organization). The parent can still read and manage a suspended child.
* **archived** — offboarded. Terminal: projects/keys are revoked and the org can't be restored. Returning customers get a fresh child org.

### Metadata [#metadata]

Every child org carries an opaque [`metadata`](/docs/api/reference/organizations/create-organization) object that's yours to own — stash your internal customer id, plan tier, Salesforce account id, whatever you reconcile against. Layers never reads or indexes it. It's **Stripe-shaped**: string→string pairs, each key ≤ 40 chars, each value ≤ 500 chars, ≤ 50 keys (a 16 KB cap backstops the whole object); anything outside those bounds is rejected with `422 VALIDATION`. It round-trips unchanged on every read and is editable via [`PATCH`](/docs/api/reference/organizations/patch-organization), which **merges** updates key-by-key (Stripe-style) — send only the keys you're changing, unset one by sending it with an empty string (`""`), or clear all with `metadata: null`. The same bounds and the same merge semantics apply to the `metadata` on a [credit allocation](/docs/api/reference/organizations/credits/allocate).

## Operating inside a child org [#operating-inside-a-child-org]

Today there is one way to act inside a child: your parent `org:admin&#x60; key plus the &#x2A;*`X-Layers-Organization`** header naming the child. Every existing endpoint works unchanged — the header transparently scopes the call to that child:

```bash
# Create a project inside Customer A's org, using your parent key
curl https://api.layers.com/v1/projects \
  -H "Authorization: Bearer $PARENT_KEY" \
  -H "X-Layers-Organization: org_cust_a" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Acme Main", "timezone": "America/New_York" }'
```

The header is honored only for keys that hold `org:admin`, and only for a child that is a direct child of the calling org — anything else returns `404`. See [Authentication → acting on behalf of a child](/docs/api/getting-started/authentication#acting-on-behalf-of-a-child-org) and the walkthrough guide.

### Dedicated child API keys [#dedicated-child-api-keys]

The header pattern is for when *your* backend drives a child with your parent key. When you'd rather hand a customer (or a per-customer service) its own credential, mint a **dedicated child API key**: a least-privilege key scoped to a single child org, so that integration never sees the parent or its siblings. Your parent `org:admin` key [mints](/docs/api/reference/organizations/api-keys/mint), [lists](/docs/api/reference/organizations/api-keys/list), [rotates](/docs/api/reference/organizations/api-keys/rotate), and [revokes](/docs/api/reference/organizations/api-keys/delete) them.

A child key can only carry scopes the parent holds, and can **never** hold `org:admin` — the control plane stays with the parent. Rotation is zero-downtime: a [rotate](/docs/api/reference/organizations/api-keys/rotate) mints a fresh secret while the old one keeps working for a 24-hour grace window. See [API keys → child keys](/docs/api/concepts/api-keys#child-keys-for-sub-organizations) for the full model.

## What the org owns [#what-the-org-owns]

In the sub-org model, some things live on the parent (the control plane) and some are per-child:

| Resource                                                     | Scope           | Notes                                                                                                                                                                                            |
| ------------------------------------------------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Credit card / master wallet                                  | **Parent only** | The parent is the billing principal. Children never put a card on file.                                                                                                                          |
| [`org:admin`](/docs/api/concepts/api-keys#scopes) capability | **Parent only** | Creating, suspending, and archiving children.                                                                                                                                                    |
| Projects                                                     | Per-child       | One per end-customer's workload, under that child.                                                                                                                                               |
| Credit wallet                                                | Per-child       | Each child has its own isolated balance, funded by the parent via [allocate](/docs/api/reference/organizations/credits/allocate). Generation debits the child's wallet; siblings are unaffected. |
| Audit log                                                    | Per-child       | Every write is stamped with `(api_key_id, org_id, project_id)`.                                                                                                                                  |
| [API keys](/docs/api/concepts/api-keys)                      | Per-org         | Scoped to one org, not to a project. Mint [child-scoped keys](/docs/api/reference/organizations/api-keys/mint) for each customer with your parent `org:admin` key.                               |

### Funding and governing child wallets [#funding-and-governing-child-wallets]

A child starts with an empty wallet. You **allocate** credits to it from your parent balance with [`POST /v1/organizations/:orgId/credits/allocate`](/docs/api/reference/organizations/credits/allocate), read its balance with [`GET …/credits`](/docs/api/reference/organizations/credits/get-credits), and reclaim the unspent remainder automatically when you archive it. Beyond manual funding, a parent can arrange a per-child **monthly spend cap** and **auto-refill** (top the child up from the parent when it runs low) — see the [Credits concept](/docs/api/concepts/credits) for the full model of allocation, caps, auto-refill, and `balance` vs `available`.

For existing single-org partners, nothing here changes: your org remains the billing principal and your projects hang directly off it. If you later adopt sub-orgs, you can move existing flat projects under child orgs in one call — see [Migrating existing projects](#migrating-existing-projects-into-children).

## Migrating existing projects into children [#migrating-existing-projects-into-children]

If you started flat and want to adopt the sub-org model, you don't have to recreate anything. [`POST /v1/organizations/migrate`](/docs/api/reference/organizations/migrate) takes a `mapping` of `{ projectId: childOrgName }` and, in one atomic call, **creates the named child orgs and moves the mapped projects under them**. Each distinct `childOrgName` becomes one new child; the response reports `projectsMoved`, `childrenCreated`, and the `children` it created (with their new `org_…` ids and the projects now under each).

A few rules worth knowing before you run it:

* **All-or-nothing.** If any mapped project has an in-flight execution, the whole migration is rejected with `409 CONFLICT` and nothing moves — retry once those workflows finish. A project that isn't yours returns `404 NOT_FOUND` (anti-enumeration — we don't confirm which).
* **History stays with the parent.** Past credit events for a migrated project remain on the parent's ledger; only the project's *future* spend bills to its new child wallet.
* **30-day read grace.** For 30 days after the move, the parent can still read a migrated project, so dashboards and reconciliation don't break the instant you migrate.

## Introspecting your org [#introspecting-your-org]

The first call any client makes is [`GET /v1/whoami`](/docs/api/reference/organizations/whoami). It resolves your key to the org it belongs to, echoes your organization name, and names your rate-limit tier. A successful `200` is itself the "key is live and the org is in good standing" signal — if access has been suspended every endpoint, including this one, returns `503 KILL_SWITCH` with a request id you can quote to support.
