Layers

PATCH /v1/organizations/:orgId

Update a child organization's name, metadata, or billing email.

View as Markdown
PATCH/v1/organizations/:orgId
Phase 1stableidempotent
Auth
Bearer
Scope
org:admin

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

Status is not editable here. Move a child through its lifecycle with suspend, resume, and archive.

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 below.

Path
  • orgId
    stringrequired
    The child org id (`org_`-prefixed or bare UUID).
Headers
  • Idempotency-Key
    string (UUID)optional
    Optional. Same key + same body replays the cached response. See [Idempotency](/docs/api/operational/idempotency).
Body
  • name
    stringoptional
    New display name. 1–128 chars.
  • metadata
    object<string,string> | nulloptional
    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 (""). 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).
  • billingEmail
    stringoptional
    New contact email, or `null` to clear it.

Example request

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" }
  }'
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());
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()

Response

200OK
{
  "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"
}

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 (a terminal state that can no longer be patched).
  • updatedAt advances on every successful patch.

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:

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

A patch of:

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

produces:

{ "externalId": "cust_12345", "plan": "scale", "crmId": "a1b2" }
  • externalIdpreserved (not in the patch).
  • planoverwritten to "scale".
  • regionunset (sent with an empty string "").
  • crmIdadded.

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

Errors

StatusCodeWhen
404NOT_FOUNDNo such org, or it isn't a direct child of your org.
409CONFLICTThe org is archived — a terminal state whose name, metadata, and billing email can no longer be changed.
422VALIDATIONThe :orgId is not a valid organization id (malformed UUID).
422VALIDATIONname 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.
401UNAUTHENTICATEDMissing or invalid key.
403FORBIDDEN_SCOPEKey lacks org:admin.
409IDEMPOTENCY_CONFLICTSame Idempotency-Key replayed with a different body.

See also

On this page