Schema Migration
Overview
The schema migration endpoints change a project's data model over the REST API. A migration is an ordered list of operations that create, rename, update, or delete the building blocks of the model: namespaces, collections, fields, indexes, and relations.
Two endpoints cover the full workflow. One applies a migration you author; the other imports changes made directly in a data source.
- namespace — a schema in the underlying data source, grouping related collections
- collection — a container for items of the same type
- field — a named value on an item
- data source — a database Monospace connects to, identified by its ID
The Two Endpoints
| Method | Endpoint | Purpose |
|---|---|---|
POST | /api/{project}/schema/migrate | Apply a migration you author. |
POST | /api/{project}/schema/introspect/{source} | Import schema changes made directly in a data source. |
Pick the endpoint by where the change originated.
| Where the change originated | Endpoint | Guide |
|---|---|---|
| You designed it (add a collection, drop a field, create an index) | /schema/migrate | Apply Migrations |
| The data source changed outside Monospace (a collection or field added in SQL) | /schema/introspect/{source} | Import Changes |
Authentication
Both endpoints require the DataModel edit entitlement on the project. Send an access token that carries it in the Authorization header. Project admins hold this entitlement by default. See Access Control for how entitlements are granted.
Authorization: Bearer YOUR_API_KEY
Migration Format
Every operation is adjacently tagged: a kind string names the operation and a data object carries its payload.
{
"kind": "createCollection",
"data": { "...": "..." }
}
Operations nest. createCollection and updateCollection hold field and index operations in their own data.operations array, and updatePrimitiveField holds field-change operations. The Operation Reference documents every kind.
New entities carry a client-generated id — a UUID you mint and reuse to reference the entity in later operations. Existing entities use the IDs already in the schema.
Finding IDs
Operations reference the data source and existing entities by ID. Mint a fresh UUID for anything you create; look up the rest.
The data source ID — used as sourceId and as the {source} path parameter — is returned when you create a source through POST /api/{project}/sources/data. The response carries it at data.id:
{ "data": { "id": "d7c8f0e2-9b3a-4c1e-8f2a-1a2b3c4d5e6f" } }
IDs of existing collections, fields, namespaces, indexes, and relations are readable through the system collections that hold the data model, using the standard item API:
| Read | Item API path |
|---|---|
| Data sources | GET /api/blog/items/MonospaceDataSource |
| Namespaces | GET /api/blog/items/MonospaceNamespace |
| Collections | GET /api/blog/items/MonospaceCollection |
| Fields | GET /api/blog/items/MonospacePrimitiveField |
| Relation fields | GET /api/blog/items/MonospaceSingleRelationField |
| Indexes | GET /api/blog/items/MonospaceIndex |
There is no list endpoint under /sources/data — read MonospaceDataSource to enumerate existing sources.
How Migrations Apply
Applying a migration is a two-phase operation guarded by a per-project write lock. Monospace first persists the updated model to its system database inside a transaction, then applies the changes to the data source. If the data source step fails, Monospace rolls back the system transaction.
Monospace does not provide a rollback guarantee for data source changes. A failed migration can leave a data source partially changed, including a migration that targets one source. Re-introspect the source before sending another migration. A migration that spans multiple data sources can also leave sources in different states.
The write lock serializes schema changes within a project. Migrations on different projects run in parallel; migrations on the same project queue. See Concurrent Migrations for the single-instance requirement.
Errors
The endpoints can return these status codes.
| Status | Meaning |
|---|---|
401 | The access token is invalid or expired. |
403 | The caller lacks the DataModel edit entitlement. |
404 | The project does not exist. /schema/introspect/{source} also returns 404 when the source does not exist. |
422 | The /schema/migrate request JSON is malformed, has unknown or empty fields, or contains an invalid API name. The response body names the offending path for JSON errors. |
413 | The /schema/migrate request body exceeds the configured size limit. |
500 | The migration or introspection failed to execute. Semantic checks applied while the migration runs — such as an invalid default value — also return 500. |
A 422 response points at the exact field that failed:
{
"message": "Failed to deserialize the JSON body into the target type: operations[0]: data.dbName: Value must not be empty at line 1 column 174"
}
See Also
- Apply Migrations — author and send a migration with
/schema/migrate - Import Changes — reconcile a data source with
/schema/introspect/{source} - Operation Reference — every operation kind, grouped by domain
- Concurrent Migrations — why only one instance may run migrations
- OpenAPI Specification — the generated spec that reflects your latest migrations