Guides
Start runs from CI
Give your CI a key that can start runs on one project, call one endpoint, and hear back when a person needs to look.
The examples use your workspace’s address, .
-
Before you start
You need a project nightlab can already run: setup’s four required steps are done. A run you start with a key is like one started by hand in the portal: it starts as soon as there’s a slot, and doesn’t wait for anyone to approve it. While your workspace’s writes are off, it’s a dry run: nothing is written to your repository or tracker.
-
Make a key
A workspace admin makes it in the portal, under Settings › Integrations › API keys, or through the workspace’s admin API,
POST /workspace/api-keys. Give itruns:write(start and cancel runs) andruns:read, only the project your CI builds, and an expiry; 90 days is the default. If your CI calls from fixed addresses, add them to the key’s allow-list.A key can start, read and cancel runs. It can’t approve a run, merge, or change a setting, and a person still reviews every draft pull request. It sees every run in its projects, whatever a person’s tracker access allows, so give it to systems, not people.
-
Store it in your CI
The key is shown once. Copy it into your CI’s secrets as
NIGHTLAB_API_KEY. It never appears in these pages, signed in or not. To check it works, ask which key it is:curl -sS https://<workspace>.nightlab.io/v1/key -H "Authorization: Bearer $NIGHTLAB_API_KEY"The answer names the key, its scopes and projects, and when it expires.
-
Start a run
Call
POST /v1/runswith the issue. The issue picks the project. Send anIdempotency-Key, so a retried job gets the first answer back instead of starting a second run, and your ownexternal_ref, which comes back on the run and on every event about it.- name: Ask nightlab to build the issue run: | curl --fail-with-body -sS https://<workspace>.nightlab.io/v1/runs \ -H "Authorization: Bearer $NIGHTLAB_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $GITHUB_RUN_ID" \ -d "{\"issue\": \"SHOP-1431\", \"external_ref\": \"$GITHUB_RUN_ID\"}" env: NIGHTLAB_API_KEY: ${{ secrets.NIGHTLAB_API_KEY }}nightlab: script: - > curl --fail-with-body -sS https://<workspace>.nightlab.io/v1/runs -H "Authorization: Bearer $NIGHTLAB_API_KEY" -H "Content-Type: application/json" -H "Idempotency-Key: $CI_PIPELINE_ID" -d "{\"issue\": \"SHOP-1431\", \"external_ref\": \"$CI_PIPELINE_ID\"}" # NIGHTLAB_API_KEY: a masked CI/CD variablecurl --fail-with-body -sS https://<workspace>.nightlab.io/v1/runs \ -H "Authorization: Bearer $NIGHTLAB_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: build-SHOP-1431-1" \ -d '{"issue": "SHOP-1431", "external_ref": "build-SHOP-1431-1"}'It answers
201with the run: keep itsid. A retry with the sameIdempotency-Keyand body gets that same answer for 24 hours, markedIdempotent-Replayed: true. -
Hear back
Recommended: a webhook
Rather than polling, have a workspace admin add a webhook for
run.waiting(a person needs to answer a question),run.pr_openedandrun.failed: nightlab tells you the moment a person is needed, and your job can fail its own check when the run does. Each delivery is signed, and carries the run with yourexternal_ref, so you can match it to the job.Polling works too:
GET /v1/runs/{id}, orGET /v1/runs?external_ref=…to find it by your reference, withruns:read. -
When it fails
Every refusal is
application/problem+json, with a sentence saying what to change.401: no key, or one that’s unknown, revoked or expired.403: the key lacks the scope, the issue’s project isn’t one of the key’s, or the call came from outside its allow-list.409: setup isn’t finished, the project has no repository nightlab can reach, the issue already has a run in progress (the answer names it, so follow it withGET /v1/runs/{id}), or a request with the sameIdempotency-Keyis still being answered.422: a field is wrong, or theIdempotency-Keywas used with a different body.429: the key’s rate limit, 600 reads and 60 writes a minute, withRetry-Aftersaying when to try again; or the workspace’s run slots are all in use.402: the budget is used up until it renews, or an admin raises it.