post_orgs

Changes data · you confirm first

Calls POST /api/orgs as the signed-in person · REST reference

Create an organization together with its one owner

Requires execute:CreateOrgCommand (org.create, a platform feature no organization holds); platform staff are admitted as on every gate. The owner of the new org holds everything its TYPE is entitled to. owner is REQUIRED and names exactly one owner: an EXISTING user by email (404 when no user has it), or a newUser created with the org in both the graph and the auth store (409 when the email already belongs to a user). One DAL transaction creates the Org, its per-org permissions, its OWNER and MEMBER groups and roles, the new owner when there is one, and the owner's membership of the OWNER group — there is no window in which an org exists without its owner. parentOrgId places the new org under an existing organization — its parentId, written in that same transaction. It is checked before anything is written: an id that names no live organization is a 404 and creates nothing. Placing an org in the hierarchy is platform authority, which creating one already requires; PATCH /api/orgs/{id} (parentId) moves it later. type takes the Title_Case values from GET /api/orgs/types.

Arguments

ArgumentTypeDescription
bodyJSONobjectrequiredThe request body, as a JSON object. Its fields are listed below.

Fields of body

FieldTypeDescription
addressstring
emailstring (email)
namestringrequired
ownerobjectrequiredExactly one of email (an existing user) and newUser (a user created with the org).
owner.emailstring (email)
owner.newUserobject
owner.newUser.emailstring (email)required
owner.newUser.firstNamestringrequired
owner.newUser.lastNamestringrequired
owner.newUser.phonestringE.164, e.g. +15035550100.
parentOrgIdstring (uuid)The existing organization to create this one under (its parentId).
phonestring
taxIdstring
type"Network" | "Partner" | "Distributor" | "Service_Provider" | "Donor"required

Example call

A client sends this over the MCP session; Claude and other agents build it for you. The values are placeholders — the shape is real.

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "post_orgs",
    "arguments": {
      "body": {
        "name": "string",
        "owner": {
          "email": "name@example.org",
          "newUser": {
            "email": "name@example.org",
            "firstName": "string",
            "lastName": "string",
            "phone": "string"
          }
        },
        "type": "Network",
        "address": "string",
        "email": "name@example.org",
        "parentOrgId": "11111111-1111-4111-8111-111111111111",
        "phone": "string",
        "taxId": "string"
      }
    }
  }
}
Confirmation
The client shows the person the exact request and waits. Nothing runs until they tick Make this change. See Reading and changing.

Result

On success (HTTP 201) the tool returns one text item holding the API's JSON, pretty-printed.

The created organization, as the Org model wrapper serialises it. Bare object, no envelope.

json
{
  "createdAt": "2026-01-15T12:00:00Z",
  "id": "11111111-1111-4111-8111-111111111111",
  "name": "string",
  "status": "string",
  "type": "string",
  "address": "string",
  "email": "string",
  "parentId": "11111111-1111-4111-8111-111111111111",
  "phone": "string",
  "taxId": "string",
  "updatedAt": 1
}

Fields

FieldTypeDescription
addressstring | null
createdAtstring (date-time)ISO-8601 — coerced by the model, unlike every other timestamp here.
emailstring | null
idstring (uuid)
namestring
parentIdstring (uuid) | null
phonestring | null
statusstringThe status the DAL assigns on create, 'active' (this route sends none). A free string, not the DAL's OrgStatus set (ACTIVE | INACTIVE | SUSPENDED).
taxIdstring | null
typestring
updatedAtinteger | nullEpoch milliseconds, passed through untouched.

When it fails

A failed call returns isError: true with the API's message as text. A change the person declines, or lets time out, also comes back this way and nothing changes.

400

name, type or owner missing; owner naming both or neither form; a parentOrgId that is not an id; or a field the DAL refuses (an invalid type, email or E.164 phone, a name out of range).

403

The caller does not hold execute:CreateOrgCommand and is not platform staff, or the bearer is donor-realm and refused by authenticate.

404

owner.email names no user, or parentOrgId names no live organization. Nothing is created.

409

owner.newUser.email already belongs to a user — name them by owner.email.

What the model is shown

This is the tool exactly as the server lists it in tools/list. The description starts with the REST call it makes.

json
{
  "name": "post_orgs",
  "description": "POST /api/orgs — Requires `execute:CreateOrgCommand` (`org.create`, a platform feature no organization holds); platform staff are admitted as on every gate. The owner of the new org holds everything its TYPE is entitled to. `owner` is REQUIRED and names exactly one owner: an EXISTING user by `email` (404 when no user has it), or a `newUser` created with the org in both the graph and the auth store (409 when the email already belongs to a user). One DAL transaction creates the Org, its per-org permissions, its OWNER and MEMBER groups and roles, the new owner when there is one, and the owner's membership of the OWNER group — there is no window in which an org exists without its owner. `parentOrgId` places the new org under an existing organization — its `parentId`, written in that same transaction. It is checked before anything is written: an id that names no live organization is a 404 and creates nothing. Placing an org in the hierarchy is platform authority, which creating one already requires; `PATCH /api/orgs/{id}` (`parentId`) moves it later. `type` takes the Title_Case values from `GET /api/orgs/types`.\n\nThis changes data. The person is asked to confirm it before it runs, and nothing happens if they decline.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "body": {
        "type": "string",
        "description": "The JSON request body: { address: string; email: string; name*: string; owner*: { email: string; newUser: object } (Exactly one of `email` (an existing user) and `newUser` (a user created with the org).); parentOrgId: string (The existing organization to create this one under (its `parentId`).); phone: string; taxId: string; type*: one of \"Network\" | \"Partner\" | \"Distributor\" | \"Service_Provider\" | \"Donor\" }"
      }
    },
    "required": [
      "body"
    ]
  },
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": true,
    "idempotentHint": false,
    "openWorldHint": false
  }
}