# API (/docs/developer/api)

Sift exposes a REST API at `https://api.getsift.ai`. Use it to read actions, agents, queues, conversations, and analytics. You can also set custom fields, submit moderation cases, and ingest normalized records.

## At a glance [#at-a-glance]

| | |
| --- | --- |
| REST API at `https://api.getsift.ai` | ✅ |
| Read actions, agents, queues, analytics; search conversations | ✅ |
| Write: custom fields, moderation intake, normalized ingestion | ✅ |
| Send a reply as a customer-facing message | ❌ not available via this API today |
| `X-API-Key` header authentication | ✅ |
| OpenAPI spec and Swagger UI | ✅ |
| Full Operations DSL in `GET /v0/action/list?fullFilter=` | ✅ |
| Per-key rate limiting | ❌ not implemented yet |
| Current version `v0` | ✅ |

{/* screenshot */}
<figure>
  <img src="/assets/screenshots/developer/api.png" alt="The Sift API Swagger UI listing available endpoints" />
  <figcaption>Explore the REST API endpoints in the interactive Swagger UI.</figcaption>
</figure>

## Authentication [#authentication]

Every request must include your API key in the `X-API-Key` header:

```http
GET /v0/action/list
X-API-Key: sk_live_your_key_here
```

See the [API Keys](/docs/developer/api-keys) page for how to generate and manage keys, and what a key can and can't do.

## Exploring the API [#exploring-the-api]

Sift publishes a full OpenAPI spec and an interactive Swagger UI:

* **OpenAPI JSON**: `https://api.getsift.ai/docs/json`
* **Swagger UI**: `https://api.getsift.ai/docs`

You can import the OpenAPI spec into Postman, Insomnia, or any API client.

## First request for an agent [#first-request-for-an-agent]

Read the [API field reference](/docs/developer/api-reference) before building a request.
Use the [OpenAPI JSON](https://api.getsift.ai/docs/json) for complete machine-readable schemas.

```sh
curl --get 'https://api.getsift.ai/v0/action/list' \
  --header "X-API-Key: $SIFT_API_KEY" \
  --data-urlencode 'fullFilter=src=TWITTER+st=OPEN' \
  --data-urlencode 'from=2026-10-01T00:00:00Z' \
  --data-urlencode 'to=2026-10-07T23:59:59Z' \
  --data-urlencode 'pageSize=20'
```

`SIFT_API_KEY` is an environment variable, not a literal key in the example.
`fullFilter` applies all conditions and adds no responded default.
Legacy `filter` and `filterJson` read only the responded flag. Do not use them for platform or queue filtering.

Inspect this endpoint's `data` array. Other REST endpoints use their own response envelopes.
For reliable cursor walks and dependent agent calls, use [MCP](/docs/developer/mcp).

## Endpoints [#endpoints]

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/v0/action/list` | List actions. Supports `fullFilter`, date range, and sort - see [Query Syntax](/docs/developer/query-syntax) |
| `GET` | `/v0/action/:id` | Fetch a single action |
| `GET` | `/v0/actions/:actionId/custom-fields` | Read an action's custom fields (requires a `read`-scoped key) |
| `PATCH` | `/v0/actions/:actionId/custom-fields` | Set an action's custom fields (requires a `write`-scoped key) |
| `GET` | `/v0/agents` | List agents with performance metrics |
| `GET` | `/v0/agents/:id` | Fetch a single agent |
| `GET` | `/v0/queues` | List queues with metrics |
| `GET` | `/v0/analytics/tags` | Tag dashboard stats |
| `GET` | `/v0/analytics/tags/volume` | Tag volume over time |
| `GET` | `/v0/analytics/sentiment` | Daily sentiment distribution |
| `GET` | `/v0/analytics/sentiment/by-tag` | Sentiment broken down by tag |
| `GET` | `/v0/analytics/engagement` | Engagement percentiles, weekly growth, and funnel data |
| `GET` | `/v0/conversations/search` | Full-text and DSL search across conversations |
| `POST` | `/v0/moderation/cases` | Submit one moderation case (requires `write`) |
| `POST` | `/v0/moderation/cases/batch` | Submit 1–100 moderation cases in order (requires `write`) |
| `GET` | `/v0/moderation/cases/:caseId` | Poll the exact current evaluation status (requires `read`) |
| `POST` | `/v0/ingest/records` | Ingest 1–500 normalized records asynchronously (requires `write`) |

There is no endpoint that sends or posts a reply on a customer's behalf - the write routes cover custom fields, moderation submissions, normalized ingestion, and Custom API.

### Moderation intake [#moderation-intake]

Moderation endpoints are available only when your organization is configured for Sift's
`ad_policy` moderation mode. Intake validates the complete request before enqueueing work into the
normal Sift ingestion and synthesis pipeline.

The `externalId` is immutable within your organization. Retrying the exact same submission returns
`duplicate`. Reusing an existing `externalId` with different known content returns `409` for the
single-case endpoint. In batch, every input receives an ordered `accepted`, `duplicate`, or
`rejected` result; a conflicting item is rejected without preventing other valid items from being
accepted.

Use the returned `caseId` with the GET endpoint. Polling returns only the evaluation for the
currently active policy workflow and never falls back to an older completed evaluation. A case may
report `policy_not_configured`, `pending`, `running`, `completed`, or `failed`. The final decision is
included only after completion.

## Custom API intake [#custom-api-intake]

Use [Custom API](/docs/integrations/custom-api) to import customer content from your own tools. Create a source under Integrations, then create an Import key for its selected sources. Intake requires `ingest`; receipt reads require `ingest:read`. Reporting and general Write keys do not grant these scopes.

The guide includes single-item and batch imports, validation, receipt tracking, threading, and corrections.

## Error Format [#error-format]

Errors return a consistent JSON body:

```json
{
  "success": false,
  "error": "Action not found"
}
```

Some endpoints add extra fields on specific error cases - for example, the custom-fields `PATCH` returns a `fieldErrors` object (field name → message) on a `422`. An uncaught server error always returns `500` with `{ "success": false, "error": "Internal server error" }`.

Common status codes: `400` (bad request), `401` (missing or invalid key), `403` (forbidden, e.g. a `read`-scoped key calling a write route), `404` (not found), `409` (immutable identifier conflict), `422` (validation failed), `500` (server error).

## Versioning [#versioning]

The current API version is `v0`. Breaking changes will be introduced under a new version prefix; non-breaking additions (new fields, new endpoints) can be added at any time without a version bump.

## Related [#related]

<Cards>
  <Card href="/docs/developer/api-reference" title="API field reference">Every declared request and response field, including nested objects.</Card>
  <Card href="/docs/developer/api-keys" title="API Keys">Generate and manage the keys you pass in the `X-API-Key` header.</Card>
  <Card href="/docs/developer/query-syntax" title="Query Syntax">The filter DSL used by `GET /v0/action/list`.</Card>
  <Card href="/docs/developer/mcp" title="MCP">Query the same data conversationally from Claude, Cursor, and other clients.</Card>
</Cards>
