Built-in kits

Every Space automatically sees 3 built-in kits, visible to every Space and immutable via this API: todo-list (Users who own Lists of Tasks), blog (Authors writing Posts that readers Comment on), and ecommerce (Customers placing Orders of Products, tracked via Order Items). A Space can also author its own custom kits, visible only to that Space. A local operator can add further global kits (see "Create a global starter kit" below).

The definition shape

A kit's definition cross-references its own Types and Relationships by a local string key (typeKey/relationshipKey) instead of a real id, since none exist until the kit is applied to a Project. It follows the exact same field shapes as the manual Data Types, Relationships, and Mock Routes create requests.

Example definition

{
  "types": [
    {
      "typeKey": "user",
      "name": "User",
      "routeName": "users",
      "schema": {
        "id": {
          "type": "number"
        },
        "email": {
          "type": "string",
          "format": "email"
        }
      },
      "identifierField": "id",
      "uniqueFields": [
        "email"
      ],
      "generation": {
        "recordCount": 5
      }
    },
    {
      "typeKey": "list",
      "name": "List",
      "routeName": "lists",
      "schema": {
        "id": {
          "type": "number"
        },
        "userId": {
          "type": "number"
        }
      },
      "generation": {
        "recordCount": 8
      }
    }
  ],
  "relationships": [
    {
      "relationshipKey": "list-user",
      "sourceTypeKey": "list",
      "sourceField": "userId",
      "targetTypeKey": "user",
      "targetField": "id"
    }
  ],
  "routes": [
    {
      "method": "GET",
      "path": "/lists",
      "typeKey": "list"
    },
    {
      "method": "GET",
      "path": "/users/{id}/lists",
      "typeKey": "list",
      "relationshipKey": "list-user"
    }
  ]
}

Saving a kit (create/update) runs a structural, database-free validation pass over this shape - the same rules a manual Type/Relationship/Route create request is checked against, just resolved against the definition's own arrays instead of live database rows. Applying a kit always re-runs the real, database-aware validation regardless, so correctness never depends on this pass alone.

GET
/admin/spaces/{spaceId}/starter-kits
List starter kits

Returns the 3 built-in kits plus any custom kits this Space has authored. Requires any resolvable Space role (viewer included).

POST
/admin/spaces/{spaceId}/starter-kits
Create a custom starter kit

Requires Space owner or admin. key must be lowercase letters/numbers/hyphens and unique within the Space. Returns 422 for a structurally invalid definition, 409 if key is already used in this Space.

Request

{
  "key": "notes",
  "name": "Notes",
  "description": "A single flat Note type",
  "definition": {
    "types": [
      {
        "typeKey": "note",
        "name": "Note",
        "routeName": "notes",
        "schema": {
          "id": {
            "type": "number"
          }
        },
        "generation": {
          "recordCount": 10
        }
      }
    ]
  }
}

Response (201)

{
  "id": 4,
  "key": "notes",
  "name": "Notes",
  "description": "A single flat Note type",
  "isSystem": false
}
PATCH / DELETE
/admin/spaces/{spaceId}/starter-kits/{starterKitId}
Update or delete a custom starter kit

Requires Space owner or admin. Returns 403 against any of the 3 built-in kits - they're immutable via this API. Deleting a kit is safe at any time: applying a kit only ever copies its definition into a Project once, so no durable link back to the kit exists afterward.

POST
/admin/starter-kits
Create a global starter kit (local development only)

A global kit (space_id NULL) belongs to no Space, is visible to every Space immediately, and is always is_system (immutable, same as the 3 built-in kits) - so it needs a different authorization check than the Space-scoped create endpoint above, which checks a caller's role within a specific Space. This service has no separate "platform admin" identity concept, so this endpoint reuses the one bypass that already exists for exactly this kind of local-operator action: it only succeeds when AUTH_MODE=mock, and returns 403 otherwise. Never enable AUTH_MODE=mock in a shared/deployed environment - doing so also opens this endpoint there. Same request/response shape as Create a custom starter kit above, minus spaceId. There is no corresponding update/delete endpoint - removing a global kit created this way requires direct database access.

POST
/admin/projects/{projectId}/starter-kit
Apply a starter kit

Requires effective Project editor or admin. starterKitId must be a built-in kit or belong to the Project's own Space. The target Project must have no existing Data Types - returns 409 otherwise, since applying a kit is all-or-nothing (no merging into an existing data model). Creates every Type, then every Relationship, then every Route from the kit's definition in one transaction; any failure rolls back the entire kit. Only scaffolds structure - it does not generate mock data (see Master Template docs) or create an API key (see API Keys docs), both of which remain separate follow-up steps.

Request

{
  "starterKitId": 1
}

Response (201)

{
  "starterKitId": 1,
  "projectId": 42,
  "typesCreated": [
    {
      "id": 101,
      "typeKey": "user",
      "name": "User",
      "routeName": "users"
    }
  ],
  "relationshipsCreated": [
    {
      "id": 12,
      "relationshipKey": "list-user"
    }
  ],
  "routesCreated": [
    {
      "id": 55,
      "method": "GET",
      "path": "/lists"
    }
  ]
}