post_users
Calls POST /api/users as the signed-in person · REST reference
Create a user directly, into a named organization
The admin "Add User" path, as opposed to the invitation flow. Requires execute:CreateUserCommand; platform staff bypass that gate. IDENTITY FIELDS ONLY. The DAL's CreateUserInput assigns the initial status and roles and groups are granted separately, so a status or roles in the body is IGNORED rather than half-applied. profile is forwarded as the user's metadata. orgId is required by this route because the DAL declares it UUID! and evaluates the write against that scope — the endpoint refuses to guess which org a new user belongs to. The caller must hold execute:CreateUserCommand through a membership IN that org (403 otherwise); a permission held in one org does not create users in another. email is lower-cased before it reaches the DAL.
Arguments
| Argument | Type | Description | |
|---|---|---|---|
bodyJSON | object | required | The request body, as a JSON object. Its fields are listed below. |
Fields of body
| Field | Type | Description | |
|---|---|---|---|
email | string (email) | required | Validated against a simple x@y.z regex before anything else runs. |
firstName | string | Defaults to an empty string when omitted. | |
lastName | string | Defaults to an empty string when omitted. | |
orgId | string (uuid) | required | Non-empty string. Not validated as a UUID by this route. |
profile | object | Stored as the user's metadata. |
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.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "post_users",
"arguments": {
"body": {
"email": "name@example.org",
"orgId": "11111111-1111-4111-8111-111111111111",
"firstName": "string",
"lastName": "string",
"profile": {}
}
}
}
}Result
On success (HTTP 201) the tool returns one text item holding the API's JSON, pretty-printed.
The created user — the DAL entity, passed through unchanged. Bare object, no envelope; timestamps are epoch MILLISECONDS.
{
"createdAt": 1,
"deleted": true,
"email": "string",
"emailVerified": true,
"firstName": "string",
"id": "string",
"lastName": "string",
"mfaEnabled": true,
"status": "ACCEPTED",
"acceptedAt": 1,
"deletedAt": 1,
"deletedBy": "string",
"invitedAt": 1,
"lastLoginAt": 1
}Fields
| Field | Type | Description |
|---|---|---|
acceptedAt | number | null | |
createdAt | number | |
deleted | boolean | |
deletedAt | number | null | |
deletedBy | string | null | |
email | string | |
emailVerified | boolean | |
firstName | string | |
id | string | |
invitedAt | number | null | |
lastLoginAt | number | null | |
lastName | string | |
metadata | object | null | |
mfaEnabled | boolean | |
phone | string | null | |
status | "ACCEPTED" | "ACTIVE" | "APPROVED" | "DELETED" | "DONOR_ONLY" | "INVITED" | … (9 values) | |
suspendedAt | number | null |
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.
400emailmissing or malformed, ororgIdmissing or not a non-empty string — or a field the DAL refuses (a name's length, a phone's format), in the DAL's words.403Authenticated, but the caller holds none of the required permissions.
404The organization does not exist, or has no member group to create the user into.
409A user with that email already exists — the graph's
email IS UNIQUEconstraint refused the write. The body is fixed; the upstream text is never forwarded.
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.
{
"name": "post_users",
"description": "POST /api/users — The admin \"Add User\" path, as opposed to the invitation flow. Requires `execute:CreateUserCommand`; platform staff bypass that gate. IDENTITY FIELDS ONLY. The DAL's `CreateUserInput` assigns the initial status and roles and groups are granted separately, so a `status` or `roles` in the body is IGNORED rather than half-applied. `profile` is forwarded as the user's `metadata`. `orgId` is required by this route because the DAL declares it `UUID!` and evaluates the write against that scope — the endpoint refuses to guess which org a new user belongs to. The caller must hold `execute:CreateUserCommand` through a membership IN that org (403 otherwise); a permission held in one org does not create users in another. `email` is lower-cased before it reaches the DAL.\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: { email*: string (Validated against a simple `x@y.z` regex before anything else runs.); firstName: string (Defaults to an empty string when omitted.); lastName: string (Defaults to an empty string when omitted.); orgId*: string (Non-empty string.); profile: object (Stored as the user's `metadata`.) }"
}
},
"required": [
"body"
]
},
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false,
"openWorldHint": false
}
}