Skip to content

Schema engine endpoints

18 endpoints at https://api.vaif.studio, generated from the API's OpenAPI document. The REST API page covers authentication, and All endpoints lists every group.

POST /schema-engine/analyze/{projectId}

Analyze SQL statements without executing them

ParameterInTypeRequired
envIdquerystringNo
projectIdpathstring (uuid)Yes
Request body (application/json, required)
{
  sql: string;
}
Response 200 (application/json)
{
  readOnly: boolean;
  destructive: boolean;
  ddl: boolean;
  statements: {
    sql: string;
    kind: string;
    readOnly: boolean;
    destructive: boolean;
    ddl: boolean;
    temporaryTable?: boolean;
  }[];
}

Errors: 400, 401, 403, 404, with the error body.

POST /schema-engine/preview

Preview schema migration changes

Request body (application/json, required)
{
  projectId: string (uuid);
  envId?: string (uuid);
  definition: {
    schemaVersion: "1.0";
    tables: {
      name: string;
      columns: {
        name: string;
        type: "uuid" | "text" | "varchar" | "string" | "int" | "integer" | "bigint" | "boolean" | "bool" | "jsonb" | "json" | "timestamptz" | "timestamp" | "date" | "numeric" | "decimal" | "float" | "double" | "text[]" | "integer[]";
        primaryKey?: boolean;
        unique?: boolean;
        nullable?: boolean;
        default?: string | number | boolean;
        references?: {
          table: string;
          column?: string;
          onDelete?: "CASCADE" | "RESTRICT" | "SET NULL" | "SET DEFAULT" | "NO ACTION";
          onUpdate?: "CASCADE" | "RESTRICT" | "SET NULL" | "SET DEFAULT" | "NO ACTION";
        };
      }[];
      indexes?: {
        name: string;
        columns: string[];
        unique?: boolean;
      }[];
      unique?: {
        name: string;
        columns: string[];
      }[];
      publicRead?: boolean;
      ownerColumn?: string;
    }[];
    rawSql?: any;
  };
  allowDestructive?: boolean;
  allowProduction?: boolean;
  allowEmpty?: boolean;
}
Response 200 (application/json)
{
  ok: true;
  projectSchema: string;
  plan: any[];
  upSql: string;
  downSql?: string | null;
  warnings?: any[];
  destructive?: boolean[];
  hasDestructive?: boolean;
  planHash?: string;
}

Errors: 400, 401, 403, 404, 409, 500, with the error body.

POST /schema-engine/apply

Apply schema migration

Request body (application/json, required)
{
  projectId: string (uuid);
  envId?: string (uuid);
  definition: {
    schemaVersion: "1.0";
    tables: {
      name: string;
      columns: {
        name: string;
        type: "uuid" | "text" | "varchar" | "string" | "int" | "integer" | "bigint" | "boolean" | "bool" | "jsonb" | "json" | "timestamptz" | "timestamp" | "date" | "numeric" | "decimal" | "float" | "double" | "text[]" | "integer[]";
        primaryKey?: boolean;
        unique?: boolean;
        nullable?: boolean;
        default?: string | number | boolean;
        references?: {
          table: string;
          column?: string;
          onDelete?: "CASCADE" | "RESTRICT" | "SET NULL" | "SET DEFAULT" | "NO ACTION";
          onUpdate?: "CASCADE" | "RESTRICT" | "SET NULL" | "SET DEFAULT" | "NO ACTION";
        };
      }[];
      indexes?: {
        name: string;
        columns: string[];
        unique?: boolean;
      }[];
      unique?: {
        name: string;
        columns: string[];
      }[];
      publicRead?: boolean;
      ownerColumn?: string;
    }[];
    rawSql?: any;
  };
  migrationName?: string;
  allowDestructive?: boolean;
  allowProduction?: boolean;
  allowEmpty?: boolean;
  expectedPlanHash?: string;
  fromAiWorkspace?: boolean;
}
Response 200 (application/json)
{
  ok: true;
  projectSchema: string;
  migration: {
    id: string;
    name: string;
    status: string;
    appliedAt: string | string (date-time);
  };
  appliedSteps: number;
  snapshotId?: string;
}

Errors: 400, 401, 403, 404, 409, 500, with the error body.

GET /schema-engine/introspect/{projectId}

Introspect current database schema for a project

