# PATCH /v1/organizations/:orgId (/docs/api/reference/organizations/patch-organization)



<Endpoint method="PATCH" path="/v1/organizations/:orgId" auth="Bearer" scope="org:admin" phase="1" />

Update the editable fields on a child org. Only the fields you send are changed; omitted fields are left as-is.

<Callout type="info">
  Status is **not** editable here. Move a child through its lifecycle with [suspend](/docs/api/reference/organizations/suspend-organization), [resume](/docs/api/reference/organizations/resume-organization), and [archive](/docs/api/reference/organizations/archive-organization).
</Callout>

<Callout type="info">
  **`metadata` is merged, not replaced.** Like Stripe, the object you send is merged key-by-key into the stored metadata: keys you send are added or overwritten, keys you omit are preserved. Unset a single key by sending it with an empty string (`""`); send `metadata: null` to clear the whole object. (Per-key values must be strings — a per-key `null` is rejected with `422`; `null` is only valid as the whole-object clear.) See [Merge semantics](#metadata-merge-semantics) below.
</Callout>

<Parameters
  title="Path"
  rows="[
  { name: 'orgId', type: 'string', required: true, description: 'The child org id (`org_`-prefixed or bare UUID).' },
]"
/>

<Parameters
  title="Headers"
  rows="[
  { name: 'Idempotency-Key', type: 'string (UUID)', description: 'Optional. Same key + same body replays the cached response. See [Idempotency](/docs/api/operational/idempotency).' },
]"
/>

<Parameters
  title="Body"
  rows="[
  { name: 'name', type: 'string', description: 'New display name. 1–128 chars.' },
  { name: 'metadata', type: 'object<string,string> | null', description: 'Key-value pairs merged into the stored metadata (Stripe-style): sent keys are added/overwritten, omitted keys are preserved. Unset a key by sending it with an empty string (&#x22;&#x22;). Send metadata: null to clear all. String values only; keys ≤ 40 chars, values ≤ 500 chars, ≤ 50 keys after merge (16 KB hard cap as a backstop).' },
  { name: 'billingEmail', type: 'string', description: 'New contact email, or `null` to clear it.' },
]"
/>

## Example request [#example-request]

<Tabs items="['curl', 'TypeScript', 'Python']">
  <Tab value="curl">
    ```bash
    curl -X PATCH https://api.layers.com/v1/organizations/org_d4e5f6a7-8b9c-4d0e-9f2a-3b4c5d6e7f80 \
      -H "Authorization: Bearer $PARENT_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Acme Coffee (US)",
        "metadata": { "externalId": "cust_12345", "plan": "scale" }
      }'
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    const org = await fetch(
      "https://api.layers.com/v1/organizations/org_d4e5f6a7-8b9c-4d0e-9f2a-3b4c5d6e7f80",
      {
        method: "PATCH",
        headers: {
          "Authorization": `Bearer ${process.env.PARENT_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          name: "Acme Coffee (US)",
          metadata: { externalId: "cust_12345", plan: "scale" },
        }),
      },
    ).then((r) => r.json());
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os, requests

    org = requests.patch(
        "https://api.layers.com/v1/organizations/org_d4e5f6a7-8b9c-4d0e-9f2a-3b4c5d6e7f80",
        headers={
            "Authorization": f"Bearer {os.environ['PARENT_KEY']}",
            "Content-Type": "application/json",
        },
        json={"name": "Acme Coffee (US)", "metadata": {"externalId": "cust_12345", "plan": "scale"}},
    ).json()
    ```
  </Tab>
</Tabs>

## Response [#response]

<Response status="200" description="OK">
  ```json
  {
    "id": "org_d4e5f6a7-8b9c-4d0e-9f2a-3b4c5d6e7f80",
    "parentOrganizationId": "org_2481fa5c-a404-44ed-a561-565392499abc",
    "name": "Acme Coffee (US)",
    "status": "active",
    "metadata": { "externalId": "cust_12345", "plan": "scale" },
    "billingEmail": "ops@acme.example",
    "archivedAt": null,
    "createdAt": "2026-06-01T14:30:00.000000+00:00",
    "updatedAt": "2026-06-02T09:15:00.000000+00:00"
  }
  ```
</Response>

### Field notes [#field-notes]

* `metadata` is **merged per key** (Stripe-style), not replaced wholesale. Keys you omit are preserved; send a key with `""` to unset just that key; send `metadata: null` to clear everything. Per-key values must be strings — a per-key `null` is rejected. The string/key/value/count bounds (and the 16 KB backstop) apply to the result *after* the merge.
* `archivedAt` is `null` for an active or suspended org; it carries the archival timestamp only after the org is [archived](/docs/api/reference/organizations/archive-organization) (a terminal state that can no longer be patched).
* `updatedAt` advances on every successful patch.

## Metadata merge semantics [#metadata-merge-semantics]

Metadata updates follow the same convention as Stripe, so an agent can update one key without round-tripping the whole object first.

Given a child org that already stores:

```json
{ "externalId": "cust_12345", "plan": "growth", "region": "us" }
```

A patch of:

```json
{ "metadata": { "plan": "scale", "region": "", "crmId": "a1b2" } }
```

produces:

```json
{ "externalId": "cust_12345", "plan": "scale", "crmId": "a1b2" }
```

* `externalId` — **preserved** (not in the patch).
* `plan` — **overwritten** to `"scale"`.
* `region` — **unset** (sent with an empty string `""`).
* `crmId` — **added**.

To clear the entire object, send `"metadata": null`. If a merge removes the last remaining key, the stored value becomes `null`.

## Errors [#errors]

| Status | Code                   | When                                                                                                                                             |
| ------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| 404    | `NOT_FOUND`            | No such org, or it isn't a direct child of your org.                                                                                             |
| 409    | `CONFLICT`             | The org is **archived** — a terminal state whose name, metadata, and billing email can no longer be changed.                                     |
| 422    | `VALIDATION`           | The `:orgId` is not a valid organization id (malformed UUID).                                                                                    |
| 422    | `VALIDATION`           | `name` out of range; `metadata` violates the string/≤40-key/≤500-value/≤50-keys bounds (or the 16 KB backstop after merge); or an unknown field. |
| 401    | `UNAUTHENTICATED`      | Missing or invalid key.                                                                                                                          |
| 403    | `FORBIDDEN_SCOPE`      | Key lacks `org:admin`.                                                                                                                           |
| 409    | `IDEMPOTENCY_CONFLICT` | Same `Idempotency-Key` replayed with a different body.                                                                                           |

## See also [#see-also]

* [`GET /v1/organizations/:orgId`](/docs/api/reference/organizations/get-organization) — read the current state.
* [Suspend](/docs/api/reference/organizations/suspend-organization) / [resume](/docs/api/reference/organizations/resume-organization) / [archive](/docs/api/reference/organizations/archive-organization) — lifecycle transitions.
