Skip to content

Ask AI

Ask anything about Destesi — setup, products, APIs.

Powered by Claude. Answers may be wrong — always verify against the docs.

Deploy reference

The Deploy API is served at api.deploy.destesi.io and is fully workspace-scoped. Every route is under /v1.

Deploy uses session-based authentication. When you open deploy.destesi.io from the suite launcher, single sign-on sets a host-only session cookie automatically — you don’t manage it yourself. All API calls from the Deploy web app use this cookie.

There is no standalone API-key system for Deploy. Scripted access requires an active browser session or a Personal Access Token (the same idn_pat_ token used across the suite, sent with an X-Destesi-Workspace header).

A deployment moves through twelve states. Which ones it visits depends on how it is sourced: a deployment built from a GitHub repo walks the full path, while one that carries a pre-built container image skips the stages that read or build source.

State Meaning What happens next
pending Deployment created. The pipeline is initializing. fetching_repo, or straight to planning for an image-sourced deployment.
fetching_repo Deploy is fetching your repo’s source through your GitHub connection. analyzing.
analyzing The readiness analyzer is reading the source tree — container build target, port, health-check path, infrastructure dependencies. planning, or needs_config if it found a blocker.
needs_config The analyzer found something that will block a deploy and is waiting on you: missing configuration, an ambiguous port, an app variable it can’t infer. You supply the missing piece or override the report → analyzing / planning / awaiting_apply.
planning terraform plan is running against the composed configuration. The plan streams to your browser as it builds. awaiting_apply.
awaiting_apply The plan is ready and waiting for your action. No resources have been created yet. You click Applybuilding_image (or applying for an image-sourced deployment).
building_image Your repo is being built into a container image and pushed to the ECR repository. applying.
applying terraform apply is running and resources are being provisioned. live on success, failed on error.
live Deployment succeeded. The service endpoint is active and reachable. Redeploy (→ planning) or Destroy (→ destroying).
destroying terraform destroy is running. destroyed, or failed.
destroyed All resources have been removed. Terraform state confirms nothing remains in your cloud account. Terminal. Create a new deployment to start again.
failed A step errored. The streamed log contains the cause. Retry the plan (→ planning) or tear down (→ destroying).

Deploy does not generate arbitrary Terraform. A deployment is composed from modules — vetted building blocks assembled into a single Terraform root. Two modules exist today.

A containerized web service on ECS Fargate behind a load balancer.

Parameter Default Purpose
container_port 8080 The port your container listens on. The load balancer routes to it.
cpu 256 Fargate task CPU units.
memory 512 Fargate task memory in MiB.
desired_count 1 How many tasks to run.

It provisions an ECR repository, an ECS cluster, task definition and service, an Application Load Balancer with a listener and target group, security groups for the load balancer and the tasks, a CloudWatch log group, and an IAM task execution role. Resources are created in a single AWS region using your account’s default VPC.

The load balancer accepts inbound traffic on port 80 from any source and forwards it to your container port. The task security group accepts traffic only from the load balancer’s security group, so your container is never reachable directly from the internet.

An RDS PostgreSQL instance in the same default VPC.

Parameter Default Purpose
engine_version 16 PostgreSQL major version.
instance_class db.t3.micro RDS instance class. Must be a db.* class.
allocated_storage 20 Storage in GiB.
db_name app Name of the database created on the instance.
username app Master username.

It provisions a DB subnet group, a security group, and the instance itself. The master password is generated for you.

Modules are connected by declared wiring, not by hand-editing Terraform. The one wiring kind today is database_url: it injects a DATABASE_URL environment variable pointing at the Postgres instance into the web service, and opens the database’s security group to the service’s task security group. Adding a database and wiring it is a single conversational step (see Chat-driven composition).

App variables are environment configuration for your services, scoped to the workspace rather than to a single deployment. Every deployment in the workspace resolves the workspace’s variables when it applies.

Field Meaning
key The environment-variable name. Must match ^[A-Z_][A-Z0-9_]*$ — the key is rendered into Terraform, so anything outside that charset is rejected at the API boundary.
value The value. For a secret variable this is write-only: the list response returns a mask, never the plaintext.
is_secret Whether the value is masked on read and delivered as an SSM task secret rather than a plain environment entry.
target auto (default), env, or secret — how the variable should be delivered to the container.

