> ## Documentation Index
> Fetch the complete documentation index at: https://trygradient.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Settings API

> Update organization settings over the API: rollout flags, the logo and wordmark, profile photos, and a preview of the branded invite email.

# Settings

Manage your organization's display settings and brand assets.

## Update Settings

```
PATCH /api/settings
```

<Info>This endpoint requires a session cookie - API key authentication is not currently supported for settings updates.</Info>

<ParamField body="userName" type="string">
  Your display name
</ParamField>

<ParamField body="orgName" type="string">
  Organization name
</ParamField>

<ParamField body="jobRole" type="string">
  Your job role
</ParamField>

<ParamField body="autoJoinEnabled" type="boolean">
  Turn email-domain auto-join on or off for the organization. Owners/admins only.
</ParamField>

<ParamField body="autoJoinDomains" type="string[]">
  Allowed email domains for auto-join (e.g. `["acme.com"]`). Generic providers (gmail.com, outlook.com, etc.) are rejected.
</ParamField>

<ParamField body="notifyOnSubmit" type="boolean">
  Email the org when a candidate submits an assessment.
</ParamField>

<ParamField body="companyDescription" type="string">
  Free-text description of the company, used to ground AI generation. Trimmed; an empty string clears it.
</ParamField>

<ParamField body="captureReplaysEnabled" type="boolean">
  Org-wide toggle for capturing candidate session replays.
</ParamField>

