# API versioning

> How Done Bear versions the REST surface, evolves the GraphQL schema, marks retiring fields with @deprecated, and sunsets documented fields.

Done Bear does not put a version in the URL. There is one API origin, `https://api.donebear.com`, and one GraphQL schema at `/graphql`. Breaking changes are announced, then marked, then removed.

## REST versioning

The version travels in a header.

- Send `Api-Version: 1` to pin a version. Omit it to get the current one.
- Every response carries `Api-Version`, including errors.
- A version this server does not serve is refused with `400 unsupported_api_version`, listing what it does serve, rather than being answered by a version you did not ask for.

```bash
curl -sS -D- -o/dev/null https://api.donebear.com/health -H "Api-Version: 1"
```

The same declaration is machine-readable in [the OpenAPI document](https://donebear.com/openapi.json): every operation takes the `Api-Version` header parameter, and the top-level `x-versioning` object names the current version, the supported versions, the deprecation headers, and the notice period.

## REST deprecation

A retiring operation or version gains, at least 90 days before it stops answering:

- `Deprecation` ([RFC 9745](https://www.rfc-editor.org/rfc/rfc9745.html)) — the date the deprecation took effect.
- `Sunset` ([RFC 8594](https://www.rfc-editor.org/rfc/rfc8594.html)) — the earliest date the operation may stop answering.
- `Link` with `rel="deprecation"` — the note explaining what replaces it.

Nothing on the REST surface is deprecated today, so no response carries those headers yet. Watch for them rather than polling the changelog.

## GraphQL stability

Fields and operations documented on [GraphQL reference](/api/graphql) are stable. Undocumented schema details may change without notice.

## GraphQL deprecation

When a field is retiring, it stays in the schema with GraphQL `@deprecated` and a reason that names the replacement.

Example: `Task.notes` is deprecated. Use `Task.description`. The CLI flag is `--description`.

Introspection hides deprecated fields unless you ask for them, so a plain `fields { name }` query reports none. Ask for them:

```graphql
{
  __type(name: "Task") {
    fields(includeDeprecated: true) {
      name
      isDeprecated
      deprecationReason
    }
  }
}
```

## GraphQL sunset

A deprecated field remains queryable for at least 90 days after the deprecation reason lands in the schema. After that window it may be removed. Removals of previously documented query fields are listed in the API changelog and guarded by the schema test that tracks retired names.

## Rate limits and cost

The `Query` and `Mutation` types carry a `@rateLimit(limit: 100, durationSeconds: 60)` directive. That matches the API origin's per-IP ceiling (100 requests per 60 seconds). Query depth is capped at 10. The hosted MCP server has a separate per-token limit of 120 requests per minute.

There is no per-field query-cost budget beyond that depth cap.

The whole schema is also served as SDL at [`https://api.donebear.com/graphql/schema.graphql`](https://api.donebear.com/graphql/schema.graphql), where `@deprecated(reason: ...)` is inline and readable with a `GET`.

## Next steps

- [GraphQL reference](/api/graphql)
- [API scopes](/api/scopes)
- [OpenAPI specification](https://donebear.com/openapi.json)

---

Every Done Bear page is listed at https://donebear.com/llms.txt. Any page has a markdown copy at its URL plus `.md`. Start from the map rather than guessing a path.
