---
title: "Rotate Write Token"
method: POST
path: "/v1/projects/{project_id}/write-tokens/{token_id}/rotate/"
tags: ["Projects"]
---

# Rotate Write Token

`POST /v1/projects/{project_id}/write-tokens/{token_id}/rotate/`

## Request

- Base URL: `https://logfire-api.pydantic.dev/api`
- URL: `https://logfire-api.pydantic.dev/api/v1/projects/{project_id}/write-tokens/{token_id}/rotate/`
- Auth: OAuth 2
  - Authorization code: `api/oauth/authorize`
  - Token URL: `/api/oauth/token` scopes: `project:write_token`
- TypeScript: <https://skmtc.dev/pydantic/apis/pydantic-logfire-api/docs/v1/projects/:project_id/write-tokens/:token_id/rotate/post.ts> (a function with typed inputs; its response is checked against the schema)

Rotate a write token: expire the existing token and create a new one atomically.

By default the old token stops working immediately. Pass `grace_period_hours` to keep it working
while the replacement is deployed, which is what a credential baked into a running deployment
needs, since that deployment has to be rolled out before the old token can stop. During that
window both tokens work, and `previous_token_expires_at` says when that stops being true.

A grace period only ever shortens the outgoing token's life. If it was already going to expire
before the window ran out, that earlier expiry stands: this route retires a credential, so it
never hands one more time than it was granted.

The replacement is minted inside the rotation rather than here, because what it has to look like
(whether it is public, its scopes) is a property of the token being rotated and is not known
until that row has been read.

A token belonging to a frontend application is refused, as it already is by the expire and revoke
routes beside this one. Those credentials live in a shipped browser bundle, and replacing one is
two steps taken on the customer's own timing: add a second token, redeploy, then revoke the
first. A grace period would instead retire a published credential on a clock this API sets.

## Path parameters

- `token_id` string, uuid, required
- `project_id` string, uuid, required

## Request body

- RotateWriteTokenInput
  - `grace_period_hours` integer, nullable

## Response `201`

Successful Response

- RotateWriteTokenOutput
  - `id` string, uuid, required
  - `project_id` string, uuid, required
  - `created_at` string, date-time, required
  - `description` string, nullable, required
  - `expires_at` string, date-time, nullable, required
  - `token` string, required
  - `previous_token_expires_at` string, date-time, required

## Other responses

- `400`: Cannot rotate a token belonging to a frontend application
- `404`: Project token not found
- `422`: The requested grace period is not positive, or is longer than 7 days
- `429`: Rate limit exceeded

---

[API](https://skmtc.dev/pydantic/apis/pydantic-logfire-api.md) - [All operations](https://skmtc.dev/pydantic/apis/pydantic-logfire-api/llms.txt) - [OpenAPI document](https://skmtc.dev/pydantic/apis/pydantic-logfire-api/revisions/7996e45256f4?raw)
