---
title: "Create service user"
method: POST
path: "/service-users"
tags: ["service-users"]
---

# Create service user

`POST /service-users`

## Request

- Base URL: `https://api.opal.dev/v1`
- URL: `https://api.opal.dev/v1/service-users`
- Auth: HTTP bearer

Creates a new service user (a non-human identity owned by a team). Mirrors
the GraphQL `createServiceUser` mutation; admin-only.

The returned `User` object is the newly created service user and is a
regular `User` row — read it later via `GET /users/{user_id}`, delete it
with `DELETE /users/{user_id}`, and manage its tags via the usual
`/users/{user_id}/tags` endpoints.

**Role grants are a separate concern.** To grant a service user access
to resources or groups, use `POST /role-assignments` (or the per-pair
endpoints such as `/resources/{id}/users/{id}`). Keeping role
assignments out of this request body means each grant stays
independently addressable — the shape Terraform and other declarative
clients prefer, with no field here that would silently drift when a
grant is added or revoked through a different code path.

**Automations are not configurable here either.** The GraphQL mutation
accepts a wide typed-union of automation configuration; that surface is
intentionally omitted from this REST endpoint for the same reason.
Service users created through this endpoint start with no automations.

## Request body

- CreateServiceUserRequest: Request body for `POST /service-users`. Intentionally minimal: only the two fields that uniquely identify a new service user. Role grants are a separate resource managed through `/role-assignments`, which keeps each grant independently addressable and avoids silent drift when grants are added or revoked through another code path.
  - `name` string, required: The display name of the service user.
  - `owner_id` string, uuid, required: The ID of the owner (team of record) that owns the service user. Must reference an existing owner in the caller's organization.

## Response `200`

The created service user.

- User: # User Object ### Description The `User` object is used to represent a user. ### Usage Example Fetch from the `LIST Sessions` endpoint.
  - `user_id` string, uuid, required: The ID of the user.
  - `email` string, required: The email of the user.
  - `full_name` string, required: The full name of the user.
  - `first_name` string, required: The first name of the user.
  - `last_name` string, required: The last name of the user.
  - `position` string, required: The user's position.
  - `manager_id` string, uuid, nullable: The ID of the user's manager. Null if the user has no manager.
  - `hr_idp_status` 'ACTIVE' | 'SUSPENDED' | 'DEPROVISIONED' | 'DELETED' | 'NOT_FOUND': User status pulled from an HR/IDP provider.

## Other responses

- `400`: Invalid request body: malformed JSON, unknown field, missing or empty `name`, or missing / malformed `owner_id`.
- `403`: Caller is not an admin.
- `404`: The `owner_id` does not reference an owner in the organization.

## Changes

- **2026-10-28** `c54b1c07fcdb`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/opal/apis/opal-api/changes/service-users/post.md)

---

[API](https://skmtc.dev/opal/apis/opal-api.md) - [All operations](https://skmtc.dev/opal/apis/opal-api/llms.txt) - [OpenAPI document](https://skmtc.dev/opal/apis/opal-api/revisions/e7877ea96eab?raw)
