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

# Errors

> Status codes, the standard error envelope, and a reference of every documented error code.

Errors are returned with an appropriate HTTP status and a JSON body in a single, consistent shape:

```json theme={null}
{
  "error": "Human-readable message.",
  "code": "machine_readable_tag"
}
```

The `error` field is suitable for display to end users. The `code` field is populated only when callers are expected to programmatically branch on the failure mode (for example, to render a "Connect Notion" call-to-action when a Notion endpoint reports a missing integration). Generic validation errors omit `code`.

## Status codes

| Status                      | Meaning                                                                                                                                   |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `200 OK`                    | Request succeeded.                                                                                                                        |
| `201 Created`               | Resource created. The response body contains the new resource.                                                                            |
| `204 No Content`            | Successful preflight (CORS) only.                                                                                                         |
| `302 Found`                 | OAuth callback redirect. Only returned by `GET /api/auth/notion/callback`.                                                                |
| `400 Bad Request`           | Validation failed, or the request requires organization context the caller does not have.                                                 |
| `401 Unauthorized`          | The `Authorization` header is missing, malformed, or references a revoked credential.                                                     |
| `403 Forbidden`             | The credential is valid but the role attached to it is insufficient for the operation.                                                    |
| `404 Not Found`             | The resource does not exist, or is not visible to the caller. Some endpoints return `404` instead of `403` to avoid disclosing existence. |
| `409 Conflict`              | The operation conflicts with current state — typically a duplicate identifier on create, or a referenced role on delete.                  |
| `502 Bad Gateway`           | An upstream service (currently only Notion) returned an unexpected error.                                                                 |
| `503 Service Unavailable`   | A feature is not configured for the caller's organization (currently only the Notion integration).                                        |
| `500 Internal Server Error` | Unhandled exception. Includes a request identifier in the response body for support follow-up.                                            |

## Hiding existence with 404

For workspaces and API tokens, attempting to operate on a resource the caller cannot read returns `404 Not Found` instead of `403 Forbidden`. This prevents enumeration attacks that could otherwise reveal whether a particular ID is in use.

## Error codes

The following `code` values are a stable contract. Document any new codes here in the same pull request that introduces them.

### `no_active_org`

The session has no Clerk organization context, and the requested endpoint operates on organization-scoped data.

```json theme={null}
{
  "error": "Active organization required to create or modify org-scoped entities.",
  "code": "no_active_org"
}
```

**Resolution.** In browsers, prompt the user to pick an organization in the Clerk switcher. In headless clients, re-mint the personal access token from a session that has the desired organization active. See [Authentication › Organization binding](/authentication#organization-binding).

### `notion_misconfigured`

The caller's organization has not connected a Notion workspace.

```json theme={null}
{
  "error": "Notion integration is not configured for this organization",
  "code": "notion_misconfigured"
}
```

**Resolution.** An organization administrator must complete the OAuth flow at **Admin → Notion** in the application. Clients should treat this as recoverable and direct the user to that surface.

### `notion_access_denied`

Notion rejected an API request, typically because the integration was uninstalled from the workspace or the requested page was not shared with it.

```json theme={null}
{
  "error": "Notion rejected the request — re-connect Notion or check the integration's page access.",
  "code": "notion_access_denied"
}
```

**Resolution.** Re-run the Notion OAuth flow and verify the integration has been added to the relevant pages within Notion's sharing UI.

### `notion_not_found`

Notion returned `404` for a specific page or database ID. The integration is otherwise healthy.

```json theme={null}
{
  "error": "Notion couldn't find that page or database. Has it been shared with the integration?",
  "code": "notion_not_found"
}
```

**Resolution.** Confirm the page is shared with the Archyon integration in Notion. Pages must be explicitly added — Notion does not grant the integration access to all pages by default.

### `bad_notion_url`

A submitted URL did not parse as a Notion page or database link.

```json theme={null}
{
  "error": "Couldn't find a Notion page or database ID in that URL",
  "code": "bad_notion_url"
}
```

**Resolution.** Verify the URL matches the format `https://notion.so/…` and contains a 32-character hexadecimal identifier.

### `role_in_use`

A stakeholder role cannot be deleted because existing stakeholder links still reference it. The response includes the count of dependent links.

```json theme={null}
{
  "error": "Cannot delete \"Owner\" — 3 stakeholder link(s) still use this role. Reassign or remove them first.",
  "code": "role_in_use",
  "count": 3
}
```

**Resolution.** Reassign affected stakeholder links to a different role, or delete them, then retry the role deletion. The dependent link IDs can be obtained by listing stakeholders on the affected workspaces.

## Branching on codes

Production clients should branch on `code` rather than the `error` string, which is subject to wording changes. A typical pattern:

```typescript theme={null}
async function archyonFetch(path: string, init?: RequestInit) {
  const res = await fetch(`https://archyon.app${path}`, init);
  if (res.ok) return res.json();

  const body = await res.json().catch(() => ({}));
  switch (body.code) {
    case "no_active_org":
      throw new NeedsOrgContextError(body.error);
    case "notion_misconfigured":
      throw new NeedsNotionSetupError(body.error);
    case "role_in_use":
      throw new RoleInUseError(body.error, body.count);
    default:
      throw new ArchyonApiError(res.status, body.error ?? res.statusText);
  }
}
```

Treat any future `code` value not listed above as an unknown error and surface the human-readable `error` message until your client adds explicit handling.
