Skip to content

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.

A repository keeps one list of its deployments, wherever they ran. A deployment is one commit sent to one environment, such as production or staging. An environment is the place it went: a name, and the address it is served at. Each deployment has a list of statuses that say how it went, and its latest status is its state.

Deployments reach that list three ways. Each deployment says which in its source:

source Made by Ids
api Any CI or script, through the routes on this page, with an access token. dep_…
actions A g1t Actions job with an environment:, by itself. dep_…
g1t_page A g1t.page build of a project, to the production or preview environment. dpl_…

A status’s id starts dst_. Every deployment, whatever its source, shows on the repository’s Deployments page, sets a check on its commit, and sends webhooks.

You report a deployment by creating it, then adding a status each time it moves on. The base URL is https://api.g1t.sh, and every request carries an access token as Authorization: Bearer g1t_….

  1. Open your Settings → Access tokens, or a workspace’s Settings → Access tokens for a token that belongs to the workspace.
  2. Give the token a name, such as release-pipeline.
  3. Select the CI preset. It includes deployments:read and deployments:write. To report deployments and nothing else, tick only deployments:write under Deployments.
  4. Choose an expiry and select Create token. Copy the token now: it is not shown again.
  5. Store it in your CI as a secret named G1T_TOKEN.

Reporting also needs the Write role on the repository. See scopes.

POST /repos/{owner}/{name}/deployments creates a deployment. Give it state: "in_progress" when the deploy has started:

curl -X POST https://api.g1t.sh/repos/acme/web/deployments \
-H "Authorization: Bearer $G1T_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ref": "main",
"environment": "staging",
"description": "Deployed by the release pipeline",
"state": "in_progress",
"log_url": "https://ci.example.com/pipelines/4182"
}'

The answer is the deployment, with its first status in statuses. Keep its id for the next call.

Field Default
ref Required The branch, tag or commit deployed, such as main or v1.4.0.
sha Resolved from ref The commit deployed. Give the whole commit id to skip resolving ref.
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.
task deploy What kind of deployment, such as deploy:migrations. Up to 100 characters.
description None A short note, up to 1,000 characters.
payload {} Anything else to keep with it: a JSON object, or a JSON string of one, up to 64 KB. Returned as given.
production_environment true for production, else false Whether people use this environment directly.
transient_environment false Whether the environment goes away, such as a review app.
state queued Its first status. See statuses.
environment_url None Where it is served, an http or https address.
log_url None Where its output can be read, an http or https address.

POST /repos/{owner}/{name}/deployments/{id}/statuses adds a status. When the deploy succeeds, give the address it is served at:

curl -X POST https://api.g1t.sh/repos/acme/web/deployments/dep_01kq7z9a1c3e5g7j9m1p3r5t7v/statuses \
-H "Authorization: Bearer $G1T_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"state": "success",
"environment_url": "https://staging.example.com",
"log_url": "https://ci.example.com/pipelines/4182"
}'

When it fails:

curl -X POST https://api.g1t.sh/repos/acme/web/deployments/dep_01kq7z9a1c3e5g7j9m1p3r5t7v/statuses \
-H "Authorization: Bearer $G1T_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"state": "failure",
"description": "Smoke tests failed",
"log_url": "https://ci.example.com/pipelines/4182"
}'
Field Default
state Required queued, in_progress, success, failure, error or inactive.
description None A short note, up to 1,000 characters.
environment_url None Where it is served, an http or https address.
log_url None Where its output can be read, an http or https address.
auto_inactive true On a success, give the environment’s older successful deployments an inactive status. See auto_inactive.

The deployment takes the status’s state, and any address the status gives.

This script wraps a deploy command. It needs curl, jq, and three variables: G1T_TOKEN, G1T_REPO (such as acme/web) and GIT_COMMIT (the commit being deployed). Change ENVIRONMENT, ENVIRONMENT_URL and the deploy command to yours.

