> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.sevalla.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> This is the Sevalla REST API reference. Base URL: https://api.sevalla.com/v3. Authenticate with an API key in the Authorization header as a Bearer token.
> The OpenAPI spec is at https://api.sevalla.com/v3/openapi.json. Concepts and dashboard guides are at https://docs.sevalla.com (index: https://docs.sevalla.com/llms.txt).
> To act on a Sevalla account from an AI agent, prefer the Sevalla MCP server (https://mcp.sevalla.com/mcp, see https://docs.sevalla.com/ai-agents/overview.md) or the Sevalla CLI (https://api-docs.sevalla.com/v3/cli.md).

# Long-running operations

> Which Sevalla API operations return before the work finishes, what they return, and how to poll deployments, databases, backups, restores and deletions until they end.

A long-running operation starts work that Sevalla finishes in the background: the request returns as soon as the work is queued, and you poll another operation to learn how it ended. Deployments, database changes, backups, restores, suspensions and most deletions work this way.

## How it works

A long-running operation returns one of two things:

* **The new object, with a status.** [Trigger deployment](/v3/applications/trigger-deployment) returns the deployment with `status` `waiting`, and [Create database](/v3/databases/create-database) returns the database with `status` `creating`. Poll the object's `GET` operation until the status is final.
* **A message.** [Restore database backup](/v3/databases/restore-database-backup) returns `202` with `{"message": "Database backup restore started"}`, and [Suspend application](/v3/applications/suspend-application) returns `200` with a message. Poll the service the operation acts on.

Poll every 10 seconds or more, and stop at a final status or after a timeout you choose. Polling counts toward the read [rate limit](/v3/rate-limits) of 400 requests per minute for the company. For deployments, a [webhook](/v3/webhook-events) on `APP_DEPLOY` or `STATIC_SITE_DEPLOY` tells you when they end, without polling.

## Deployments

| Operation | Returns | Poll |
| - | - | - |
| [Trigger deployment](/v3/applications/trigger-deployment), [Rollback deployment](/v3/applications/rollback-deployment) | The deployment | [Get deployment](/v3/applications/get-deployment) |
| [Promote pipeline](/v3/pipelines/promote-pipeline) | A `target_app_id` and a `deployment_id` for each target application | [Get deployment](/v3/applications/get-deployment) for each target |
| [Deploy static site](/v3/static-sites/deploy-static-site) | The static site deployment | [Get static site deployment](/v3/static-sites/get-static-site-deployment) |
| [Create static site](/v3/static-sites/create-static-site) | The static site. Its first deployment starts on its own. | [List static site deployments](/v3/static-sites/list-static-site-deployments), newest first |

Application and static site deployments have the same statuses:

| `status` | Meaning | Final |
| - | - | - |
| `waiting` | Queued, not started yet | No |
| `inProgress` | Building or rolling out | No |
| `success` | Deployed | Yes |
| `failed` | The build or the rollout failed. Read the deployment's logs with [Get application deployment logs](/v3/applications/get-application-deployment-logs). | Yes |
| `cancelled` | Stopped while in progress | Yes |
| `skipped` | Never ran, for example because it was cancelled while `waiting` | Yes |

An application deployment's `finished_at` is set when it succeeds, fails or is cancelled. To stop a deployment, call [Cancel deployment](/v3/applications/cancel-deployment) or [Cancel static site deployment](/v3/static-sites/cancel-static-site-deployment).

### Wait for a deployment

Trigger the deployment, keep its `id`, then get it until the status is final:

```bash theme={null}
curl -X POST "https://api.sevalla.com/v3/applications/$APP_ID/deployments" \
  -H "Authorization: Bearer $SEVALLA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"branch": "main"}'
```

```json theme={null}
{
  "id": "3b2a1c0d-8e7f-4a6b-9c5d-2e1f0a9b8c7d",
  "app_id": "7f3c2b1a-9e8d-4c7b-a6f5-4e3d2c1b0a9f",
  "status": "waiting",
  "type": "manual",
  "branch": "main",
  "started_at": null,
  "finished_at": null,
  "created_at": "2026-10-15T09:12:44.000Z",
  "updated_at": "2026-10-15T09:12:44.000Z"
}
```

The response has more fields, such as `steps`, `repo_url` and `commit_sha`: see [Trigger deployment](/v3/applications/trigger-deployment). In code:

```typescript wait-for-deployment.ts theme={null}
const API = "https://api.sevalla.com/v3";
const headers = { Authorization: `Bearer ${process.env.SEVALLA_API_KEY}`, "Content-Type": "application/json" };
const FINAL = ["success", "failed", "cancelled", "skipped"];

type Deployment = { id: string; status: string | null };

async function waitForDeployment(appId: string, deploymentId: string, timeoutMs = 30 * 60_000): Promise<Deployment> {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const res = await fetch(`${API}/applications/${appId}/deployments/${deploymentId}`, { headers });
    if (!res.ok) throw new Error(`Get deployment returned ${res.status}: ${(await res.json()).message}`);
    const deployment = (await res.json()) as Deployment;
    if (deployment.status && FINAL.includes(deployment.status)) return deployment;
    await new Promise((resolve) => setTimeout(resolve, 10_000));
  }
  throw new Error(`Deployment ${deploymentId} didn't finish within ${timeoutMs / 60_000} minutes`);
}

