# Set Notification Preferences

> Changes how the signed-in person is notified: per channel (in-app, email, Slack), optionally per workspace and event kind, instant, daily digest or off.

- **URL**: https://orshot.com/docs/api-reference/approvals-preferences-set

---

The Approvals API is available on Enterprise plans, or through first-party apps (the Orshot app and the Orshot MCP server). This endpoint needs [`read` access](/docs/api-reference/approvals-overview#access-levels) and acts for one person: send their OAuth token, or an API key with their user id in `X-Orshot-User-Id`. An API key also needs [`move` access](/docs/api-reference/approvals-overview#access-levels) here, since this changes something of theirs.
See [Enterprise pricing](https://orshot.com/pricing) to get access.

Changes how the signed-in person is notified: per channel (in-app, email, Slack), optionally per workspace and event kind, instant, daily digest or off. Also sets their time zone for the digest. A third-party app changes only the workspaces it was given (workspaceId is required) and can't change the time zone.

## Endpoint

```markdown tab="Endpoint"
https://api.orshot.com/v1/notification-preferences
```

## Request Body

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `preferences` | Array | No | Up to 200 items. |
| `timezone` | String | No | IANA time zone for the daily digest, e.g. Europe/Berlin. Can be `null`. |

### `preferences[]`

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `workspaceId` | Integer | No | null applies to all your workspaces. Can be `null`. |
| `eventKind` | String | Yes | "*" for every event. One of `*`, `approval.item.added`, `approval.item.moved`, `approval.item.removed`, `approval.decision.created`, `approval.decision.revoked`, `approval.edit.started`, `approval.override.used`, `approval.render.blocked`, `template.edited`, `template.render_gate.changed`, `comment.created`, `comment.resolved`, `comment.hidden`, `approval_flow.created`, `approval_flow.updated`, `approval_flow.archived`, `approval_flow.duplicated`, `workspace_group.updated`. |
| `channel` | String | Yes | One of `inapp`, `email`, `slack`. |
| `mode` | String | Yes | One of `instant`, `digest`, `off`. |

## Headers

| Header | Required | Description |
| ------ | -------- | ----------- |
| `Authorization` | Yes | `Bearer <API key or OAuth token>` |
| `X-Orshot-User-Id` | With an API key | The user id of the owner, admin or member this call is for. OAuth tokens don't send it. See Acting for a Team Member below. |
| `X-Session-Id` | No | Groups the calls of one agent or MCP session in the activity history. |

## Acting for a Team Member

This acts for one person. With an API key, send `X-Orshot-User-Id` with the user id of an owner, admin or member of the workspace: the answer is theirs, as their own OAuth token would get it, within the key's workspace. It changes something of theirs, so the key needs `move` access as well: a key with only `read` gets `403 enterprise_api_required`. Without the header, API keys get `403 permission_denied`, code `approval.person_required`. See [acting for a team member](https://orshot.com/docs/api-reference/approvals-overview#acting-for-a-team-member).

## Request

**Request**
```js
await fetch("https://api.orshot.com/v1/notification-preferences", {
  method: "PUT",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <ORSHOT_OAUTH_TOKEN>",
  },
  body: JSON.stringify({
    "preferences": [
      {
        "eventKind": "*",
        "channel": "email",
        "mode": "digest"
      }
    ],
    "timezone": "Europe/Berlin"
  }),
});
```

**Response**
```json
{
  "preferences": [
    {
      "workspaceId": null,
      "eventKind": "*",
      "channel": "email",
      "mode": "digest"
    }
  ],
  "timezone": "Europe/Berlin",
  "warnings": []
}
```

## Response Fields

Responds `200` with:

| Field | Type | Description |
| ----- | ---- | ----------- |
| `preferences` | Array | List of NotificationPreference, fields below. |
| `preferences[].workspaceId` | Integer | Can be `null`. |
| `preferences[].eventKind` | String | "*" or an event kind. |
| `preferences[].channel` | String | One of `inapp`, `email`, `slack`. |
| `preferences[].mode` | String | One of `instant`, `digest`, `off`. |
| `timezone` | String | Can be `null`. |
| `warnings` | Array | Non-blocking notices, each `{ code, message? }`, for example `no_approver`. Always present on writes, empty when there is nothing to say. |

## Error Responses

Every error has the same body: `error`, `code`, `message` and `helpUrl`, plus the fields that apply (`required`, `role`, `context`, `blockers`, `violations`, `issues`, `current`). Branch on `error`; `code` is more specific. See the [error reference](https://orshot.com/docs/error-reference).

| Status Code | Error | Description |
| ----------- | ----- | ----------- |
| 401 | `oauth_token_invalid` | The OAuth token is invalid, expired or revoked. |
| 403 | `api_key_missing` | No `Authorization: Bearer` header. |
| 403 | `permission_denied` | You don't have access to do this. Ask an owner or admin. `code` names the capability, for example `approval.item.decide_denied`; `required` and `role` say what was missing. With `code: insufficient_scope`: the OAuth token lacks `workspace:approvals:write`. API keys without `X-Orshot-User-Id` get `approval.person_required`: this acts for one person. With `code: approval.member_header_not_member`: `X-Orshot-User-Id` names someone who isn't an owner, admin or member of the workspace now. With `code: approval.member_header_not_allowed`: an OAuth token sent `X-Orshot-User-Id`. |
| 403 | `plan_required` | The workspace's plan doesn't include Approvals, or doesn't include this part of it. |
| 403 | `enterprise_api_required` | Approvals through third-party apps need an Enterprise plan. Contact hi@orshot.com to turn it on. |
| 404 | `not_found` | This doesn't exist or isn't available to you. Reviewers get this, never 403, for flows and templates outside their access. |
| 422 | `validation_failed` | Some fields aren't valid: the fields listed in issues. `issues` lists each field with a path and a reason. |
| 422 | `validation_failed` | With `code: approval.member_header_invalid`: `X-Orshot-User-Id` isn't a user id. |
| 429 | `rate_limit_exceeded` | More than 120 approvals requests in a minute from one person or key. Wait for Retry-After. |
| 503 | `approvals_unavailable` | Approvals aren't available right now. Try again in a moment. Also returned while approvals are not switched on for the API. |