#!/bin/sh
set -eu
API="https://api.g1t.sh/repos/$G1T_REPO/deployments"
ENVIRONMENT="staging"
ENVIRONMENT_URL="https://staging.example.com"
report() {
curl -fsS -X POST "$1" \
-H "Authorization: Bearer $G1T_TOKEN" \
-H "Content-Type: application/json" \
-d "$2"
}
# 1. Create the deployment, in progress.
ID=$(report "$API" "$(jq -n \
--arg ref "$GIT_COMMIT" \
--arg environment "$ENVIRONMENT" \
'{ref: $ref, environment: $environment, state: "in_progress"}')" | jq -r .id)
# 2. Deploy, then report how it went.
if ./deploy.sh; then
report "$API/$ID/statuses" "$(jq -n --arg url "$ENVIRONMENT_URL" \
'{state: "success", environment_url: $url}')" > /dev/null
else
report "$API/$ID/statuses" '{"state": "failure", "description": "The deploy command failed"}' > /dev/null
exit 1
fi

Give log_url in both calls to link the deployment to your CI’s page for the job.

State Means The commit’s check
queued It is waiting to start. Pending
in_progress It is deploying. Pending
success It is live. Success
failure It did not go live. Failure
error Something went wrong around it, such as a cancelled run. Error
inactive It is no longer what the environment serves. Left as it was

A g1t.page build’s statuses are read from the build itself: queued, then in_progress while it builds, then how it ended, and inactive once a newer build replaced it or it was taken down. You cannot add statuses to a g1t.page build: POST …/deployments/dpl_…/statuses answers 409.

An environment serves one deployment at a time. When a deployment succeeds, the environment’s older successful deployments each get an inactive status, so only the newest stays current. To keep them as they are, for example when several deployments are live side by side, send "auto_inactive": false with the success.

An environment exists once something deploys to it. You do not create environments first.

  • Production environments are those people use directly. An environment named production is one unless the deployment says "production_environment": false. Set it to true for others, such as live.
  • Transient environments go away, such as a review app for one pull request. Set "transient_environment": true on their deployments.

GET /repos/{owner}/{name}/environments lists them: production first, then other production environments, then the rest, most recently deployed first. Each has:

Field
name The environment’s name, as first spelled.
url Where it is served: current’s environment_url.
production_environment, transient_environment As its deployments said.
deployments_count How many deployments went to it.
latest Its newest deployment, whatever its state.
current Its newest successful deployment that is still active. Null when none is.
updated_at When it last changed.

total_count counts deployments across every environment. GET /repos/{owner}/{name}/environments/{environment} returns one, matched without regard to case. URL-encode a name with slashes: …/environments/review%2Ffeature-x.

An environment with protection rules also has protection_rules (required_reviewers, wait_timer, branch_policy), deployment_branch_policy, branch_policies and can_admins_bypass, and is listed even before anything deploys to it. PUT …/environments/{environment} sets the rules and DELETE removes them.

Route What it returns
GET /repos/{owner}/{name}/deployments Deployments, newest first, as { deployments, total_count, page, per_page }.
GET /repos/{owner}/{name}/deployments/{id} One deployment, with every status in statuses, oldest first.
GET /repos/{owner}/{name}/deployments/{id}/statuses Its statuses, newest first.
GET /repos/{owner}/{name}/environments The environments, as above.
GET /repos/{owner}/{name}/environments/{environment} One environment.

The list takes these filters:

Query
environment Only this environment’s, matched without regard to case.
ref Only deployments of this branch, tag or commit, as it was given.
sha Only deployments of this commit, or of commits starting with it.
task Only this task’s.
state Only deployments whose latest status has this state.
source api, actions or g1t_page.
creator Only those this username made, or g1t.
page, per_page Which page, from 1, and how many on it: 30 unless you say, at most 100.
curl "https://api.g1t.sh/repos/acme/web/deployments?environment=production&state=success&per_page=5" \
-H "Authorization: Bearer $G1T_TOKEN"

A deployment has these fields:

Field
id dep_…, or dpl_… for a g1t.page build.
environment, ref, sha, task, description, payload As reported.
production_environment, transient_environment As reported.
state Its latest status’s state.
environment_url, log_url The latest addresses it was given.
creator The username of whoever reported it or started its run, or g1t.
source api, actions or g1t_page.
run_id, run_url The g1t Actions run that made it. Null for other sources.
project, number For a g1t.page build: the project, and for a preview its pull request’s number. Null for other sources.
created_at, updated_at When it was made, and when it last changed.

Reading needs the deployments:read scope and the Read role. A public repository’s deployments can be read by anyone. Reporting needs deployments:write and the Write role, and an archived repository refuses it with 409. Every route, with its example, is in the API reference.