ParameterInTypeRequired
envIdquerystringNo
projectIdpathstring (uuid)Yes
Response 200 (application/json)
{
  ok: true;
  schemaExists: boolean;
  schemaName?: string;
  supportedColumnTypes: ("uuid" | "text" | "varchar" | "string" | "int" | "integer" | "bigint" | "boolean" | "bool" | "jsonb" | "json" | "timestamptz" | "timestamp" | "date" | "numeric" | "decimal" | "float" | "double" | "text[]" | "integer[]")[];
  tables: {
    name: string;
    columns: {
      name: string;
      type: string;
      primaryKey: boolean;
      nullable: boolean;
      unique: boolean;
      default: string | null;
      udtName?: string;
      enumValues?: string[];
      comment?: string | null;
      formattedType?: string;
      references?: {
        table: string;
        column: string;
        onDelete?: "CASCADE" | "RESTRICT" | "SET NULL" | "SET DEFAULT" | "NO ACTION";
        onUpdate?: "CASCADE" | "RESTRICT" | "SET NULL" | "SET DEFAULT" | "NO ACTION";
      };
    }[];
    indexes: string[];
    indexDetails: {
      name: string;
      columns: string[];
      unique: boolean;
      method?: string;
      where?: string | null;
      expression?: boolean;
      valid?: boolean;
    }[];
    primaryKey: string[];
    foreignKeys: {
      constraintName: string;
      columnName: string;
      refTable: string;
      refColumn: string;
      onDelete?: "CASCADE" | "RESTRICT" | "SET NULL" | "SET DEFAULT" | "NO ACTION";
      onUpdate?: "CASCADE" | "RESTRICT" | "SET NULL" | "SET DEFAULT" | "NO ACTION";
    }[];
    uniqueConstraints: {
      constraintName: string;
      columns: string[];
    }[];
    rlsEnabled: boolean;
    policies: {
      name: string;
      command: string;
      permissive: boolean;
      roles: string[];
      using?: string;
      withCheck?: string;
    }[];
    triggers: {
      name: string;
      table: string;
      definition?: string;
      enabled: string;
    }[];
    estimatedRows?: number;
    comment?: string | null;
  }[];
  enums?: {
    name: string;
    values: string[];
  }[];
  views?: {
    name: string;
    definition?: string;
    materialized: boolean;
    columns: {
      name: string;
      type: string;
      primaryKey: boolean;
      nullable: boolean;
      unique: boolean;
      default: string | null;
      udtName?: string;
      enumValues?: string[];
      comment?: string | null;
      formattedType?: string;
      references?: {
        table: string;
        column: string;
        onDelete?: "CASCADE" | "RESTRICT" | "SET NULL" | "SET DEFAULT" | "NO ACTION";
        onUpdate?: "CASCADE" | "RESTRICT" | "SET NULL" | "SET DEFAULT" | "NO ACTION";
      };
    }[];
  }[];
  functions?: {
    name: string;
    arguments: string;
    resultType: string;
    language: string;
    definition?: string;
  }[];
  definitionsHidden?: boolean;
  triggers?: {
    name: string;
    table: string;
    definition?: string;
    enabled: string;
  }[];
  compositeTypes?: {
    name: string;
    attributes: {
      name: string;
      type: string;
    }[];
  }[];
}

Errors: 400, 401, 403, 404, 500, with the error body.

GET /schema-engine/migrations/project/{projectId}

List migrations for a project

ParameterInTypeRequired
envIdquerystringNo
limitquerynumberNo
offsetquerynumberNo
projectIdpathstring (uuid)Yes
Response 200 (application/json)
{
  ok: true;
  migrations: any[];
}

Errors: 400, 401, 403, 404, with the error body.

GET /schema-engine/{projectId}/changes

Get schema change history for a project

ParameterInTypeRequired
projectIdpathstring (uuid)Yes
limitquerystringNo
Response 200 (application/json)
{
  ok: true;
  data: any[];
}

Errors: 400, 401, 403, with the error body.

POST /schema-engine/query/{projectId}

Execute SQL query against project database (single OR multi-statement)

ParameterInTypeRequired
envIdquerystringNo
projectIdpathstring (uuid)Yes
Request body (application/json, required)
{
  sql: string;
  editorSessionToken?: string;
  timeoutMs?: number;
  rowLimit?: number;
  params?: any[];
  allowProduction?: boolean;
  readOnly?: boolean;
}
Response 200 (application/json)
{
  ok: true;
  executionTimeMs: number;
  rows?: any[];
  rowValues?: any[][];
  rowCount?: number | null;
  command?: string;
  fields?: {
    name: string;
    dataTypeID: number;
  }[];
  truncated?: boolean;
  rowLimit?: number;
  results?: {
    rows: any[];
    rowValues?: any[][];
    rowCount: number | null;
    command?: string;
    truncated?: boolean;
    rowLimit?: number;
    executionTimeMs?: number;
    fields?: {
      name: string;
      dataTypeID: number;
    }[];
  }[];
  statementCount?: number;
}

Errors: 400, 401, 403, 404, 500, with the error body.

GET /schema-engine/migrations/{projectId}/{migrationId}/diff

Get diff between pre-migration snapshot and current schema

ParameterInTypeRequired
envIdquerystringNo
projectIdpathstring (uuid)Yes
migrationIdpathstring (uuid)Yes
Response 200 (application/json)
{
  ok: true;
  migrationId: string;
  snapshotId: string | null;
  diff: {
    entries: {
      table: string;
      column?: string;
      kind: string;
      destructive: boolean;
      detail?: string;
    }[];
    destructiveCount: number;
  };
  rollbackHasDestructive?: boolean;
  rollbackPlan?: any[];
  rollbackSql?: string;
  rollbackPlanHash?: string;
}

