---
title: "Schedule a downgrade to Free"
method: POST
path: "/api/billing/downgrade"
tags: ["Billing"]
---

# Schedule a downgrade to Free

`POST /api/billing/downgrade`

## Request

- Base URL: `https://trydock.ai`
- URL: `https://trydock.ai/api/billing/downgrade`
- Auth: HTTP bearer

Schedule a downgrade to Free at the end of the current billing period. The org keeps its current plan + caps until then. Idempotent on already-Free orgs.

Same human-vs-agent split as `/api/billing/upgrade`: humans execute directly; agents get `{ status: "approval_required", approval_url }` for the org owner to approve. `confirm_token` and `mode` are ignored.

## Request body

- object
  - `confirm_token` string
  - `mode` 'chat' | 'web'

## Response `200`

Discriminated by `status`: `downgrade-scheduled` | `already-free` | `approval_required`.

- object
  - `plan` 'free' | 'pro' | 'scale': Plan tier. Caps live in `src/lib/plan.ts`.
  - `status` string, required
  - `message` string
  - `approval_url` string, uri
  - `polling_url` string, uri
  - `expires_at` string

## Other responses

- `400`: Invalid confirm_token.

## Changes

- **2026-11-05** `767beae124a8`: 3 warning, 3 info
  - removed the optional property `confirm_token` from the response with the `200` status
  - removed the optional property `expires_in` from the response with the `200` status
  - removed the optional property `url` from the response with the `200` status
  - added the optional property `approval_url` to the response with the `200` status
  - …2 more

[Operation history](https://skmtc.dev/trydock/apis/dock-rest-api/changes/api/billing/downgrade/post.md)

---

[API](https://skmtc.dev/trydock/apis/dock-rest-api.md) - [All operations](https://skmtc.dev/trydock/apis/dock-rest-api/llms.txt) - [OpenAPI document](https://skmtc.dev/trydock/apis/dock-rest-api/revisions/dd96ae96a412?raw)
