PATCH /v1/organizations/:orgId
Update a child organization's name, metadata, or billing email.
/v1/organizations/:orgId- 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.
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.
orgIdstringrequiredThe child org id (`org_`-prefixed or bare UUID).
Idempotency-Keystring (UUID)optionalOptional. Same key + same body replays the cached response. See [Idempotency](/docs/api/operational/idempotency).
namestringoptionalNew display name. 1–128 chars.metadataobject<string,string> | nulloptionalKey-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).billingEmailstringoptionalNew 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
{
"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
metadatais merged per key (Stripe-style), not replaced wholesale. Keys you omit are preserved; send a key with""to unset just that key; sendmetadata: nullto clear everything. Per-key values must be strings — a per-keynullis rejected. The string/key/value/count bounds (and the 16 KB backstop) apply to the result after the merge.archivedAtisnullfor 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).updatedAtadvances 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" }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
| 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
GET /v1/organizations/:orgId— read the current state.- Suspend / resume / archive — lifecycle transitions.