Migrations
The migrations endpoint accepts a set of migrations, lints them, and optionally
applies them to an environment. The lint step is always performed first —
apply: false is the safe default for reviewing a migration plan before
committing it.
Endpoint
POST /v1/organizations/{org}/projects/{project}/environments/{env}/migrations
Request body:
{
"migrations": [
{
"version": "1",
"name": "init",
"up": "CREATE TABLE orders (id uuid primary key);",
"down": "DROP TABLE orders;"
},
{
"version": "2",
"name": "add_user_id",
"up": "ALTER TABLE orders ADD COLUMN user_id uuid NOT NULL;"
}
],
"apply": false
}Set apply: true to execute the migrations after lint passes.
Response 200 OK (lint only):
{
"plan": [
{
"version": "1",
"name": "init",
"direction": "up",
"findings": []
},
{
"version": "2",
"name": "add_user_id",
"direction": "up",
"findings": [
{
"severity": "warning",
"message": "Adding a NOT NULL column without a default to a large table may cause a full rewrite."
}
]
}
],
"applied": false
}Response 200 OK (applied):
{
"plan": [...],
"applied": true,
"applied_at": "2026-01-01T12:00:00Z"
}Lint findings
| Severity | Meaning |
|---|---|
error |
Migration will fail or cause data loss. Blocked — apply cannot proceed. |
warning |
Migration may cause problems at scale or violate best practices. |
info |
Informational note. |
Migration version ordering
Migrations are ordered by their version field parsed as a number. The server
applies them in ascending order and skips already-applied versions.
CLI shortcut
# Lint only
scribase migrate acme store production --dir ./migrations
# Apply
scribase migrate acme store production --dir ./migrations --applySee scribase migrate for directory and file formats.