# Overview

What the Simple Feedback API does, the base URL, every REST route with its scope, and the conventions all routes follow, such as paging.

The Simple Feedback API lets your own software do what the dashboard does: create and change feedback
surveys, get the link and QR code respondents open, and read the responses and statistics they produce.

There are two ways in, with the same key and the same rules:

- **REST** under `/v1`, for your backend, a script or a no-code tool. Start with the [Quickstart](https://simplefeedback.de/developer/docs/quickstart.md).
- **MCP** at `/mcp`, for an AI assistant that should work with your surveys directly. See
  [Connect an AI assistant](https://simplefeedback.de/developer/docs/mcp.md).

## Base URL

All routes are relative to `https://simplefeedback.de/api`, so checking your key is `GET https://simplefeedback.de/api/v1/me`. Requests and
responses are JSON over HTTPS.

## What you need

- **A plan with API access.** Without it, every call answers `403 API_ACCESS_NOT_IN_TIER`. The plan is checked on
  every request, so a downgrade switches existing keys off at once and an upgrade switches them on again.
- **An API key.** Owners and admins of an organization create them in the dashboard under **Settings → API keys**.
  [Authentication and API keys](https://simplefeedback.de/developer/docs/authentication.md) explains scopes, venue keys and what happens when people leave.

## Routes

| Method   | Path                                   | Scope                | What it does                                  |
| -------- | -------------------------------------- | -------------------- | --------------------------------------------- |
| `GET`    | `/v1/me`                               | none                 | Describes the key: organization, plan, scopes |
| `GET`    | `/v1/usage`                            | `surveys:read`       | The plan's limits and what is used            |
| `GET`    | `/v1/venues`                           | `surveys:read`       | Lists the organization's venues               |
| `GET`    | `/v1/surveys`                          | `surveys:read`       | Lists surveys as summaries                    |
| `GET`    | `/v1/surveys/plan-fit`                 | `surveys:read`       | Surveys beyond the plan, and the grace period |
| `GET`    | `/v1/surveys/deleted`                  | `surveys:read`       | Lists the trash                               |
| `POST`   | `/v1/surveys`                          | `surveys:write`      | Creates a survey                              |
| `GET`    | `/v1/surveys/{id}`                     | `surveys:read`       | Returns one survey with its questions         |
| `PATCH`  | `/v1/surveys/{id}`                     | `surveys:write`      | Changes a survey                              |
| `DELETE` | `/v1/surveys/{id}`                     | `surveys:write`      | Deletes a survey                              |
| `POST`   | `/v1/surveys/{id}/undo`                | `surveys:write`      | Undoes the last change made through the API   |
| `POST`   | `/v1/surveys/{id}/restore`             | `surveys:write`      | Brings a survey back out of the trash         |
| `GET`    | `/v1/surveys/{id}/qr`                  | `surveys:read`       | Returns the public link and a QR code image   |
| `POST`   | `/v1/surveys/{id}/qr-codes`            | `qr:write`           | Creates a saved QR code that counts scans     |
| `GET`    | `/v1/surveys/{id}/plan-copy`           | `surveys:read`       | Previews a copy that fits the plan            |
| `POST`   | `/v1/surveys/{id}/plan-copy`           | `surveys:write`      | Replaces a survey with a copy that fits       |
| `POST`   | `/v1/surveys/{id}/access-codes`        | `access-codes:write` | Creates single-use codes                      |
| `GET`    | `/v1/surveys/{id}/access-codes`        | `access-codes:read`  | Lists single-use codes with their status      |
| `POST`   | `/v1/surveys/{id}/access-codes/revoke` | `access-codes:write` | Revokes unused single-use codes               |
| `POST`   | `/v1/surveys/{id}/access-codes/qr`     | `access-codes:read`  | Returns the link and QR code of one code      |
| `GET`    | `/v1/surveys/{id}/responses`           | `responses:read`     | Lists the submitted responses                 |
| `GET`    | `/v1/surveys/{id}/statistics`          | `responses:read`     | Counts and averages per question              |
| `GET`    | `/v1/report`                           | `responses:read`     | Responses, ratings and scans across venues    |
| `GET`    | `/v1/responses`                        | `responses:read`     | The newest responses over every survey        |
| `GET`    | `/v1/surveys/{id}/insights`            | `insights:read`      | Returns the stored AI analysis                |
| `POST`   | `/v1/surveys/{id}/insights/generate`   | `insights:write`     | Runs a new AI analysis                        |
| `GET`    | `/v1/qr-codes`                         | `qr:read`            | Lists the saved QR codes                      |
| `GET`    | `/v1/qr-codes/{id}`                    | `qr:read`            | Returns one QR code                           |
| `GET`    | `/v1/qr-codes/{id}/stats`              | `qr:read`            | Scans per day and platform                    |
| `PATCH`  | `/v1/qr-codes/{id}`                    | `qr:write`           | Renames, re-points or switches off a QR code  |
| `DELETE` | `/v1/qr-codes/{id}`                    | `qr:write`           | Deletes a QR code                             |
| `POST`   | `/v1/images`                           | `files:write`        | Uploads a survey logo or background image     |

The [API reference](https://simplefeedback.de/api/docs) lists every request body and response field. Code generators can read the
same thing as an [OpenAPI document](https://simplefeedback.de/api/docs-json).

## Conventions

**Lists** are newest first and paginated with `limit` (1 to 100, default 25) and `offset` (default 0). They come back
as:

```json
{
  "data": [],
  "pagination": { "total": 0, "limit": 25, "offset": 0 }
}
```

**Survey lists carry summaries**: id, name, venue, whether it is published and how many responses it has. Fetch a
single survey for its `modules` (the pages and questions) and `settings`.

**Venues.** Every survey belongs to one venue. When you create a survey without `venueId`, it goes to the
organization's default venue, or to the key's venue if the key is limited to one.

**`PATCH` replaces each field you send.** There is no deep merge. To change one question, fetch the survey, edit the
`modules` array you got back and send the whole array.

**`settings` is validated as a whole.** When you send it, `general`, `display` and `participation` and all their
required fields must be there; a partial object is rejected with `400 VALIDATION_FAILED` rather than filled up with
defaults. The [API reference](https://simplefeedback.de/api/docs) shows the smallest body that validates.

**Publishing is the top-level `active` flag.** `active: true` on create or update publishes the survey;
`settings.general.active` is part of the survey body and does not.

> **Note:** Create a survey as a draft first and publish it with a separate `PATCH`. If publishing fails, you then
> know exactly which survey it was about.

**`DELETE` answers `204`** with an empty body. Everything else answers JSON.

**Retrying safely.** Every `POST` accepts an `Idempotency-Key` header: any unique string of up to 255 characters,
for example a UUID. When a request times out or the connection drops, send it again with the same key. If the first
one went through, you get its answer back with the header `Idempotent-Replayed: true`, and no second survey, QR
code or batch of codes is created. Keys are kept for 24 hours and belong to your API key. Failed requests are not remembered, so the
same key can retry them. `PATCH` and `DELETE` need no key: repeating them changes nothing further.

```bash
curl -X POST "$SF_API/v1/surveys" \
  -H "Authorization: Bearer $SF_KEY" \
  -H "Idempotency-Key: 6f1c2b0e-8d4a-4e47-9b51-2f7c0d9a3e11" \
  -H "Content-Type: application/json" \
  -d @survey.json
```

**Errors** share one shape and a fixed set of codes. Branch on `error.code`, never on the message. See
[Errors](https://simplefeedback.de/developer/docs/errors.md).

**Rate limits** are 60 requests per minute for each key and route, with the current state in the
`X-RateLimit-*` headers of every response. See [Rate limits](https://simplefeedback.de/developer/docs/rate-limits.md).