Errors: 400, 401, 403, 404, 500, with the error body.

POST /schema-engine/migrations/{projectId}/{migrationId}/rollback

Roll back a migration using its pre-apply snapshot

ParameterInTypeRequired
projectIdpathstring (uuid)Yes
migrationIdpathstring (uuid)Yes
Request body (application/json, required)
any | {
  envId?: string (uuid);
  allowDestructive?: boolean;
  allowProduction?: boolean;
  expectedPlanHash?: string;
} | null
Response 200 (application/json)
{
  ok: true;
  migrationId: string;
  snapshotId: string;
  appliedSteps: number;
  warning?: string;
}

Errors: 400, 401, 403, 404, 409, 500, with the error body.

GET /schema-engine/snapshots/project/{projectId}

List migration snapshots for a project

ParameterInTypeRequired
projectIdpathstring (uuid)Yes
limitquerystringNo
Response 200 (application/json)
{
  ok: true;
  snapshots: any[];
}

Errors: 400, 401, 403, with the error body.

GET /schema-engine/editor-sessions/{projectId}/capabilities

ParameterInTypeRequired
projectIdpathstring (uuid)Yes
Response 200 (application/json)
{
  enabled: boolean;
  idleTimeoutMs: 600000;
  maxPerUser: 2;
  maxPerProject: 4;
}

Errors: 400, 401, 403, 404, 429, 503, with the error body.

POST /schema-engine/editor-sessions/{projectId}

ParameterInTypeRequired
envIdquerystring (uuid)No
projectIdpathstring (uuid)Yes
Response 200 (application/json)
{
  token: string;
  envId: string (uuid);
  idleTimeoutMs: 600000;
}

Errors: 400, 401, 403, 404, 429, 503, with the error body.

POST /schema-engine/editor-sessions/{projectId}/close

ParameterInTypeRequired
envIdquerystring (uuid)No
projectIdpathstring (uuid)Yes
Request body (application/json, required)
{
  token: string;
}
Response 200 (application/json)
{
  ok: true;
}

Errors: 400, 401, 403, 404, 429, 503, with the error body.

POST /schema-engine/push/{projectId}

Apply a SQL migration file through the schema engine (vaif db push)

ParameterInTypeRequired
envIdquerystringNo
projectIdpathstring (uuid)Yes
Request body (application/json, required)
{
  sql: string;
  name?: string;
  dryRun?: boolean;
  allowProduction?: boolean;
  force?: boolean;
  pushRunId?: string (uuid);
  before?: string[];
}
Response 200 (application/json)
{
  ok: true;
  status: "applied" | "no-changes" | "dry-run" | "already-applied";
  projectSchema: string;
  plan: any[];
}

Errors: 400, 401, 403, 404, 409, 429, 500, 503, with the error body.

GET /schema-engine/snippets/{projectId}

List the caller's SQL snippets and the project's shared ones

ParameterInTypeRequired
projectIdpathstring (uuid)Yes
Response 200 (application/json)
{
  snippets: {
    id: string (uuid);
    name: string;
    folder: string;
    sql: string;
    shared: boolean;
    owner: {
      id: string (uuid);
      name: string | null;
    };
    mine: boolean;
    canEdit: boolean;
    createdAt: string;
    updatedAt: string;
  }[];
}

Errors: 401, 403, 404, with the error body.

POST /schema-engine/snippets/{projectId}

Save a SQL snippet (private unless shared with project members)

ParameterInTypeRequired
projectIdpathstring (uuid)Yes
Request body (application/json, required)
{
  name: string;
  folder?: string;
  sql: string;
  shared?: boolean;
}
Response 201 (application/json)
{
  snippet: {
    id: string (uuid);
    name: string;
    folder: string;
    sql: string;
    shared: boolean;
    owner: {
      id: string (uuid);
      name: string | null;
    };
    mine: boolean;
    canEdit: boolean;
    createdAt: string;
    updatedAt: string;
  };
}

Errors: 400, 401, 403, 404, 409, with the error body.

PATCH /schema-engine/snippets/{projectId}/{snippetId}

Rename, move, edit, share or unshare a SQL snippet

ParameterInTypeRequired
projectIdpathstring (uuid)Yes
snippetIdpathstring (uuid)Yes
Request body (application/json, required)
{
  name?: string;
  folder?: string;
  sql?: string;
  shared?: boolean;
}
Response 200 (application/json)
{
  snippet: {
    id: string (uuid);
    name: string;
    folder: string;
    sql: string;
    shared: boolean;
    owner: {
      id: string (uuid);
      name: string | null;
    };
    mine: boolean;
    canEdit: boolean;
    createdAt: string;
    updatedAt: string;
  };
}

Errors: 400, 401, 403, 404, with the error body.

DELETE /schema-engine/snippets/{projectId}/{snippetId}

Delete a SQL snippet

ParameterInTypeRequired
projectIdpathstring (uuid)Yes
snippetIdpathstring (uuid)Yes
Response 200 (application/json)
{
  ok: true;
}

Errors: 401, 403, 404, with the error body.