Skip to content

Projects API

These endpoints manage the full lifecycle of dagctl projects — SQLMesh and dbt pipelines connected to a Git repository.

GET /api/v1/projects

Returns all projects in your organization.

Response

{
  "projects": [
    {
      "id": "proj_abc123",
      "organization_id": "org-xxxxxxxx",
      "name": "analytics",
      "description": "Core analytics models",
      "project_type": "sqlmesh",
      "repository_url": "git@github.com:acme/analytics.git",
      "branch": "main",
      "git_auth_method": "ssh",
      "sqlmesh_environment": "prod",
      "sqlmesh_version": "0.228.5",
      "use_managed_state_database": true,
      "auto_apply": false,
      "run_tests": true,
      "deployed": true,
      "suspended": false,
      "status": "active",
      "commits_behind": 0,
      "current_commit_sha": "a1b2c3d",
      "latest_commit_sha": "a1b2c3d",
      "latest_commit_message": "Add orders model",
      "cpu_request": "500m",
      "cpu_limit": "2000m",
      "memory_request": "1Gi",
      "memory_limit": "4Gi",
      "concurrency_policy": "Allow",
      "successful_runs_history_limit": 3,
      "failed_runs_history_limit": 3,
      "created_at": "2026-01-15T10:00:00Z",
      "updated_at": "2026-02-20T14:30:00Z"
    }
  ],
  "total": 3
}

GET /api/v1/projects/:id

Returns a single project by ID.

Path Parameters

Parameter Type Description
id string Project ID

Response

Returns a single project object (same shape as list items above).


POST /api/v1/projects

Creates a new project and auto-deploys it. For SQLMesh projects, dagctl reads the repository to detect gateway names from your config.yaml or config.py.

Request Body

SQLMesh project:

{
  "name": "analytics",
  "description": "Core analytics models",
  "project_type": "sqlmesh",
  "repository_url": "https://github.com/acme/analytics",
  "branch": "main",
  "git_auth_method": "github_app",
  "sqlmesh_environment": "prod",
  "sqlmesh_version": "0.228.5",
  "use_managed_state_database": true,
  "auto_apply": false,
  "run_tests": true
}

dbt project:

{
  "name": "dbt-core",
  "project_type": "dbt",
  "repository_url": "https://github.com/acme/dbt-project",
  "branch": "main",
  "git_auth_method": "github_app",
  "dbt_version": "1.11.6",
  "python_version": "3.11",
  "polling_interval": "5m",
  "version_strategy": "semantic"
}

Body Fields

