# Give or remove a platform role

> The platform.staff.set_role operation: POST /api/v1/platform/staff in the REST API and the platform_staff_set_role tool in the MCP server.

Gives a person a platform role (`platform_admin`, `platform_tax_admin` or `platform_support`), changes it, or
removes it (`platformRole: null`) (spec §3.2). Only addresses at the platform's staff domains
(`PLATFORM_STAFF_DOMAINS`) can hold a role; a person without an account gets one and signs in with Google or
Microsoft (staff can't use email codes, spec §6.2). The platform always keeps one platform admin. Platform roles
never open an organization's data by themselves: support staff still request access per organization.
Platform admins only, after signing in within the last 15 minutes.

## At a glance

- **REST:** `POST /api/v1/platform/staff`
- **MCP tool:** `platform_staff_set_role`
- **Operation ID:** `platform.staff.set_role`
- **Scope:** Platform
- **Permission:** `platform:admin`
- **Risk tier:** `security`
- **Dry run:** Supported (send `Ownrecord-Dry-Run: true`)
- **Idempotency:** `Idempotency-Key` required
- **Modes:** Test and live
- **Authentication:** API key with the `platform:admin` scope
- **Fresh sign-in:** In the console, a sign-in within the last 15 minutes
- **Since:** M12

## Request

### Body

A JSON object (`Content-Type: application/json`):

| Field          | Type                                                                      | Required | Description                             |
| -------------- | ------------------------------------------------------------------------- | -------- | --------------------------------------- |
| `email`        | string                                                                    | Yes      | The person's address at a staff domain. |
| `platformRole` | one of `platform_admin`, `platform_tax_admin`, `platform_support` or null | Yes      | The role, or null to remove it.         |

## Response

`200 OK` with a JSON object:

| Field          | Type                                                                      | Description |
| -------------- | ------------------------------------------------------------------------- | ----------- |
| `object`       | `"platform_staff_member"`                                                 |             |
| `userId`       | `usr_…` ID                                                                |             |
| `email`        | string                                                                    |             |
| `name`         | string or null                                                            |             |
| `platformRole` | one of `platform_admin`, `platform_tax_admin`, `platform_support` or null |             |
| `lastLoginAt`  | date-time or null                                                         |             |
| `disabled`     | boolean                                                                   |             |

## Examples

### Add a support engineer

```http
POST /api/v1/platform/staff
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "email": "ivo@ownrecord.com",
  "platformRole": "platform_support"
}
```

Response (`200`):

```json
{
  "object": "platform_staff_member",
  "userId": "usr_01m5wvkry1e6w94sjxgme33fsb",
  "email": "ivo@ownrecord.com",
  "name": null,
  "platformRole": "platform_support",
  "lastLoginAt": null,
  "disabled": false
}
```

## Errors

| Code                                                                                                             | HTTP status | When                                                          | What to do                                                                                                |
| ---------------------------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| [`platform_staff_domain_required`](https://ownrecord.io/docs/reference/errors.md#platform_staff_domain_required) | 400         | The address isn't at one of the staff domains.                | Use the person's address at one of the staff domains (`PLATFORM_STAFF_DOMAINS`, shown on the Staff page). |
| [`platform_last_admin`](https://ownrecord.io/docs/reference/errors.md#platform_last_admin)                       | 409         | The change would leave the platform without a platform admin. | Make someone else a platform admin first, then change this person's role.                                 |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)                           | 400         | The address or role is invalid.                               | Fix the fields listed in `details.issues` and send the request again.                                     |
| [`fresh_auth_required`](https://ownrecord.io/docs/reference/errors.md#fresh_auth_required)                       | 403         | You last signed in more than 15 minutes ago.                  | Sign in again, then repeat the action within 15 minutes.                                                  |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)                           | 403         | You aren't a platform admin.                                  | Ask an organization owner or admin for a role or scope that allows this action.                           |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`api_version_unsupported`](https://ownrecord.io/docs/reference/errors.md#api_version_unsupported), [`invalid_json`](https://ownrecord.io/docs/reference/errors.md#invalid_json), [`unsupported_media_type`](https://ownrecord.io/docs/reference/errors.md#unsupported_media_type), [`authentication_required`](https://ownrecord.io/docs/reference/errors.md#authentication_required), [`api_key_invalid`](https://ownrecord.io/docs/reference/errors.md#api_key_invalid), [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found), [`idempotency_key_required`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_required), [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