Writes save immediately; a variable is not tied to one deployment, so nothing is staged as a pending revision. The change takes effect on each deployment’s next apply, which is why the UI suggests a redeploy after you change one — the apply remains the approve-and-spend gate.

Deploy runs a readiness analyzer over your repo before planning and records a report of what would block a deploy — a missing container build target, an unclear container port, configuration it could not infer. GET /v1/deployments/{id}/readiness returns that report; a deployment that hasn’t been analyzed yet returns 404 no_report.

When the analyzer finds a blocker, the deployment parks in needs_config instead of planning. You can either fix the underlying issue and let it re-analyze, or override the report with POST /v1/deployments/{id}/readiness/override — the deployment then proceeds to the plan and the normal apply gate. Overriding skips the advice, not the approval.

Separately, GET /v1/setup/readiness answers a workspace-level question: can Deploy do its job at all yet? It reports two items — an AWS connection through Connect, which is required, and a GitHub connection, which is advisory. A signal Deploy could not evaluate is reported as unknown rather than as missing, so a transient Connect outage never tells you your AWS account has disappeared.

Available when the deployment is at the awaiting_apply gate or has a pending manifest revision.

Runs the plan you reviewed: builds and pushes your container image where applicable, then runs terraform apply. Returns a live service endpoint on success and moves the deployment to live. Apply is always preceded by a plan — you cannot apply without one.

Available when the deployment is live or failed.

POST /v1/deployments/{id}/redeploy re-arms the deployment: it re-analyzes the current repo, re-composes the stack (picking up, say, a Postgres dependency added since the last deploy), re-plans, and parks at awaiting_apply. It never applies on its own — the same approval gate as the first deploy stands.

Its responses are worth knowing: a deployment already at the gate (awaiting_apply or needs_config) returns 200 as a no-op; one with a run in flight returns 409 wrong_state; a destroyed deployment also returns 409, because there is nothing left to redeploy — create a new one.

Available when the deployment is live or failed.

Runs terraform destroy. Removes every resource Terraform created — ECR repository, ECS cluster, service and task definition, load balancer, target group, security groups, CloudWatch log group, IAM execution role, and the RDS instance if a postgres module was part of the stack. Moves the deployment through destroying to destroyed.

You can talk to a deployment to change its shape. POST /v1/deployments/{id}/messages sends a message to that deployment’s conversation; “add a database” appends a postgres module and the database_url wiring, and “remove the database” takes them back out. The revised manifest is staged as a pending revision and the deployment returns to the plan — it never auto-applies.

If a run is already in flight when you send a message, the API returns 409 deployment_busy — wait for the current plan or apply to settle, then send it again.

All routes require an active session (cookie or idn_pat_ bearer token + X-Destesi-Workspace header).