Every status sets a check on the deployment’s commit, named deploy / <environment>, such as deploy / staging. It links to the deployment’s page, https://g1t.sh/{owner}/{name}/deployments/{id}. The statuses table says which state the check takes. A pull request whose head was deployed shows the check with its other checks.

g1t.page builds keep their own check, g1t / deploy. See previews of branches.

A ruleset’s Require deployments to succeed rule (required_deployments) holds a pull request until its head has deployed successfully to each environment it names. A successful deploy / <environment> check meets it, so any environment you report to, from anywhere, can be required:

curl -X POST https://api.g1t.sh/repos/acme/web/rulesets \
-H "Authorization: Bearer $G1T_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ruleset_name": "Staging first",
"conditions": { "ref_name": { "include": ["~DEFAULT_BRANCH"] } },
"rules": [
{ "type": "required_deployments", "parameters": { "environments": ["staging"] } }
]
}'

For this to work, your CI deploys each pull request’s head to staging and reports it. See rules.

A g1t Actions job with an environment: makes a deployment by itself. You do not call the API.

jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh

Give the environment’s address with url. Its expressions are filled in from the github, inputs and matrix contexts, and it must be an http or https address:

jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com/${{ github.sha }}
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh staging

A run makes one deployment per environment, however many of its jobs name it. A matrix that deploys to three regions makes one production deployment, not three:

jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
region: [us-east, eu-west, ap-south]
environment: production
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh ${{ matrix.region }}

How the deployment goes:

  1. It is made, in_progress, when the first job that names the environment starts.
  2. If one of those jobs fails, it is marked failure at once, unless the job has continue-on-error.
  3. When the run finishes, it settles: failure if any of those jobs failed, error if the run was cancelled, and success otherwise. Jobs that were skipped do not count. If none of them ran, no deployment is made.

Its ref is the run’s branch, sha the run’s commit, creator whoever started the run, and log_url and run_url the run’s page. A run attempted again makes a deployment of its own. Jobs on self-hosted runners make deployments the same way.

A job that needs an environment’s secrets and variables but does not deploy, such as one that plans a change, says deployment: false:

jobs:
plan:
runs-on: ubuntu-latest
environment:
name: production
deployment: false
steps:
- uses: actions/checkout@v4
- run: ./plan.sh

A job’s G1T_TOKEN can also report deployments of its own with the API, given deployments: write in its permissions:.

A job that names an environment with protection rules waits for them before it starts, and its deployment is made only once it does.

Webhooks send two events for deployments of every source, g1t.page builds included:

Event When data
deployment.created A deployment was made. repo_id, deployment (without payload)
deployment_status.created A deployment got a status. repo_id, deployment (without payload), deployment_status

deployment.succeeded and deployment.failed are sent for g1t.page builds only.

The workflow tool has an action for each route:

Action Route
list_deployments GET /repos/{owner}/{name}/deployments
get_deployment GET /repos/{owner}/{name}/deployments/{id}
create_deployment POST /repos/{owner}/{name}/deployments
deployment_statuses GET /repos/{owner}/{name}/deployments/{id}/statuses
create_deployment_status POST /repos/{owner}/{name}/deployments/{id}/statuses
list_environments GET /repos/{owner}/{name}/environments
get_environment GET /repos/{owner}/{name}/environments/{environment}
update_environment PUT /repos/{owner}/{name}/environments/{environment}
delete_environment DELETE /repos/{owner}/{name}/environments/{environment}

They take the repository as repo, written owner/name, and the same fields as the routes.

  • The Deployments page, g1t.sh/<owner>/<project>/deployments, shows a card for each environment: its state, address, commit, ref, who deployed it and from where, when, and how many deployments it has had. Below is every deployment, newest first, filtered by Environment, State, Source, Creator and Ref. Projects deployed to g1t.page manage their apps under On g1t.page on the same page.
  • A deployment’s page, g1t.sh/<owner>/<project>/deployments/<id>, shows its statuses in order, links to its log and run, and its payload. Only the newest status can show as still under way; a queued or in_progress one that a later status followed shows as over.
  • Every link to where something runs, a g1t.page address, an environment’s URL, a pull request’s preview or a project’s homepage, opens in a new tab, wherever on the site it appears.
  • The repository’s code page and the project’s overview show a Deployments panel with how many there are, and each environment’s latest deployment and when. It links to the Deployments page.
  • The project’s overview shows a production environment deployed elsewhere in its production card: its address, state, commit and when it went up.