Field Type Required Description
name string yes Project name
project_type string no "sqlmesh" (default) or "dbt"
repository_url string yes Git repository URL
branch string no Branch name (default: "main")
git_auth_method string no "none", "ssh", or "github_app"
git_secret_ref string no Name of the SSH key secret (when git_auth_method is "ssh")
git_path string no Subdirectory within the repo containing the project
description string no Human-readable description
sqlmesh_environment string no SQLMesh environment name (default: "prod")
sqlmesh_version string no SQLMesh version. Must be one of the values returned by GET /api/v1/sqlmesh-versions; any other value is rejected with 400.
use_managed_state_database boolean no Use dagctl-managed state DB (default: true for SQLMesh)
auto_apply boolean no Auto-apply plans. When true, every explain plan for the project is approved and applied automatically as soon as the explain completes — no human review. Default false. See Auto-apply.
slack_alerts_enabled boolean or null no Plan alerting: null (default) inherits the organization default, true enables, false disables. See Plan alerts.
slack_channel string no Slack channel for plan alerts (e.g. #data-alerts). Overrides the organization default channel.
slack_alert_on_success boolean no Also alert when a plan applies successfully (default: failures only).
run_tests boolean no Run tests during execution
cpu_request string no Kubernetes CPU request (default: "500m")
cpu_limit string no Kubernetes CPU limit (default: "2000m")
memory_request string no Kubernetes memory request (default: "1Gi")
memory_limit string no Kubernetes memory limit (default: "4Gi")
concurrency_policy string no Kubernetes CronJob concurrency policy: "Allow", "Forbid", "Replace" (default: "Allow")
suspended boolean no Start the project in suspended state
agent_context string no Custom context for the AI agent (max 10,000 characters)
dbt_version string no dbt-core version (dbt projects only). Must be one of the values returned by GET /api/v1/dbt-versions; any other value is rejected with 400.
python_version string no Python version for dbt build (dbt projects only)
polling_interval string no Version polling interval, e.g. "5m", "30s" (dbt projects only, minimum 10s)
version_strategy string no "semantic" or "continuous" (dbt projects only)

Response

Returns 201 Created with the project object.


PUT /api/v1/projects/:id

Updates an existing project. Only supplied fields are changed. If the project is deployed, the ArgoCD Application is updated automatically.

Path Parameters

Parameter Type Description
id string Project ID

Request Body

All fields are optional. Same fields as create, with these additions:

Field Type Description
auto_apply boolean Auto-apply plans — approve and apply every plan with no human review. See Auto-apply.
slack_alerts_enabled boolean or null Plan alerting: send null explicitly to switch back to the organization default. See Plan alerts.
slack_channel string Slack channel for plan alerts; send "" to clear.
slack_alert_on_success boolean Also alert on successful applies.
run_tests boolean Run tests during execution
suspended boolean Suspend or resume the project
remediation_auto_merge boolean Automatically merge remediation PRs
max_concurrent_job_runs integer Maximum concurrent job runs (default: 1). Runs triggered beyond this limit are rejected and recorded as skipped.
status string Override project status

Response

Returns 200 OK with the updated project object.


DELETE /api/v1/projects/:id

Deletes a project. If the project is deployed, it is undeployed first via ArgoCD. The managed state database is also deleted if use_managed_state_database is true.

Path Parameters

Parameter Type Description
id string Project ID

Response

{
  "message": "Project deleted successfully"
}

POST /api/v1/projects/:id/deploy

Deploys a project by creating or updating its ArgoCD Application.

Response

{
  "message": "Project deployed successfully",
  "project": { }
}

DELETE /api/v1/projects/:id/deploy

Undeploys a project by removing its ArgoCD Application. The project record is retained.

Response

{
  "message": "Project undeployed successfully",
  "project": { }
}

POST /api/v1/projects/:id/run

Triggers an immediate run using the project's default job configuration. If no job exists for the project, a default one is auto-created. This is the "Run Now" shortcut that does not require a pre-existing job ID.

Response

{
  "message": "SQLMesh job run triggered successfully",
  "job_run_id": "run_abc123",
  "job_id": "job_xyz789",
  "job_name": "analytics-scheduled-run-manual-20260428-120000"
}

The message text reflects the project framework — "SQLMesh job run triggered successfully" or "DBT job run triggered successfully". No status field is returned; poll the run to get its status.

Manual runs have their own concurrency limit

This endpoint allows at most 3 active run jobs per project and returns 429 with too_many_active_runs beyond that. This is a separate limit from max_concurrent_job_runs, which applies to scheduled runs and rejects them as skipped.


GET /api/v1/projects/:id/sync-status

Returns the current git sync status for the project.

Response

{
  "is_synced": false,
  "needs_sync": true,
  "current_sha": "a1b2c3d4e5f6",
  "latest_sha": "f6e5d4c3b2a1",
  "commits_behind": 2,
  "last_check": "2026-04-28T12:00:00Z",
  "check_age_minutes": 7,
  "check_is_stale": false,
  "sync_error": ""
}
Field Type Description
is_synced boolean Whether the deployed commit matches the latest commit on the tracked branch
needs_sync boolean Computed: whether a sync is required
current_sha string Commit SHA currently deployed
latest_sha string Latest commit SHA on the tracked branch
commits_behind integer How many commits the deployment is behind
last_check string RFC3339 timestamp of the last sync check
check_age_minutes integer Minutes since the last check. Present only when a check has run.
check_is_stale boolean Whether the last check is old enough to be unreliable. Present only when a check has run.
sync_error string Error from the last sync check, empty if none

Auto-apply

auto_apply turns a project into a continuous-deployment target. With it on:

  • Every explain plan that reaches pending is approved and applied immediately — plans created by the git sync poller and plans you create manually via the API or the Create and apply plan button. There is no review step and no undo.
  • Changes deploy to the project's configured environment (sqlmesh_environment, prod by default), including model removals, restatements and backfills.
  • If another plan is already executing, the new explain waits (status stays pending) and is applied automatically when that execution finishes.
  • The execute step records triggered_by: "auto_apply" and approved_by_actor: "system:auto_apply".
  • Plans that remove models post a warning to the project's Slack alert channel (if configured) but are not blocked.
  • Environment protections are not consulted by automatic approvals.

auto_apply defaults to false and must be enabled explicitly per project (the project settings page asks for confirmation). Calling POST /plans/:planId/approve on a plan that auto-apply already approved returns 200 with "status": "already_approved" rather than an error, so approval clients stay idempotent.

Warning

Before this release the field was stored but never read; every plan required manual approval regardless of its value. All existing projects were reset to false when the behaviour went live.

Plan alerts

Plan explain and execute failures (and, optionally, successes) are posted to Slack using the organization's Slack connection (Settings → Integrations). Where the message goes is resolved per project:

slack_alerts_enabled Behaviour
null (default) Inherit: alerts are sent if the organization's Send plan alerts default is on. Channel = the project's slack_channel if set, otherwise the organization default channel.
true Always alert. Channel = slack_channel, falling back to the organization default channel.
false Never alert for this project, regardless of organization defaults.

Organization defaults are managed with GET/PUT /api/v1/organizations/settings/alert-settings:

{
  "default_slack_channel": "#data-alerts",
  "plan_alerts_enabled": true
}

PUT accepts a partial body and requires the manage_org permission. Job-run alerts that have alerts enabled but no channel of their own fall back to the same default channel.

Each plan alerts at most once (alerts_sent), so retried job-watcher callbacks do not produce duplicates. Cancelling a plan does not alert.