const appId = process.env.APP_ID!;
const res = await fetch(`${API}/applications/${appId}/deployments`, {
  method: "POST",
  headers,
  body: JSON.stringify({ branch: "main" }),
});
if (!res.ok) throw new Error(`Trigger deployment returned ${res.status}: ${(await res.json()).message}`);
const { id } = (await res.json()) as Deployment;

const deployment = await waitForDeployment(appId, id);
console.log(`Deployment ${deployment.id}: ${deployment.status}`);
process.exit(deployment.status === "success" ? 0 : 1);
```

## Databases

Poll [Get database](/v3/databases/get-database) and read `status`:

| Operation | `status` while it runs | When it's done | When it fails |
| - | - | - | - |
| [Create database](/v3/databases/create-database) | `creating` | `ready` | `error` |
| [Update database](/v3/databases/update-database) with a new `resource_type_id` | `updating` | `ready` | `error` |
| [Restart database](/v3/databases/restart-database) | `updating` | `ready` | `error` |
| [Reset database password](/v3/databases/reset-database-password) | `passwordChange` | `ready` | `passwordChangeFailed` |
| [Restore database backup](/v3/databases/restore-database-backup), and `POST /databases/{id}/external-backups/{backup_id}/restore` for external backups | `restoring`, on the database restored into | `ready` | `restoringFailed` |
| [Toggle external connection](/v3/databases/toggle-external-connection) | `updatingExternalConnection` | `ready` | `ready` too: check `external_hostname` and `external_port` to see whether the change applied |

[Suspend database](/v3/databases/suspend-database) and [Activate database](/v3/databases/activate-database) don't change `status`: `is_suspended` changes to `true` or `false` when they finish. For [Delete database](/v3/databases/delete-database), see [Deletions](#deletions).

Restarting, updating, suspending, restoring and deleting a database are rejected with `400` while the database is busy, for example while a backup is in progress. Wait for `ready` before you start the next operation.

## Backups

| Operation | Returns | When it's done |
| - | - | - |
| [Create database backup](/v3/databases/create-database-backup) | `202` with `{"message": "Database backup creation started"}` | The backup appears in [List database backups](/v3/databases/list-database-backups) |
| `POST /databases/{id}/external-backups` | `202` with a message | The backup appears in `GET /databases/{id}/external-backups` once its upload to the backup target finishes |

Backups have no status of their own: poll the list until a backup with your `display_name` appears.

## Deletions

| Operation | Returns | Poll | When it's done | When it fails |
| - | - | - | - | - |
| [Delete application](/v3/applications/delete-application) | `200` with a message | [Get application](/v3/applications/get-application) | `404` (`Application not found`) | `status` `appDeletionFailed` |
| [Delete database](/v3/databases/delete-database) | `200` with a message | [Get database](/v3/databases/get-database) | `404` (`Database not found`) | `status` `dbDeletionFailed` |
| [Delete object storage bucket](/v3/object-storages/delete-object-storage-bucket) | `202` with no body | [Get object storage bucket](/v3/object-storages/get-object-storage-bucket) | `404` (`Object storage not found`) | `status` `deletionFailed` |

While the deletion runs, `status` is `deleting`. Deleting a static site or a project finishes before the response.

## Other background operations

| Operation | Poll | When it's done |
| - | - | - |
| [Suspend application](/v3/applications/suspend-application), [Activate application](/v3/applications/activate-application) | [Get application](/v3/applications/get-application) | `is_suspended` is `true` or `false` |
| [Create TCP proxy](/v3/applications/create-tcp-proxy), [Create private port](/v3/applications/create-private-port) | [List TCP proxies](/v3/applications/list-tcp-proxies), [List private ports](/v3/applications/list-private-ports) | `status` changes from `creating` to `ready`, or to `failed` |
| [Add application domain](/v3/applications/add-application-domain), and the same operation for static sites and load balancers | [Get application domain](/v3/applications/get-application-domain) | `status` changes from `VERIFICATION` to `POINTING` to `ACTIVE` as your DNS records are found, or to `TIMED_OUT` when verification doesn't finish within 7 days. See [Custom domains](https://docs.sevalla.com/domains/overview). |

## Changes that wait for a deployment

Some operations finish at once but take effect only at the application's next deployment: creating a process, changing a process's resource type, scaling or probes, deleting a process, and changing environment variables. [Trigger a deployment](/v3/applications/trigger-deployment) to apply them. With `is_restart` set to `true`, the deployment reuses the last successful build instead of building again.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.