<ParamField body="useCase" type="string">
  Why the organization runs assessments: `hiring` or `upskilling`. An empty string clears it. Human review is in rollout: the setting has an effect only for an organization that Gradient has turned the feature on for. `hiring` turns on human review: every submitted session must be marked reviewed with an outcome (see [Record Human Review](/docs/api-reference/sessions#record-human-review)), the decision is written to the audit log, and the member who sent the invite gets reminder emails (see `reviewRemindersEnabled`). Owners/admins only.
</ParamField>

<ParamField body="reviewRemindersEnabled" type="boolean">
  Whether the member who sent the invite gets reminder emails while a submitted session has no review decision. Default `true`. It has an effect only while `useCase` is `hiring` and human review is on for the organization. Only sessions submitted after reminders turn on get reminders. When you turn reminders on again, this start time resets. Owners/admins only.
</ParamField>

<Accordion title="Response">
  ```json theme={null}
  {
    "ok": true
  }
  ```
</Accordion>

## Set an Internal Rollout Flag

```
PATCH /api/settings/flags
```

Turn one **internal rollout** flag on or off for a named organization. Backs the
staff-only Settings → Feature Flags console.

<Warning>
  This is the one endpoint that deliberately crosses the tenant boundary: it writes
  to an organization the caller need not belong to. Gated on Gradient staff by exact
  email against the `staff_members` table, a dedicated table whose only sanctioned
  writer is `src/lib/auth/staff.ts` (enforced by `scripts/check-staff-writes.ts` in
  CI). Non-staff callers get `404`, so the endpoint is not discoverable.
</Warning>

<ParamField body="orgId" type="string" required>
  The target organization's id. Required and explicit; there is no "current
  organization" fallback. The audit row records this alongside the acting staff
  member.
</ParamField>

<ParamField body="key" type="string" required>
  Registry key of the flag, e.g. `identitySsoEnabled`. Must be declared in the flag
  registry with `audience: "internal"`.
</ParamField>

<ParamField body="value" type="boolean" required>
  New value. Must be a literal `true` or `false`.
</ParamField>

<Info>
  **Customer-facing flags are rejected here at any privilege level.**
  `captureReplaysEnabled`, `emailBrandingEnabled`, `autoJoinEnabled`,
  `notifyOnSubmit`, `captureDeliverableHistoryEnabled` and `atsIntegrationsEnabled`
  are the organization's own decision, and several carry consent or validation
  obligations that live on their own settings pages (the session-replay consent
  warning, the auto-join domain rules and Stytch sync). They are changed via
  `PATCH /api/settings` by that organization's own owners and admins.
</Info>

Every accepted change is appended to `org_flag_changes` with the acting staff
member's email, and appears in the console's Change log.

<Accordion title="Response">
  ```json theme={null}
  {
    "ok": true
  }
  ```
</Accordion>

<Accordion title="Errors">
  | Status | Meaning |
  | - | - |
  | `404` | Caller is not in `staff_members`. |
  | `403` | The flag is customer-facing and not settable here. |
  | `400` | Missing `orgId`, unknown flag key, or a non-boolean value. |
</Accordion>

## Roll an Internal Flag Out to Every Organization

```
POST /api/settings/flags
```

Set one **internal rollout** flag to the same value for every organization, in one
call. Backs the "Roll out to all" and "Turn off everywhere" controls in the
staff-only Settings → Feature Flags console. Use `PATCH` above to move a single
organization.

<Warning>
  Same two gates as `PATCH`, and the audience check matters more here rather than
  less: "turn session replay on for every organization" would be a consent decision
  for every tenant made in one click. Customer-facing flags are rejected at any
  privilege level.
</Warning>

<ParamField body="key" type="string" required>
  Registry key of the flag. Must be declared with `audience: "internal"`.
</ParamField>

<ParamField body="value" type="boolean" required>
  The value every organization should end up with.
</ParamField>

Organizations already resolving to `value` are skipped rather than rewritten,
including those riding the registry default — writing an explicit value that
matches the default adds a key that says nothing and turns the change log into
noise. A failure on one organization does not abort the rest; the response reports
counts and names what failed, so a partial result is never presented as success.

<Accordion title="Response">
  ```json theme={null}
  {
    "ok": true,
    "total": 33,
    "changed": 27,
    "skipped": 6,
    "failed": []
  }
  ```
</Accordion>

<Accordion title="Errors">
  | Status | Meaning |
  | - | - |
  | `404` | Caller is not in `staff_members`. |
  | `403` | The flag is customer-facing and not rollable. |
  | `400` | Unknown flag key or a non-boolean value. |
  | `500` | The organization list hit its 1000-row ceiling, so the rollout would have silently skipped an unknown number. Refused rather than half-applied. |
</Accordion>

## Upload Logo or Wordmark

```
POST /api/settings/logo
```

Upload a square logo or horizontal wordmark for your organization. The image is stored in Supabase storage and the URL is saved to the organization record.

<Info>Requires session cookie with owner or admin role.</Info>

<ParamField body="file" type="file" required>
  Image file (PNG, JPG, SVG, or WebP). Max 2 MB.
</ParamField>

<ParamField body="kind" type="string" required>
  Either `logo` (square icon) or `wordmark` (horizontal lockup).
</ParamField>

<Accordion title="Response">
  ```json theme={null}
  {
    "url": "https://your-project.supabase.co/storage/v1/object/public/org-assets/org-id/wordmark.png?v=1713000000000"
  }
  ```
</Accordion>

## Remove Logo or Wordmark

```
DELETE /api/settings/logo
```

Remove a previously uploaded logo or wordmark.

<Info>Requires session cookie with owner or admin role.</Info>

<ParamField body="kind" type="string" required>
  Either `logo` or `wordmark`.
</ParamField>

<Accordion title="Response">
  ```json theme={null}
  {
    "ok": true
  }
  ```
</Accordion>

## Preview Branded Invite Email

```
POST /api/settings/branding/preview
```

<Info>Requires admin role (owner or admin).</Info>

Renders the real candidate invite email (the same builder that sends it) with the supplied branding values — which may be **unsaved** edits from the Branding settings form — so the settings page can show a truthful live preview. The Gradient logo (shown only when no wordmark is set) is inlined as a data URI so it renders in-browser.

<ParamField body="sessionId" type="string">
  A real assessment session id in the caller's org. When set, the preview uses that session's real candidate, assessment, and due date instead of the sample values. An unknown id, or one from another org, returns a 404.
</ParamField>

<ParamField body="orgName" type="string">
  Company name shown in the copy/subject. Falls back to the saved org name.
</ParamField>

<ParamField body="wordmarkUrl" type="string">
  Public wordmark URL to render on a white header. Omit/null for the Gradient header.
</ParamField>

<ParamField body="accentColor" type="string">
  `#rrggbb` accent applied to the stripe and button. Ignored when not a valid hex.
</ParamField>

<ParamField body="inviteIntro" type="string">
  Custom first sentence. Blank uses the default org/role sentence.
</ParamField>

<Accordion title="Response">
  ```json theme={null}
  {
    "subject": "Assessment invitation from Acme",
    "html": "<!DOCTYPE html> … ",
    "to": "alex.rivera@example.com"
  }
  ```
</Accordion>

## Upload Profile Photo

```
POST /api/settings/avatar
```

Upload a profile photo for the signed-in user. The image is stored in Supabase storage and the URL is saved to the user record, replacing the initials avatar shown in the top bar.

<Info>Requires an admin-portal session cookie. Candidate sessions are rejected.</Info>

<ParamField body="file" type="file" required>
  Image file (PNG, JPG, or WebP). Max 5 MB.
</ParamField>

<Accordion title="Response">
  ```json theme={null}
  {
    "url": "https://your-project.supabase.co/storage/v1/object/public/user-avatars/user-id/avatar.png?v=1713000000000"
  }
  ```
</Accordion>

## Remove Profile Photo

```
DELETE /api/settings/avatar
```

Remove the signed-in user's profile photo, reverting to initials.

<Info>Requires an admin-portal session cookie. Candidate sessions are rejected.</Info>

<Accordion title="Response">
  ```json theme={null}
  {
    "ok": true
  }
  ```
</Accordion>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.