Skip to content
444 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Merge branch 'main' into worktree-agent-a69aeabc4b0deeb971---
2title: Deployments API
3description: 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
6A repository keeps one list of its deployments, wherever they ran. A
7deployment 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
9is served at. Each deployment has a list of statuses that say how it went,
10and its latest status is its state.
11
12Deployments 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
21A status's id starts `dst_`. Every deployment, whatever its source, shows
22on the repository's **Deployments** page, sets a check on its commit, and
23sends [webhooks](#webhooks).
24
25## Report a deployment from any CI
26
27You report a deployment by creating it, then adding a status each time it
28moves on. The base URL is `https://api.g1t.sh`, and every request carries
29an access token as `Authorization: Bearer g1t_…`.
30
31### Make a token
32
331. Open your **Settings → Access tokens**, or a workspace's
34 **Settings → Access tokens** for a token that belongs to the workspace.
352. Give the token a name, such as `release-pipeline`.
363. 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**.
394. Choose an expiry and select **Create token**. Copy the token now: it is
40 not shown again.
415. Store it in your CI as a secret named `G1T_TOKEN`.
42
43Reporting also needs the Write [role](/guides/access-and-roles/) on the
44repository. 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
52curl -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
64The answer is the deployment, with its first status in `statuses`. Keep
65its `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
84the deploy succeeds, give the address it is served at:
85
86```sh
87curl -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
97When it fails:
98
99```sh
100curl -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
118The deployment takes the status's state, and any address the status gives.
119
120### A script for any CI
121
122This script wraps a deploy command. It needs `curl`, `jq`, and three
123variables: `G1T_TOKEN`, `G1T_REPO` (such as `acme/web`) and `GIT_COMMIT`
124(the commit being deployed). Change `ENVIRONMENT`, `ENVIRONMENT_URL` and
125the deploy command to yours.
126
127```sh
128#!/bin/sh
129set -eu
130
131API="https://api.g1t.sh/repos/$G1T_REPO/deployments"
132ENVIRONMENT="staging"
133ENVIRONMENT_URL="https://staging.example.com"
134
135report() {
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.
143ID=$(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.
149if ./deploy.sh; then
150 report "$API/$ID/statuses" "$(jq -n --arg url "$ENVIRONMENT_URL" \
151 '{state: "success", environment_url: $url}')" > /dev/null
152else
153 report "$API/$ID/statuses" '{"state": "failure", "description": "The deploy command failed"}' > /dev/null
154 exit 1
155fi
156```
157
158Give `log_url` in both calls to link the deployment to your CI's page for
159the 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
172A 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
174newer build replaced it or it was taken down. You cannot add statuses to a
175g1t.page build: `POST …/deployments/dpl_…/statuses` answers `409`.
176
177### auto_inactive
178
179An environment serves one deployment at a time. When a deployment
180succeeds, the environment's older successful deployments each get an
181`inactive` status, so only the newest stays current. To keep them as they
182are, for example when several deployments are live side by side, send
183`"auto_inactive": false` with the `success`.
184
185## Environments
186
187An environment exists once something deploys to it. You do not create
188environments 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,
198then other production environments, then the rest, most recently deployed
199first. 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
213without regard to case. URL-encode a name with slashes:
214`…/environments/review%2Ffeature-x`.
215
Actions: keep workflow runs safe216An environment with [protection rules](/guides/actions/#environments)
217also 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
221them.
222
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97223## 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
233The 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
247curl "https://api.g1t.sh/repos/acme/web/deployments?environment=production&state=success&per_page=5" \
248 -H "Authorization: Bearer $G1T_TOKEN"
249```
250
251A 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
266Reading needs the `deployments:read` scope and the Read role. A public
267repository'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
275Every status sets a check on the deployment's commit, named
276`deploy / <environment>`, such as `deploy / staging`. It links to the
277deployment's page, `https://g1t.sh/{owner}/{name}/deployments/{id}`. The
278[statuses table](#statuses) says which state the check takes. A pull
279request whose head was deployed shows the check with its other checks.
280
281g1t.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
286A ruleset's **Require deployments to succeed** rule
287(`required_deployments`) holds a pull request until its head has deployed
288successfully to each environment it names. A successful
289`deploy / <environment>` check meets it, so any environment you report to,
290from anywhere, can be required:
291
292```sh
293curl -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
305For this to work, your CI deploys each pull request's head to `staging`
306and reports it. See [rules](/guides/rules/#pull-requests-and-checks).
307
308## Deployments from g1t Actions
309
310A [g1t Actions](/guides/actions/) job with an `environment:` makes a
311deployment by itself. You do not call the API.
312
313```yaml
314jobs:
315 deploy:
316 runs-on: ubuntu-latest
317 environment: production
318 steps:
319 - uses: actions/checkout@v4
320 - run: ./deploy.sh
321```
322
323Give the environment's address with `url`. Its expressions are filled in
324from the `github`, `inputs` and `matrix` contexts, and it must be an
325`http` or `https` address:
326
327```yaml
328jobs:
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
339A run makes one deployment per environment, however many of its jobs name
340it. A matrix that deploys to three regions makes one `production`
341deployment, not three:
342
343```yaml
344jobs:
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
356How the deployment goes:
357
3581. It is made, `in_progress`, when the first job that names the
359 environment starts.
3602. If one of those jobs fails, it is marked `failure` at once, unless the
361 job has `continue-on-error`.
3623. 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
367Its `ref` is the run's branch, `sha` the run's commit, `creator` whoever
368started the run, and `log_url` and `run_url` the run's page. A run
369attempted again makes a deployment of its own. Jobs on
370[self-hosted runners](/guides/self-hosted-runners/) make deployments the
371same way.
372
373A job that needs an environment's
374[secrets and variables](/guides/secrets-and-variables/#a-value-per-environment)
375but does not deploy, such as one that plans a change, says
376`deployment: false`:
377
378```yaml
379jobs:
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
Actions: keep workflow runs safe390A job's `G1T_TOKEN` can also report deployments of its own with the API,
391given `deployments: write` in its [`permissions:`](/guides/actions/#the-jobs-token).
392
393A job that names an environment with
394[protection rules](/guides/actions/#environments) waits for them before it
395starts, and its deployment is made only once it does.
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97396
397## Webhooks
398
399[Webhooks](/guides/webhooks/) send two events for deployments of every
400source, 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
408only.
409
410## MCP
411
412The [`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}` |
Actions: keep workflow runs safe423| `update_environment` | `PUT /repos/{owner}/{name}/environments/{environment}` |
424| `delete_environment` | `DELETE /repos/{owner}/{name}/environments/{environment}` |
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97425
426They take the repository as `repo`, written `owner/name`, and the same
427fields 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.

This file's history is long; its oldest lines are credited to the oldest commit read.