`POST /api/v1/users` — create a local or service account. Create an account.
const url = 'https://example.com/api/v1/users';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"kind":"local","password":"example","role":"example","username":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/api/v1/users \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "kind": "local", "password": "example", "role": "example", "username": "example" }'A service account (kind: "service") is a machine identity: no password, and no way to sign
in through either the local form or SSO. It exists to own API tokens, so an unattended
integration keeps working when the person who set it up changes teams — and so that disabling it
stops every credential it owns at once.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”Create-user request body. The password is hashed before storage and never logged.
object
What kind of account this is. Defaults to local, so a client written before service
accounts existed keeps creating exactly what it did before.
oidc is not accepted: those accounts are provisioned by signing in through the IdP, never
by hand — creating one here would produce an account whose subject matches nobody.
Required for a local account, and rejected for a service one — a machine account has no
password by design, so accepting a discarded one would advertise a login that does not exist.
Responses
Section titled “Responses”Account created
The id of a freshly created resource — the whole body of a 201.
Deliberately one shape for every creator. The json!({"id": …}) literal it replaces was written
out per handler, which is how {"id": …} and {"node_id": …} both ended up in this API for the
same idea; a client then needs to know which creator it called to read the id back.
object
Examplegenerated
{ "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"}Empty username, an unknown role, a password that is missing/too short for a local account or supplied for a service one, or kind: oidc
The ADR-019 envelope every failure renders as. pub(crate) and schema-bearing so the OpenAPI
document can name one error shape for every endpoint (ADR-035) instead of leaving 4xx/5xx bodies
undescribed — a client that has to guess the failure shape ends up parsing the success shape and
reading undefined.
object
object
Stable machine-readable code. Clients branch on this, never on the message.
Operator-facing sentence. Safe to display; never carries an internal error’s own text.
Examplegenerated
{ "error": { "code": "example", "message": "example" }}No valid bearer token
The ADR-019 envelope every failure renders as. pub(crate) and schema-bearing so the OpenAPI
document can name one error shape for every endpoint (ADR-035) instead of leaving 4xx/5xx bodies
undescribed — a client that has to guess the failure shape ends up parsing the success shape and
reading undefined.
object
object
Stable machine-readable code. Clients branch on this, never on the message.
Operator-facing sentence. Safe to display; never carries an internal error’s own text.
Examplegenerated
{ "error": { "code": "example", "message": "example" }}Role below Admin
The ADR-019 envelope every failure renders as. pub(crate) and schema-bearing so the OpenAPI
document can name one error shape for every endpoint (ADR-035) instead of leaving 4xx/5xx bodies
undescribed — a client that has to guess the failure shape ends up parsing the success shape and
reading undefined.
object
object
Stable machine-readable code. Clients branch on this, never on the message.
Operator-facing sentence. Safe to display; never carries an internal error’s own text.
Examplegenerated
{ "error": { "code": "example", "message": "example" }}The username is already taken
The ADR-019 envelope every failure renders as. pub(crate) and schema-bearing so the OpenAPI
document can name one error shape for every endpoint (ADR-035) instead of leaving 4xx/5xx bodies
undescribed — a client that has to guess the failure shape ends up parsing the success shape and
reading undefined.
object
object
Stable machine-readable code. Clients branch on this, never on the message.
Operator-facing sentence. Safe to display; never carries an internal error’s own text.
Examplegenerated
{ "error": { "code": "example", "message": "example" }}This core has no write side (skeleton mode)
The ADR-019 envelope every failure renders as. pub(crate) and schema-bearing so the OpenAPI
document can name one error shape for every endpoint (ADR-035) instead of leaving 4xx/5xx bodies
undescribed — a client that has to guess the failure shape ends up parsing the success shape and
reading undefined.
object
object
Stable machine-readable code. Clients branch on this, never on the message.
Operator-facing sentence. Safe to display; never carries an internal error’s own text.
Examplegenerated
{ "error": { "code": "example", "message": "example" }}