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

# API overview

> Base URL, authentication, and data model for the Whistle API.

The **API reference** tab lists SDK operations from [`openapi.json`](/api-reference/openapi.json). Use **Try it** against a running API (for example `http://localhost:3001`). Feature-specific organization routes may appear in the server’s Swagger UI even when not yet mirrored in this bundle—when in doubt, treat the running server as source of truth.

## Base URL

SDK operations in this spec use:

```http theme={null}
https://api.kansato.com/sdk/whistle/v1/...
```

Local development often uses `http://localhost:3001` with the same path prefix.

Organization-scoped REST routes (dashboard, AI, attachments) typically live under `/api/v1/organizations/{orgSlug}/...`. See [Developer access](/developer-access) and [Kuro AI](/kuro-ai) for examples.

## Authentication

| Type        | Prefix     | Scope                               | Where to use        |
| ----------- | ---------- | ----------------------------------- | ------------------- |
| Secret      | `sk_live_` | Full server-side access             | Your backend only   |
| Publishable | `pk_live_` | Create reports, list report reasons | Browser (React SDK) |

```http theme={null}
Authorization: Bearer sk_live_xxxxxxxxxxxx
```

Create keys in **Settings → Developer → API Keys**. Keys are tied to a **project** inside your workspace.

Publishable keys are limited to report ingestion and reasons (for example `POST /projects/:projectId/reports` and `GET /projects/:projectId/report-reasons` on the SDK mount). Other SDK routes return **403** with a publishable key.

## Rate limiting

Report creation is limited to **10 requests per minute per API key**. When exceeded, responses may include:

```json theme={null}
{ "error": "Too many reports. Please try again later.", "retryAfter": 45 }
```

`retryAfter` is seconds until the window resets (when provided).

## Errors

Failures return JSON such as:

```json theme={null}
{ "error": "Unauthorized" }
```

Message strings can change; always branch on HTTP status and log the body for support.

## Data model

### Project

`projectId` is the stable UUID from the dashboard, not the display name. A workspace can host multiple projects.

### Identity

Keyed by project + identity `type` + `externalId`. Mirrors a user (or actor) on your platform. Upsert semantics preserve existing display fields when you send empty or omitted values—see the identities endpoints in the reference.

### Case

A case is the canonical moderation envelope — one incident tied to content (target), optional reporter/subject identities, and linked duplicate reports.

| Field                | Values                                                                        |
| -------------------- | ----------------------------------------------------------------------------- |
| Status               | `OPEN`, `TRIAGED`, `IN_REVIEW`, `RESOLVED`, `DISMISSED`, `APPEALED`, `CLOSED` |
| Severity bucket      | `emergency`, `high`, `normal` (assigned at intake)                            |
| Reason severity hint | `LOW`, `MEDIUM`, `HIGH`, `CRITICAL` (from linked report reasons)              |

Case lifecycle and queue semantics: [Moderation workflow](/moderation-workflow).

### Enforcement action

State changes on warnings, suspensions, bans, etc. Webhook events cover create, update, and revoke—see [Outbound webhooks](/webhooks).
