Deploy reference
The Deploy API is served at api.deploy.destesi.io and is fully workspace-scoped. Every route is under /v1.
Authentication
Section titled “Authentication”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).
Deployment states
Section titled “Deployment states”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 Apply → building_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). |
Module catalog
Section titled “Module catalog”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.
web-service
Section titled “web-service”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.
postgres
Section titled “postgres”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.
Wiring
Section titled “Wiring”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
Section titled “App variables”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.
Readiness
Section titled “Readiness”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.
Actions
Section titled “Actions”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.
Redeploy
Section titled “Redeploy”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.
Destroy
Section titled “Destroy”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.
Chat-driven composition
Section titled “Chat-driven composition”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).
Deployments
Section titled “Deployments”| 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. |
Conversation
Section titled “Conversation”| 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. |
Workspace
Section titled “Workspace”| 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.
Deployment object
Section titled “Deployment object”{ "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"}repoandimage_uriare mutually exclusive — whichever is present tells you which pipeline the deployment runs and which states it will visit.recipenames the infrastructure pattern the deployment resolved to. Read the manifest route for the actual modules, parameters, and wiring in play.live_urlis populated only once the deployment islive. It is the load balancer’s DNS name.
Live stream
Section titled “Live stream”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 createddata: + 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
Section titled “Errors”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 |
Credentials
Section titled “Credentials”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.
Limits and constraints
Section titled “Limits and constraints”| 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 |