Guide
projects

Deploy API

Create API tokens and trigger deployments programmatically from CI, a CMS, or any HTTP client.


The Deploy API lets you trigger a deployment of your project from outside the dashboard — from a CI pipeline, a CMS webhook, or any script that can make an HTTP request. You authenticate with a bearer token that you create on this page.

To open it, go to your project's Settings, and under Integrations click Deploy API.

Note: Managing deploy tokens requires an organization admin role. If you don't have it, you'll be redirected to the project settings page.

Watch: create a deploy token

Create a token

  1. Click New token in the top-right.
  2. In the New deploy token dialog, fill in:
    • Name — a label so you remember what the token is for (for example, GitHub Actions production). It's also shown in deploy notifications.
    • Scope — choose one of:
      • Live deployment — always deploys your project's deploy branch (typically main) to the production URL.
      • Preview branch — deploys one specific branch to a preview URL. A Branch field appears for you to name it.
      • Any branch in this project — the caller chooses the branch and whether it's live or preview, per request. Useful for A/B variants and CI scripts that build many branches under one token.
    • Expires inNever, 7 days, 30 days, 90 days, or 1 year. After it expires, the token stops working and returns a "token expired" error.
  3. Click Create token.

Warning: The token is shown once, right after creation, in a highlighted box with a Copy button. Copy it now and store it somewhere safe — it won't be shown again. If you lose it, revoke it and create a new one.

Treat tokens like passwords: keep them out of source control, and give each integration its own token with the narrowest scope it needs.

Manage existing tokens

Each token appears in the Tokens list with its name, a masked prefix, what it deploys, when it was created, and when it was last used (or Never used). Expired tokens are flagged.

To revoke a token, click Revoke on its row and confirm. The token stops working immediately, so any integration using it will need a new one.

Trigger a deployment

The page shows the exact base URL for your project. It looks like this:

https://<api-host>/v1/projects/<your-project-slug>/deployments

There are two endpoints:

Method Path Purpose
POST /v1/projects/<slug>/deployments Trigger a new deployment
GET /v1/projects/<slug>/deployments/<deployment-id> Poll deployment status

Authenticate every request by sending your token in the Authorization header:

Authorization: Bearer pdt_...

Trigger with curl

curl -X POST https://<api-host>/v1/projects/<slug>/deployments \
  -H "Authorization: Bearer pdt_..." \
  -H "Content-Type: application/json" \
  -d '{"branch": "main", "triggeredBy": "ci-build-${BUILD_ID}"}'

Trigger with fetch (JavaScript)

const res = await fetch('https://<api-host>/v1/projects/<slug>/deployments', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer pdt_...',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    branch: 'main',           // omit for branch-scoped tokens
    // isLive: false,         // optional override; inferred from branch
    triggeredBy: 'ci-build',
  }),
})
const { deploymentId, statusUrl } = await res.json()

Trigger with Python

import requests

res = requests.post(
    'https://<api-host>/v1/projects/<slug>/deployments',
    headers={'Authorization': 'Bearer pdt_...'},
    json={
        'branch': 'main',          # omit for branch-scoped tokens
        # 'isLive': False,         # optional override; inferred from branch
        'triggeredBy': 'ci-build',
    },
)
data = res.json()
deployment_id = data['deploymentId']
status_url = data['statusUrl']

Tip: The dashboard's API documentation section has ready-to-copy snippets for curl, fetch, PHP, and Python — and when you've just created a token, the snippets are pre-filled with the real token value.

Request body

POST accepts an optional JSON body (Content-Type: application/json, max 8 KB):

Field Type When Description
triggeredBy string optional Free-form label shown next to the token name in deploy notifications (max 100 chars).
branch string required for any branch tokens; ignored for branch-scoped tokens The branch to deploy.
isLive boolean optional override Defaults to true when branch matches the project's deploy branch (typically main), otherwise false. Pass it explicitly only for edge cases.

For a branch-scoped token (Live deployment or Preview branch), you can omit the body entirely — the token already knows what to deploy.

Response

A successful POST returns 202 Accepted with the deployment id and the URL to poll:

{
  "deploymentId": "01J...",
  "statusUrl": "https://<api-host>/v1/projects/<slug>/deployments/01J..."
}

Poll deployment status

Send a GET to the statusUrl with the same bearer token:

curl https://<api-host>/v1/projects/<slug>/deployments/<deployment-id> \
  -H "Authorization: Bearer pdt_..."

The response reports progress:

{
  "deploymentId": "01J...",
  "status": "running",
  "createdAt": "2026-05-07T12:00:00.000Z",
  "startedAt": "2026-05-07T12:00:01.000Z",
  "completedAt": null,
  "publishedUrl": null,
  "errorMessage": null
}

The status field moves through:

pending → queued → running → completed

Or it ends in a terminal failure state:

  • failed — the build or deploy hit an error; see errorMessage.
  • cancelled — a newer deployment for the same branch superseded this one.

When a deployment completes, publishedUrl points at the live result.

Good to know

  • Idempotency-Key. To make retries safe, send an Idempotency-Key header. A second POST within 24 hours using the same key (and same branch) returns the original deploymentId instead of creating a duplicate, and the replay carries an Idempotent-Replayed: true header. Use a UUID, build id, or content hash (1–200 chars).
  • Rate limits. Each token is limited to 60 requests per minute; exceeding it returns 429 Too Many Requests. Bursts to the same branch are coalesced — only the latest deployment runs — while different branches deploy in parallel.
  • Errors. All errors return JSON shaped like {"error": {"code": "...", "message": "..."}}. Switch on code in your scripts; the human-readable message may change. Common codes include missing_authorization, invalid_token, token_expired, invalid_branch, and rate_limited.
  • Builds disabled. If the project has managed builds disabled, a POST returns 409 with code builds_disabled — deploy the site through your own CI instead.