---
title: "Change a database directly"
method: POST
path: "/sites/{id}/db/migrate"
tags: ["Sites"]
---

# Change a database directly

`POST /sites/{id}/db/migrate`

## Request

- Base URL: `https://api.crevio.co/v1`
- URL: `https://api.crevio.co/v1/sites/{id}/db/migrate`
- Auth: API key in header `Authorization`

**Structural changes belong on the development database.** Pass `environment: "development"` and reshape it as freely as you like — it holds no customer data, so there is no gate and nothing to approve. Deploying compares the two databases and applies the difference to production as one change; nobody names or numbers it.

Structural SQL aimed at production is refused (`structural_change_needs_schema`): production's shape moves only by deploying. What production accepts here is **data** — seeding rows, correcting a value, a backfill — recorded under the filename you choose, checksummed so an edited migration can't silently fail to land.

A Time Travel restore point is captured before anything runs and returned as `bookmark`. Statements that destroy data are refused unless `allow_destructive` is true, and only when the target actually holds rows.

## Path parameters

- `id` string, required

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `name` string, required: Unique migration name, e.g. `0003_add_ratings`. Used as the idempotency key in the ledger.
  - `sql` string, required: The migration SQL. Multiple statements may be separated by `;`. Runs in one batch with the ledger insert, so a failed migration is never recorded as applied.
  - `allow_destructive` boolean: Set to `true` to permit data-destroying statements (DROP/TRUNCATE/unqualified DELETE|UPDATE) against tables that hold rows. Defaults to `false`.
  - `environment` 'development' | 'production': Which database to change. `development` is the one you build against — created on first use, and where every structural change belongs. `production` is the live site's, and accepts data changes only. Defaults to `production`.

## Response `200`

Migration result

- object
  - `object` string
  - `name` string
  - `applied` boolean
  - `already_applied` boolean
  - `destructive` boolean
  - `bookmark` string, nullable
  - `destructive_statements` object[]: The statements that were classified as destructive, each with the `table` it targets and that table's `row_count`.
  - `risks` object[]: Statements that keep every row but can still fail against existing data or break the currently live version of the site (column renames, NOT NULL without DEFAULT, UNIQUE indexes). Reported, never blocked.
  - `checksum` string, nullable: SHA-256 of the migration SQL, recorded in the ledger to detect later edits to the file.

## Other responses

- `400`: Bad request
- `401`: Authentication required
- `404`: Resource not found
- `409`: Conflict
- `422`: Validation error
- `429`: Rate limit exceeded — 600 requests per minute per credential. Retry after the number of seconds in `Retry-After`.

---

[API](https://skmtc.dev/crevio/apis/crevio-api-v1.md) · [All operations](https://skmtc.dev/crevio/apis/crevio-api-v1/llms.txt) · [OpenAPI document](https://skmtc.dev/crevio/apis/crevio-api-v1/revisions/2219f18ad651?raw)
