| 1 | --- |
| 2 | title: Deployments API |
| 3 | description: Report deployments from any CI, see every deployment of a repository and its environments in one place, require them before a merge, and hear of them by webhook. |
| 4 | --- |
| 5 | |
| 6 | A repository keeps one list of its deployments, wherever they ran. A |
| 7 | deployment is one commit sent to one environment, such as `production` or |
| 8 | `staging`. An environment is the place it went: a name, and the address it |
| 9 | is served at. Each deployment has a list of statuses that say how it went, |
| 10 | and its latest status is its state. |
| 11 | |
| 12 | Deployments reach that list three ways. Each deployment says which in its |
| 13 | `source`: |
| 14 | |
| 15 | | `source` | Made by | Ids | |
| 16 | | --- | --- | --- | |
| 17 | | `api` | Any CI or script, through the routes on this page, with an access token. | `dep_…` | |
| 18 | | `actions` | A [g1t Actions](/guides/actions/) job with an `environment:`, by itself. | `dep_…` | |
| 19 | | `g1t_page` | A [g1t.page](/guides/deployments/) build of a project, to the `production` or `preview` environment. | `dpl_…` | |
| 20 | |
| 21 | A status's id starts `dst_`. Every deployment, whatever its source, shows |
| 22 | on the repository's **Deployments** page, sets a check on its commit, and |
| 23 | sends [webhooks](#webhooks). |
| 24 | |
| 25 | ## Report a deployment from any CI |
| 26 | |
| 27 | You report a deployment by creating it, then adding a status each time it |
| 28 | moves on. The base URL is `https://api.g1t.sh`, and every request carries |
| 29 | an access token as `Authorization: Bearer g1t_…`. |
| 30 | |
| 31 | ### Make a token |
| 32 | |
| 33 | 1. Open your **Settings → Access tokens**, or a workspace's |
| 34 | **Settings → Access tokens** for a token that belongs to the workspace. |
| 35 | 2. Give the token a name, such as `release-pipeline`. |
| 36 | 3. Select the **CI** preset. It includes `deployments:read` and |
| 37 | `deployments:write`. To report deployments and nothing else, tick only |
| 38 | `deployments:write` under **Deployments**. |
| 39 | 4. Choose an expiry and select **Create token**. Copy the token now: it is |
| 40 | not shown again. |
| 41 | 5. Store it in your CI as a secret named `G1T_TOKEN`. |
| 42 | |
| 43 | Reporting also needs the Write [role](/guides/access-and-roles/) on the |
| 44 | repository. See [scopes](/guides/authentication/#scopes). |
| 45 | |
| 46 | ### Create the deployment |
| 47 | |
| 48 | `POST /repos/{owner}/{name}/deployments` creates a deployment. Give it |
| 49 | `state: "in_progress"` when the deploy has started: |
| 50 | |
| 51 | ```sh |
| 52 | curl -X POST https://api.g1t.sh/repos/acme/web/deployments \ |
| 53 | -H "Authorization: Bearer $G1T_TOKEN" \ |
| 54 | -H "Content-Type: application/json" \ |
| 55 | -d '{ |
| 56 | "ref": "main", |
| 57 | "environment": "staging", |
| 58 | "description": "Deployed by the release pipeline", |
| 59 | "state": "in_progress", |
| 60 | "log_url": "https://ci.example.com/pipelines/4182" |
| 61 | }' |
| 62 | ``` |
| 63 | |
| 64 | The answer is the deployment, with its first status in `statuses`. Keep |
| 65 | its `id` for the next call. |
| 66 | |
| 67 | | Field | Default | | |
| 68 | | --- | --- | --- | |
| 69 | | `ref` | Required | The branch, tag or commit deployed, such as `main` or `v1.4.0`. | |
| 70 | | `sha` | Resolved from `ref` | The commit deployed. Give the whole commit id to skip resolving `ref`. | |
| 71 | | `environment` | `production` | Any name up to 255 characters, such as `staging` or `review/feature-x`. Names are matched without regard to case, and the first spelling is kept. | |
| 72 | | `task` | `deploy` | What kind of deployment, such as `deploy:migrations`. Up to 100 characters. | |
| 73 | | `description` | None | A short note, up to 1,000 characters. | |
| 74 | | `payload` | `{}` | Anything else to keep with it: a JSON object, or a JSON string of one, up to 64 KB. Returned as given. | |
| 75 | | `production_environment` | `true` for `production`, else `false` | Whether people use this environment directly. | |
| 76 | | `transient_environment` | `false` | Whether the environment goes away, such as a review app. | |
| 77 | | `state` | `queued` | Its first status. See [statuses](#statuses). | |
| 78 | | `environment_url` | None | Where it is served, an `http` or `https` address. | |
| 79 | | `log_url` | None | Where its output can be read, an `http` or `https` address. | |
| 80 | |
| 81 | ### Report how it went |
| 82 | |
| 83 | `POST /repos/{owner}/{name}/deployments/{id}/statuses` adds a status. When |
| 84 | the deploy succeeds, give the address it is served at: |
| 85 | |
| 86 | ```sh |
| 87 | curl -X POST https://api.g1t.sh/repos/acme/web/deployments/dep_01kq7z9a1c3e5g7j9m1p3r5t7v/statuses \ |
| 88 | -H "Authorization: Bearer $G1T_TOKEN" \ |
| 89 | -H "Content-Type: application/json" \ |
| 90 | -d '{ |
| 91 | "state": "success", |
| 92 | "environment_url": "https://staging.example.com", |
| 93 | "log_url": "https://ci.example.com/pipelines/4182" |
| 94 | }' |
| 95 | ``` |
| 96 | |
| 97 | When it fails: |
| 98 | |
| 99 | ```sh |
| 100 | curl -X POST https://api.g1t.sh/repos/acme/web/deployments/dep_01kq7z9a1c3e5g7j9m1p3r5t7v/statuses \ |
| 101 | -H "Authorization: Bearer $G1T_TOKEN" \ |
| 102 | -H "Content-Type: application/json" \ |
| 103 | -d '{ |
| 104 | "state": "failure", |
| 105 | "description": "Smoke tests failed", |
| 106 | "log_url": "https://ci.example.com/pipelines/4182" |
| 107 | }' |
| 108 | ``` |
| 109 | |
| 110 | | Field | Default | | |
| 111 | | --- | --- | --- | |
| 112 | | `state` | Required | `queued`, `in_progress`, `success`, `failure`, `error` or `inactive`. | |
| 113 | | `description` | None | A short note, up to 1,000 characters. | |
| 114 | | `environment_url` | None | Where it is served, an `http` or `https` address. | |
| 115 | | `log_url` | None | Where its output can be read, an `http` or `https` address. | |
| 116 | | `auto_inactive` | `true` | On a `success`, give the environment's older successful deployments an `inactive` status. See [auto_inactive](#auto_inactive). | |
| 117 | |
| 118 | The deployment takes the status's state, and any address the status gives. |
| 119 | |
| 120 | ### A script for any CI |
| 121 | |
| 122 | This script wraps a deploy command. It needs `curl`, `jq`, and three |
| 123 | variables: `G1T_TOKEN`, `G1T_REPO` (such as `acme/web`) and `GIT_COMMIT` |
| 124 | (the commit being deployed). Change `ENVIRONMENT`, `ENVIRONMENT_URL` and |
| 125 | the deploy command to yours. |
| 126 | |
| 127 | ```sh |
| 128 | #!/bin/sh |
| 129 | set -eu |
| 130 | |
| 131 | API="https://api.g1t.sh/repos/$G1T_REPO/deployments" |
| 132 | ENVIRONMENT="staging" |
| 133 | ENVIRONMENT_URL="https://staging.example.com" |
| 134 | |
| 135 | report() { |
| 136 | curl -fsS -X POST "$1" \ |
| 137 | -H "Authorization: Bearer $G1T_TOKEN" \ |
| 138 | -H "Content-Type: application/json" \ |
| 139 | -d "$2" |
| 140 | } |
| 141 | |
| 142 | # 1. Create the deployment, in progress. |
| 143 | ID=$(report "$API" "$(jq -n \ |
| 144 | --arg ref "$GIT_COMMIT" \ |
| 145 | --arg environment "$ENVIRONMENT" \ |
| 146 | '{ref: $ref, environment: $environment, state: "in_progress"}')" | jq -r .id) |
| 147 | |
| 148 | # 2. Deploy, then report how it went. |
| 149 | if ./deploy.sh; then |
| 150 | report "$API/$ID/statuses" "$(jq -n --arg url "$ENVIRONMENT_URL" \ |
| 151 | '{state: "success", environment_url: $url}')" > /dev/null |
| 152 | else |
| 153 | report "$API/$ID/statuses" '{"state": "failure", "description": "The deploy command failed"}' > /dev/null |
| 154 | exit 1 |
| 155 | fi |
| 156 | ``` |
| 157 | |
| 158 | Give `log_url` in both calls to link the deployment to your CI's page for |
| 159 | the job. |
| 160 | |
| 161 | ## Statuses |
| 162 | |
| 163 | | State | Means | The commit's check | |
| 164 | | --- | --- | --- | |
| 165 | | `queued` | It is waiting to start. | Pending | |
| 166 | | `in_progress` | It is deploying. | Pending | |
| 167 | | `success` | It is live. | Success | |
| 168 | | `failure` | It did not go live. | Failure | |
| 169 | | `error` | Something went wrong around it, such as a cancelled run. | Error | |
| 170 | | `inactive` | It is no longer what the environment serves. | Left as it was | |
| 171 | |
| 172 | A g1t.page build's statuses are read from the build itself: `queued`, then |
| 173 | `in_progress` while it builds, then how it ended, and `inactive` once a |
| 174 | newer build replaced it or it was taken down. You cannot add statuses to a |
| 175 | g1t.page build: `POST …/deployments/dpl_…/statuses` answers `409`. |
| 176 | |
| 177 | ### auto_inactive |
| 178 | |
| 179 | An environment serves one deployment at a time. When a deployment |
| 180 | succeeds, the environment's older successful deployments each get an |
| 181 | `inactive` status, so only the newest stays current. To keep them as they |
| 182 | are, for example when several deployments are live side by side, send |
| 183 | `"auto_inactive": false` with the `success`. |
| 184 | |
| 185 | ## Environments |
| 186 | |
| 187 | An environment exists once something deploys to it. You do not create |
| 188 | environments first. |
| 189 | |
| 190 | - **Production environments** are those people use directly. An |
| 191 | environment named `production` is one unless the deployment says |
| 192 | `"production_environment": false`. Set it to `true` for others, such as |
| 193 | `live`. |
| 194 | - **Transient environments** go away, such as a review app for one pull |
| 195 | request. Set `"transient_environment": true` on their deployments. |
| 196 | |
| 197 | `GET /repos/{owner}/{name}/environments` lists them: `production` first, |
| 198 | then other production environments, then the rest, most recently deployed |
| 199 | first. Each has: |
| 200 | |
| 201 | | Field | | |
| 202 | | --- | --- | |
| 203 | | `name` | The environment's name, as first spelled. | |
| 204 | | `url` | Where it is served: `current`'s `environment_url`. | |
| 205 | | `production_environment`, `transient_environment` | As its deployments said. | |
| 206 | | `deployments_count` | How many deployments went to it. | |
| 207 | | `latest` | Its newest deployment, whatever its state. | |
| 208 | | `current` | Its newest successful deployment that is still active. Null when none is. | |
| 209 | | `updated_at` | When it last changed. | |
| 210 | |
| 211 | `total_count` counts deployments across every environment. |
| 212 | `GET /repos/{owner}/{name}/environments/{environment}` returns one, matched |
| 213 | without regard to case. URL-encode a name with slashes: |
| 214 | `…/environments/review%2Ffeature-x`. |
| 215 | |
| 216 | An environment with [protection rules](/guides/actions/#environments) |
| 217 | also has `protection_rules` (`required_reviewers`, `wait_timer`, |
| 218 | `branch_policy`), `deployment_branch_policy`, `branch_policies` and |
| 219 | `can_admins_bypass`, and is listed even before anything deploys to it. |
| 220 | `PUT …/environments/{environment}` sets the rules and `DELETE` removes |
| 221 | them. |
| 222 | |
| 223 | ## Read deployments |
| 224 | |
| 225 | | Route | What it returns | |
| 226 | | --- | --- | |
| 227 | | `GET /repos/{owner}/{name}/deployments` | Deployments, newest first, as `{ deployments, total_count, page, per_page }`. | |
| 228 | | `GET /repos/{owner}/{name}/deployments/{id}` | One deployment, with every status in `statuses`, oldest first. | |
| 229 | | `GET /repos/{owner}/{name}/deployments/{id}/statuses` | Its statuses, newest first. | |
| 230 | | `GET /repos/{owner}/{name}/environments` | The environments, as above. | |
| 231 | | `GET /repos/{owner}/{name}/environments/{environment}` | One environment. | |
| 232 | |
| 233 | The list takes these filters: |
| 234 | |
| 235 | | Query | | |
| 236 | | --- | --- | |
| 237 | | `environment` | Only this environment's, matched without regard to case. | |
| 238 | | `ref` | Only deployments of this branch, tag or commit, as it was given. | |
| 239 | | `sha` | Only deployments of this commit, or of commits starting with it. | |
| 240 | | `task` | Only this task's. | |
| 241 | | `state` | Only deployments whose latest status has this state. | |
| 242 | | `source` | `api`, `actions` or `g1t_page`. | |
| 243 | | `creator` | Only those this username made, or `g1t`. | |
| 244 | | `page`, `per_page` | Which page, from 1, and how many on it: 30 unless you say, at most 100. | |
| 245 | |
| 246 | ```sh |
| 247 | curl "https://api.g1t.sh/repos/acme/web/deployments?environment=production&state=success&per_page=5" \ |
| 248 | -H "Authorization: Bearer $G1T_TOKEN" |
| 249 | ``` |
| 250 | |
| 251 | A deployment has these fields: |
| 252 | |
| 253 | | Field | | |
| 254 | | --- | --- | |
| 255 | | `id` | `dep_…`, or `dpl_…` for a g1t.page build. | |
| 256 | | `environment`, `ref`, `sha`, `task`, `description`, `payload` | As reported. | |
| 257 | | `production_environment`, `transient_environment` | As reported. | |
| 258 | | `state` | Its latest status's state. | |
| 259 | | `environment_url`, `log_url` | The latest addresses it was given. | |
| 260 | | `creator` | The username of whoever reported it or started its run, or `g1t`. | |
| 261 | | `source` | `api`, `actions` or `g1t_page`. | |
| 262 | | `run_id`, `run_url` | The g1t Actions run that made it. Null for other sources. | |
| 263 | | `project`, `number` | For a g1t.page build: the project, and for a preview its pull request's number. Null for other sources. | |
| 264 | | `created_at`, `updated_at` | When it was made, and when it last changed. | |
| 265 | |
| 266 | Reading needs the `deployments:read` scope and the Read role. A public |
| 267 | repository's deployments can be read by anyone. Reporting needs |
| 268 | `deployments:write` and the Write role, and an |
| 269 | [archived](/guides/managing-repositories/) repository refuses it with |
| 270 | `409`. Every route, with its example, is in the |
| 271 | [API reference](/reference/api/deployments/list-deployments/). |
| 272 | |
| 273 | ## The commit's check |
| 274 | |
| 275 | Every status sets a check on the deployment's commit, named |
| 276 | `deploy / <environment>`, such as `deploy / staging`. It links to the |
| 277 | deployment's page, `https://g1t.sh/{owner}/{name}/deployments/{id}`. The |
| 278 | [statuses table](#statuses) says which state the check takes. A pull |
| 279 | request whose head was deployed shows the check with its other checks. |
| 280 | |
| 281 | g1t.page builds keep their own check, `g1t / deploy`. See |
| 282 | [previews of branches](/guides/deployments/#previews-of-branches). |
| 283 | |
| 284 | ### Require a deployment before merging |
| 285 | |
| 286 | A ruleset's **Require deployments to succeed** rule |
| 287 | (`required_deployments`) holds a pull request until its head has deployed |
| 288 | successfully to each environment it names. A successful |
| 289 | `deploy / <environment>` check meets it, so any environment you report to, |
| 290 | from anywhere, can be required: |
| 291 | |
| 292 | ```sh |
| 293 | curl -X POST https://api.g1t.sh/repos/acme/web/rulesets \ |
| 294 | -H "Authorization: Bearer $G1T_TOKEN" \ |
| 295 | -H "Content-Type: application/json" \ |
| 296 | -d '{ |
| 297 | "ruleset_name": "Staging first", |
| 298 | "conditions": { "ref_name": { "include": ["~DEFAULT_BRANCH"] } }, |
| 299 | "rules": [ |
| 300 | { "type": "required_deployments", "parameters": { "environments": ["staging"] } } |
| 301 | ] |
| 302 | }' |
| 303 | ``` |
| 304 | |
| 305 | For this to work, your CI deploys each pull request's head to `staging` |
| 306 | and reports it. See [rules](/guides/rules/#pull-requests-and-checks). |
| 307 | |
| 308 | ## Deployments from g1t Actions |
| 309 | |
| 310 | A [g1t Actions](/guides/actions/) job with an `environment:` makes a |
| 311 | deployment by itself. You do not call the API. |
| 312 | |
| 313 | ```yaml |
| 314 | jobs: |
| 315 | deploy: |
| 316 | runs-on: ubuntu-latest |
| 317 | environment: production |
| 318 | steps: |
| 319 | - uses: actions/checkout@v4 |
| 320 | - run: ./deploy.sh |
| 321 | ``` |
| 322 | |
| 323 | Give the environment's address with `url`. Its expressions are filled in |
| 324 | from the `github`, `inputs` and `matrix` contexts, and it must be an |
| 325 | `http` or `https` address: |
| 326 | |
| 327 | ```yaml |
| 328 | jobs: |
| 329 | deploy: |
| 330 | runs-on: ubuntu-latest |
| 331 | environment: |
| 332 | name: staging |
| 333 | url: https://staging.example.com/${{ github.sha }} |
| 334 | steps: |
| 335 | - uses: actions/checkout@v4 |
| 336 | - run: ./deploy.sh staging |
| 337 | ``` |
| 338 | |
| 339 | A run makes one deployment per environment, however many of its jobs name |
| 340 | it. A matrix that deploys to three regions makes one `production` |
| 341 | deployment, not three: |
| 342 | |
| 343 | ```yaml |
| 344 | jobs: |
| 345 | deploy: |
| 346 | runs-on: ubuntu-latest |
| 347 | strategy: |
| 348 | matrix: |
| 349 | region: [us-east, eu-west, ap-south] |
| 350 | environment: production |
| 351 | steps: |
| 352 | - uses: actions/checkout@v4 |
| 353 | - run: ./deploy.sh ${{ matrix.region }} |
| 354 | ``` |
| 355 | |
| 356 | How the deployment goes: |
| 357 | |
| 358 | 1. It is made, `in_progress`, when the first job that names the |
| 359 | environment starts. |
| 360 | 2. If one of those jobs fails, it is marked `failure` at once, unless the |
| 361 | job has `continue-on-error`. |
| 362 | 3. When the run finishes, it settles: `failure` if any of those jobs |
| 363 | failed, `error` if the run was cancelled, and `success` otherwise. Jobs |
| 364 | that were skipped do not count. If none of them ran, no deployment is |
| 365 | made. |
| 366 | |
| 367 | Its `ref` is the run's branch, `sha` the run's commit, `creator` whoever |
| 368 | started the run, and `log_url` and `run_url` the run's page. A run |
| 369 | attempted again makes a deployment of its own. Jobs on |
| 370 | [self-hosted runners](/guides/self-hosted-runners/) make deployments the |
| 371 | same way. |
| 372 | |
| 373 | A job that needs an environment's |
| 374 | [secrets and variables](/guides/secrets-and-variables/#a-value-per-environment) |
| 375 | but does not deploy, such as one that plans a change, says |
| 376 | `deployment: false`: |
| 377 | |
| 378 | ```yaml |
| 379 | jobs: |
| 380 | plan: |
| 381 | runs-on: ubuntu-latest |
| 382 | environment: |
| 383 | name: production |
| 384 | deployment: false |
| 385 | steps: |
| 386 | - uses: actions/checkout@v4 |
| 387 | - run: ./plan.sh |
| 388 | ``` |
| 389 | |
| 390 | A job's `G1T_TOKEN` can also report deployments of its own with the API, |
| 391 | given `deployments: write` in its [`permissions:`](/guides/actions/#the-jobs-token). |
| 392 | |
| 393 | A job that names an environment with |
| 394 | [protection rules](/guides/actions/#environments) waits for them before it |
| 395 | starts, and its deployment is made only once it does. |
| 396 | |
| 397 | ## Webhooks |
| 398 | |
| 399 | [Webhooks](/guides/webhooks/) send two events for deployments of every |
| 400 | source, g1t.page builds included: |
| 401 | |
| 402 | | Event | When | `data` | |
| 403 | | --- | --- | --- | |
| 404 | | `deployment.created` | A deployment was made. | `repo_id`, `deployment` (without `payload`) | |
| 405 | | `deployment_status.created` | A deployment got a status. | `repo_id`, `deployment` (without `payload`), `deployment_status` | |
| 406 | |
| 407 | `deployment.succeeded` and `deployment.failed` are sent for g1t.page builds |
| 408 | only. |
| 409 | |
| 410 | ## MCP |
| 411 | |
| 412 | The [`workflow` tool](/reference/mcp/#workflow) has an action for each route: |
| 413 | |
| 414 | | Action | Route | |
| 415 | | --- | --- | |
| 416 | | `list_deployments` | `GET /repos/{owner}/{name}/deployments` | |
| 417 | | `get_deployment` | `GET /repos/{owner}/{name}/deployments/{id}` | |
| 418 | | `create_deployment` | `POST /repos/{owner}/{name}/deployments` | |
| 419 | | `deployment_statuses` | `GET /repos/{owner}/{name}/deployments/{id}/statuses` | |
| 420 | | `create_deployment_status` | `POST /repos/{owner}/{name}/deployments/{id}/statuses` | |
| 421 | | `list_environments` | `GET /repos/{owner}/{name}/environments` | |
| 422 | | `get_environment` | `GET /repos/{owner}/{name}/environments/{environment}` | |
| 423 | | `update_environment` | `PUT /repos/{owner}/{name}/environments/{environment}` | |
| 424 | | `delete_environment` | `DELETE /repos/{owner}/{name}/environments/{environment}` | |
| 425 | |
| 426 | They take the repository as `repo`, written `owner/name`, and the same |
| 427 | fields as the routes. |
| 428 | |
| 429 | ## On the site |
| 430 | |
| 431 | - **The Deployments page**, `g1t.sh/<owner>/<project>/deployments`, shows |
| 432 | a card for each environment: its state, address, commit, ref, who |
| 433 | deployed it and from where, when, and how many deployments it has had. |
| 434 | Below is every deployment, newest first, filtered by **Environment**, |
| 435 | **State**, **Source**, **Creator** and **Ref**. Projects deployed to |
| 436 | g1t.page manage their apps under **On g1t.page** on the same page. |
| 437 | - **A deployment's page**, `g1t.sh/<owner>/<project>/deployments/<id>`, |
| 438 | shows its statuses in order, links to its log and run, and its payload. |
| 439 | - **The repository's code page** and **the project's overview** show a |
| 440 | **Deployments** panel with how many there are, and each environment's |
| 441 | latest deployment and when. It links to the Deployments page. |
| 442 | - **The project's overview** shows a production environment deployed |
| 443 | elsewhere in its production card: its address, state, commit and when it |
| 444 | went up. |