> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.coperniq.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.coperniq.io/_mcp/server.

## Seats and labor cost on user invites

### Updated: `POST /users` takes a seat and a labor cost

Invite User now matches **Invite Members** in Coperniq. Along with name, email, role and phone, you can set the member's seat and labor cost.

```json
{
  "email": "anna.smith@example.com",
  "firstName": "Anna",
  "lastName": "Smith",
  "seatType": "FIELD",
  "roleIds": [3],
  "phone": "(208) 555-0112",
  "laborHourlyRate": 40
}
```

* `seatType` is `FIELD` (Field), `OFFICE` (Office) or `READONLY` (Viewer).
* Field and Office seats must be on your plan, with one free. The API doesn't buy seats. If none are free, the request returns `400`, for example `No available Field seats`. Buy more seats in Coperniq, then try again.
* `laborHourlyRate` is optional, in dollars per hour.
* `roleIds` is required unless `seatType` is `READONLY`. A Viewer seat only takes roles whose `roleType` is `VIEW_AND_COMMENT`. Leave `roleIds` out to use the default Viewer role.
* **Behavior change:** `seatType` is now required. Requests without it return `400`.
* **Behavior change:** an invite that Coperniq couldn't complete now returns `400` with the reason. It used to return `201` with `"status": "failed"`.
* Inviting someone who is already an active member still returns `201` with `"status": "exists"`, and nothing changes.

### Updated: `GET /users` returns `seatType` and `laborHourlyRate`

Each user now includes `seatType` and `laborHourlyRate`. Each is `null` when not set.

### Updated: `GET /roles` returns `roleType`

Each role includes `roleType`, either `REGULAR` or `VIEW_AND_COMMENT`, so you can pick roles for Viewer seats.