API reference
Base URL https://schemadrawer.com/api/v1. Two endpoints, JSON answers, no authentication. The API also describes itself at /v3/api-docs (OpenAPI 3) and /swagger-ui.html.
POST /drawing
Uploads the migration files and queues the drawing. Answers 202 Accepted with the job id. The request is multipart/form-data; every field other than files may be sent as a form field or as a query parameter.
Headers
| Header | Required | Description |
|---|---|---|
Request-Id | yes | A UUID chosen by the client for this drawing. A request repeated with the same id returns the job the first one created instead of drawing again, so retries and double clicks are safe. |
Form fields
| Field | Type | Description |
|---|---|---|
files | file, repeated | The migration files, laid out as the tool expects them. The part's file name is the path the tool will see, so send dir/file.sql as the file name to keep a directory structure. Names must be unique; content is read as UTF-8 text. |
userId | UUID | The user the drawing belongs to. Any UUID you choose; a user unknown so far is created. Keep one per project. |
migrationType | enum | The migration tool that runs the files; one of the MigrationType values. |
databaseType | enum | The engine; one of the DatabaseType values. |
version | string | The engine's version, one of those it is offered in (for example 18 for PostgreSQL, 8.4 for MySQL, 2022 for SQL Server, latest for SQLite). |
Example
curl -X POST https://schemadrawer.com/api/v1/drawing \
-H "Request-Id: $(uuidgen)" \
-F "userId=5f0c1d2e-3a4b-4c5d-8e6f-7a8b9c0d1e2f" \
-F "migrationType=FLYWAY" \
-F "databaseType=POSTGRESQL" \
-F "version=18" \
-F "files=@db/migration/V1__create_owners_and_pets.sql" \
-F "files=@db/migration/V2__create_vets.sql" \
-F "files=@db/migration/V3__create_visits.sql"
# {"jobId":"0b6e2f4c-8d1a-4c3e-9f7a-2a5b6c7d8e9f"}
Responses
| Status | Meaning |
|---|---|
202 | The drawing was queued. Body: JobCreated. |
400 | A parameter or file is missing or invalid, the version is not offered, two files share a name, or too many parts were sent (the limit is 2000). The message says which. |
413 | The upload exceeds 100 MB. |
500 | Something failed inside the service. Retry with the same Request-Id. |
{"jobId": "0b6e2f4c-8d1a-4c3e-9f7a-2a5b6c7d8e9f"}
GET /drawing/{jobId}
The job as it stands. Poll it every two or three seconds until status is SUCCESS or ERROR.
| Field | Description |
|---|---|
jobId | The id the drawing was queued under. |
status | PENDING while queued, IN_PROGRESS while drawn, then SUCCESS or ERROR. |
createdAt | When the drawing was queued, ISO 8601 in UTC. |
errorMessage | Why the job failed: the migration tool's own output (up to 10,000 characters), a message saying the drawing did not finish within 120 seconds, or timeout for a job that waited in the queue longer than 10 minutes. null otherwise. |
diagramUrl | A link to the SVG, valid for one hour. Only for SUCCESS; null otherwise. |
Examples
curl https://schemadrawer.com/api/v1/drawing/0b6e2f4c-8d1a-4c3e-9f7a-2a5b6c7d8e9f
{
"jobId": "0b6e2f4c-8d1a-4c3e-9f7a-2a5b6c7d8e9f",
"status": "PENDING",
"createdAt": "2026-09-25T08:00:00Z",
"errorMessage": null,
"diagramUrl": null
}
{
"jobId": "0b6e2f4c-8d1a-4c3e-9f7a-2a5b6c7d8e9f",
"status": "SUCCESS",
"createdAt": "2026-09-25T08:00:00Z",
"errorMessage": null,
"diagramUrl": "https://.../0b6e2f4c-8d1a-4c3e-9f7a-2a5b6c7d8e9f/diagram.svg?X-Amz-Expires=3600&..."
}
{
"jobId": "7d2e9a10-4c6b-4f1e-9b3a-5e8d1c2f3a4b",
"status": "ERROR",
"createdAt": "2026-09-25T08:03:12Z",
"errorMessage": "Flyway migration failed (exit code 1).\nOutput:\nFlyway OSS Edition 13.7.0 by Redgate\n...\nMigrating schema \"public\" to version \"1 - create owners\"\nErrors:\nERROR: Migration of schema \"public\" to version \"1 - create owners\" failed! Changes successfully rolled back.\nERROR: Failed to execute script V1__create_owners.sql\n----------------------------------------------\nSQL State : 42601\nMessage : ERROR: syntax error at or near \"last_name\"\n Position: 100\nLocation : /flyway/sql/V1__create_owners.sql\nLine : 1",
"diagramUrl": null
}
404 is answered for an unknown job id.
Error format
Every error is answered in one JSON shape, with a message written for the person calling:
{
"timestamp": "2026-09-25T08:00:00Z",
"status": 400,
"error": "Bad Request",
"message": "Unsupported POSTGRESQL version: 9. Supported versions: 10, 11, 12, 13, 14, 15, 16, 17, 18",
"path": "/api/v1/drawing"
}
{
"timestamp": "2026-09-25T08:00:00Z",
"status": 413,
"error": "Content Too Large",
"message": "Uploaded files exceed the maximum allowed size of 100MB.",
"path": "/api/v1/drawing"
}
MigrationType
| Value | Tool | Engines |
|---|---|---|
FLYWAY | Flyway | PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, SQLite |
LIQUIBASE | Liquibase | PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, SQLite |
GOLANG_MIGRATE | golang-migrate | PostgreSQL, MySQL, MariaDB, SQL Server |
ALEMBIC | Alembic | PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, SQLite |
GOOSE | goose | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite |
DBMATE | dbmate | PostgreSQL, MySQL, MariaDB, SQLite |
ATLAS | Atlas | PostgreSQL, MySQL, MariaDB, SQLite |
PRISMA | Prisma Migrate | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite |
DBUP | DbUp | PostgreSQL, MySQL, MariaDB, SQL Server, Oracle |
FLUENT_MIGRATOR | FluentMigrator | PostgreSQL, MySQL, MariaDB, SQL Server, Oracle |
DOCTRINE | Doctrine Migrations | PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, SQLite |
LARAVEL | Laravel | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite |
ACTIVE_RECORD | ActiveRecord (Rails) | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite |
DJANGO | Django | PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, SQLite |
DRIZZLE | Drizzle ORM | PostgreSQL, MySQL, MariaDB, SQLite |
SQITCH | Sqitch | PostgreSQL, MySQL, MariaDB, Oracle, SQLite |
DIESEL | Diesel | PostgreSQL, MySQL, MariaDB, SQLite |
EF_CORE | EF Core | PostgreSQL, MySQL, MariaDB, SQL Server |
MIKRO_ORM | MikroORM | PostgreSQL, MySQL, MariaDB, SQL Server |
KNEX | Knex.js | PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, SQLite |
SEQUELIZE | Sequelize | PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, SQLite |
TYPEORM | TypeORM | PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, SQLite |
Django is offered from PostgreSQL 15, MySQL 8.4 and MariaDB 11.4 upwards.
DatabaseType and versions
| Value | Engine | Versions |
|---|---|---|
POSTGRESQL | PostgreSQL | 10, 11, 12, 13, 14, 15, 16, 17, 18 |
MYSQL | MySQL | 8.0, 8.4, 9 |
MARIADB | MariaDB | 10.6, 11.4, 11.8, 12 |
MSSQL | Microsoft SQL Server | 2017, 2019, 2022, 2025 |
ORACLE | Oracle Database | 21, 23 |
SQLITE | SQLite | latest |
Limits
- 100 MB per upload, 2000 parts (files and form fields together).
- 120 seconds per drawing; 10 minutes maximum wait in the queue.
- Diagram links expire after one hour.
- The service is free and unauthenticated while in early access; please poll no more often than every two seconds and keep one
userIdper project.
Idempotency and retries
The Request-Id header makes POST /drawing idempotent: the same id always maps to the same job, whatever else the request holds. Derive it from something stable, such as a CI run id, when you want a retried pipeline step to pick up the drawing it already queued. Use a fresh UUID when you want a new drawing of changed files.
Draw your schema in the next five minutes
One HTTP call with your migration files, one poll, one SVG. Free while we build it.