Method Path Purpose
POST /v1/deployments Create a deployment. Body carries a prompt plus either repo (owner/name) or image_uri — exactly one, never both — with optional region, container_port, health_check_path. Returns {id, state} in pending.
GET /v1/deployments List deployments in the workspace.
GET /v1/deployments/{id} Fetch one deployment’s full state, including the live URL (when live) and any error detail (when failed).
GET /v1/deployments/{id}/manifest The composed manifest — modules, parameters, and wiring — for this deployment. 404 no_manifest before one exists.
GET /v1/deployments/{id}/config-suggestion Configuration the analyzer suggests for the deployment.
GET /v1/deployments/{id}/connections The provider connections this deployment resolves through Connect.
GET /v1/deployments/{id}/readiness The readiness report. 404 no_report if the deployment was never analyzed.
POST /v1/deployments/{id}/readiness/override Mark the report overridden and re-drive the pipeline past the needs_config block.
POST /v1/deployments/{id}/apply Approve the plan. The only route into applying.
POST /v1/deployments/{id}/redeploy Re-arm a live or failed deployment back to the awaiting_apply gate. Never applies.
POST /v1/deployments/{id}/destroy Start a full teardown.
GET /v1/deployments/{id}/stream Server-Sent Events stream of plan and apply output.
Method Path Purpose
POST /v1/deployments/{id}/messages Send a message to this deployment’s conversation to revise its manifest. 409 deployment_busy while a run is in flight.
GET /v1/deployments/{id}/messages List the deployment’s conversation history.
POST /v1/chat Deploy’s chat home agent. Has list, get, and apply tools only — it cannot compose a manifest.
POST /v1/chat/resume Resume a chat turn that paused for your approval.
GET /v1/conversations List chat-home conversations.
GET /v1/conversations/{id}/messages Page one conversation’s messages.
PATCH /v1/conversations/{id} Rename a conversation.
DELETE /v1/conversations/{id} Delete a conversation and its history.
Method Path Purpose
GET /v1/workspaces/{slug}/variables List app variables. Secret values are masked.
PUT /v1/workspaces/{slug}/variables/{key} Create or update a variable. Saves immediately.
DELETE /v1/workspaces/{slug}/variables/{key} Delete a variable. Saves immediately.
GET /v1/setup/readiness Workspace-level setup readiness: is an AWS connection in place?
GET /v1/guidance/{guide} Fetch the state of an onboarding guide.
POST /v1/guidance/{guide} Record an action against an onboarding guide.

The {slug} on the variables routes must address the workspace your session is already bound to; a mismatch is rejected with 403.

{
"id": "dep_abc123",
"repo": "acme/api-service",
"provider": "aws",
"recipe": "aws-fargate-web",
"region": "us-east-1",
"state": "live",
"live_url": "http://acme-api-service-alb-1234567890.us-east-1.elb.amazonaws.com",
"error": null,
"created_at": "2026-05-31T12:00:00Z",
"updated_at": "2026-05-31T12:05:00Z"
}
  • repo and image_uri are mutually exclusive — whichever is present tells you which pipeline the deployment runs and which states it will visit.
  • recipe names the infrastructure pattern the deployment resolved to. Read the manifest route for the actual modules, parameters, and wiring in play.
  • live_url is populated only once the deployment is live. It is the load balancer’s DNS name.

GET /v1/deployments/{id}/stream returns an SSE stream. Each data: event is a single line of Terraform output:

data: Terraform will perform the following actions:
data:
data: # aws_ecr_repository.this will be created
data: + resource "aws_ecr_repository" "this" {
data: + name = "acme-api-service"
...
data: Plan: 9 to add, 0 to change, 0 to destroy.

The Deploy web app connects to this stream and renders the output in real time. If you reconnect mid-stream — after a page refresh, say — the API replays buffered output from the start.

Errors are returned as JSON { "error": "<code>" } with a matching HTTP status:

Status Codes
400 invalid_body, invalid_json, invalid_repo, invalid_request, invalid_key, missing_key, missing_workspace, missing_workspace_id, invalid_cursor, empty_content, invalid_message_content, invalid_title, unknown_action, missing_action
401 Session missing or invalid
403 forbidden (the addressed workspace is not the one you are authenticated for)
404 not_found, no_manifest, no_report, not_applicable
409 wrong_state (the action is not legal in the current state), deployment_busy (a run is already in flight)
503 chat_disabled, persistence_disabled, store unavailable

Deploy reads all cloud credentials from Connect. It never stores AWS keys, secrets, or tokens itself. Revoking or rotating your AWS credentials in Connect affects every Deploy deployment in that workspace immediately.

See the Connect reference for how to authorize an AWS connection.

Limit Value
Cloud providers AWS only
Module catalog web-service and postgres
Wiring kinds database_url
AWS regions Any region accessible with the workspace’s AWS credentials
Deployments per workspace No hard cap; subject to your AWS account’s service quotas
Repo requirement Must have a buildable container definition, or supply a pre-built image_uri instead
VPC Default VPC in the selected region
HTTPS / TLS The load balancer endpoint is HTTP. TLS termination is not included
Wired DATABASE_URL Delivered as a task-definition environment variable, not an SSM or Secrets Manager secret
Plan output retention Buffered for the lifetime of the deployment; not persisted after destroyed