API reference
The 129 routes of the published API contract, generated from it. The CLI and the TypeScript SDK call the same routes, so anything you can do from them you can do over HTTP.
Basics
- Base URL:
https://api.promigence.ai. Requests and answers are JSON unless a route says otherwise. - Authentication:
Authorization: Bearer <API key>. Create a key withpromigence keys createor in the console;promigence loginstores one for the CLI and the SDK. - Errors: an answer that is not a success carries
{"error": {...}}with a stablecode, amessage, afix, the CLIexitcode, itscategory,retryableand arequest_id. Every error code - Retries: send an
Idempotency-Keyheader with a create, and a retry of the same request starts, and bills, once. - The contract itself, the document this page is generated from, as OpenAPI 3.1 for client generators and tools: /openapi.json
curl -s https://api.promigence.ai/v1/runs \
-H "Authorization: Bearer $PROMIGENCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"snapshot": "promigence/base", "count": 2, "spend_cap": 1, "exec": {"sh": "python3 -c \"print(6*7)\""}}'
Language: bashMeta
All 4 in full- GET /statusThe same data rendered as a self-contained HTML page by the API itself.
- GET /v1/healthLiveness, and whether the service can start sandboxes.
- GET /v1/llms.txtThe agent guide as
text/plain— the same documentpromigence agent-guideprints. - GET /v1/status24 hours of synthetic check results plus service health, as JSON.
Snapshots
All 13 in full- POST /v1/images/resolveTurn an image tag into a digest.
- GET /v1/snapshotsList snapshots, newest first.
- POST /v1/snapshotsBuild and verify a snapshot.
- GET /v1/snapshots/{id}One snapshot by id or alias.
- DELETE /v1/snapshots/{id}Remove one of your own snapshots from your catalogue.
- PUT /v1/snapshots/{id}/aliasMove the snapshot's alias.
- GET /v1/snapshots/{id}/manifestThe manifest: what a
.promigencebundle is built from. - POST /v1/snapshots/{id}/verifyRe-run the snapshot's own verify command in a fresh fork.
- GET /v1/snapshots/buildsList your org's builds.
- GET /v1/snapshots/builds/{id}One build.
?wait_ms=Nlong-polls until the build reaches a terminal state, capped at 30 s server-side. - GET /v1/snapshots/builds/{id}/build-logThe tail of the build log.
- GET /v1/templatesThe templates: ready-made starting points to fork (languages, app starters, agent tools, benchmarks).
- GET /v1/templates/{name}One template: its setup steps, everything it has installed (each package and its version, by package manager, and each tool's version), and every version with what changed.
GitHub
All 7 in full- GET /v1/integrations/githubYour organization's GitHub connections, the ones still being finished, and the snapshots that follow a branch.
- POST /v1/integrations/github/connectStart connecting a GitHub account: a link to install the platform's GitHub App on it, valid for 15 minutes.
- DELETE /v1/integrations/github/installations/{installation_id}Disconnect a GitHub account: nothing is built from it any more and the snapshots that followed its branches stop following them.
- GET /v1/integrations/github/reposThe repositories your GitHub connections can read, one page at a time (
page,per_pageup to 100). - POST /v1/integrations/github/requestsStart connecting a GitHub account in the console, for yourself: a one-time ticket (5 minutes) and the console page that uses it (
connect_url). - GET /v1/integrations/github/requests/{request_id}How a GitHub connection request you started ended:
pending, thenconnected,updated(already connected; its repository access changed),pending_provider_approval(an owner of the account must approve it on GitHub first),declined,other_org,needs_admin,not_installer,wrong_user,expiredorfailed, with a sentence that says what happened and what to do next. - DELETE /v1/integrations/github/tracks/{track_id}Stop a snapshot following its branch.
GitLab
All 7 in full- GET /v1/integrations/gitlabYour organization's GitLab connections (each with the URL and token of an optional project webhook that makes tracked branches build at once), the ones waiting for an owner to confirm, and the snapshots that follow a branch.
- POST /v1/integrations/gitlab/connectStart connecting a GitLab user: a link to authorize the platform's GitLab application, read-only (
read_api read_repository), valid for 15 minutes. - DELETE /v1/integrations/gitlab/connections/{connection_id}Disconnect a GitLab user: the authorization is revoked on GitLab, nothing is built from it any more and the snapshots that followed branches through it stop following them.
- GET /v1/integrations/gitlab/reposThe projects a GitLab connection can read (the oldest working one unless
connection_idnames another), one page at a time (page,per_pageup to 100). - POST /v1/integrations/gitlab/requestsStart connecting a GitLab user in the console, for yourself: a one-time ticket (5 minutes) and the console page that uses it (
connect_url). - GET /v1/integrations/gitlab/requests/{request_id}How a GitLab connection request you started ended:
pending, thenconnected,already_connected,declined,wrong_user,expiredorfailed, with a sentence that says what happened and what to do next. - DELETE /v1/integrations/gitlab/tracks/{track_id}Stop a snapshot following its GitLab branch.
Runs
All 12 in full- DELETE /v1/captures/{id}Destroy a preserved failure now.
- GET /v1/runsList your org's runs, newest first.
- POST /v1/runsFork N sandboxes from one snapshot, optionally running a command in each.
- GET /v1/runs/{id}One run and its sandboxes.
- DELETE /v1/runs/{id}Cancel the queue and kill every live sandbox of the run.
- POST /v1/runs/{id}/cancelStop what is still queued.
- GET /v1/runs/{id}/capturesWhat is preserved of this run's failures, and when each goes.
- POST /v1/runs/{id}/openReopen a preserved failure as a new, live sandbox — the state the sandbox was in when it broke, not a re-run from the start.
- POST /v1/runs/{id}/replayRe-fork the sandboxes whose outcome was an environment failure.
- GET /v1/runs/{id}/reportThe validity report: every sandbox, its outcome, and whether the failure was the environment's or the task's.
- GET /v1/runs/{id}/sandboxesOnly the sandboxes of a run.
- POST /v1/runs/quotePrice a run without starting one.
Sandboxes
All 10 in full- GET /v1/sandboxesList your org's sandboxes, newest first.
- GET /v1/sandboxes/{id}One sandbox, with its execs.
- PATCH /v1/sandboxes/{id}Extend a live sandbox's total lifetime, and set when it parks itself.
- DELETE /v1/sandboxes/{id}Kill it now. The answer's
outcomesays how it ended:aborted_by_clientfor a sandbox that had started, billed for the seconds it used;not_startedfor one still queued, which is not billed; or, when its command had already returned, that command's own outcome. - GET /v1/sandboxes/{id}/networkRead the network policy a live sandbox is running under.
- POST /v1/sandboxes/{id}/networkCut a live sandbox's egress.
- POST /v1/sandboxes/{id}/pausePark the sandbox: it does not count against your concurrency limit, and paused time is not billed.
- POST /v1/sandboxes/{id}/probeRe-check the sandbox's files against what the verified snapshot recorded.
- POST /v1/sandboxes/{id}/resumeBring a paused sandbox back where it was, and restart the billing clock.
- POST /v1/sandboxes/{id}/snapshotSnapshot a running sandbox without stopping it, and get back an ordinary snapshot id you can fork.
Execs
All 7 in full- GET /v1/execs/{id}One exec by id alone, without knowing its sandbox or run.
- GET /v1/sandboxes/{id}/execsList this sandbox's background execs.
- POST /v1/sandboxes/{id}/execsRun a command. Buffered by default;
Accept: text/event-streamstreams it;background: truereturns 202 with a handle instead of waiting. - GET /v1/sandboxes/{id}/execs/{exec_id}Read a background exec.
- DELETE /v1/sandboxes/{id}/execs/{exec_id}SIGKILL the background exec and drop its handle and buffered output.
- POST /v1/sandboxes/{id}/execs/{exec_id}/signalSignal a background exec.
- POST /v1/sandboxes/{id}/execs/{exec_id}/stdinWrite to a running command's standard input, and close it.
Files
All 12 in full- GET /v1/sandboxes/{id}/filesStream a file out.
?path=. - PUT /v1/sandboxes/{id}/filesWrite a file in.
?path=; the body is the bytes. - DELETE /v1/sandboxes/{id}/filesDelete a path. Idempotent: a missing path answers
existed: false, not 404. - POST /v1/sandboxes/{id}/files/archivePack a directory into a tar **inside the sandbox** and answer with where it landed and how big it is.
- POST /v1/sandboxes/{id}/files/chmodChange permissions and ownership.
- POST /v1/sandboxes/{id}/files/copyCopy a file, or a whole tree with
recursive. - POST /v1/sandboxes/{id}/files/extractUnpack a tar that is already inside the sandbox.
- GET /v1/sandboxes/{id}/files/listList a directory.
?path=, plus?recursive=1,?depth=N,?limit=Nand?hidden=0. - POST /v1/sandboxes/{id}/files/mkdirCreate a directory.
parentsismkdir -pand makes an existing directory a success; without it a missing parent is a 404 and an existing path is a conflict, which is what a caller using the call as a lock wants. - POST /v1/sandboxes/{id}/files/moveRename or move a path.
- GET /v1/sandboxes/{id}/files/searchFind files under
?path=. - GET /v1/sandboxes/{id}/files/statSize, mode, mtime and kind for one path.
Ports and terminal
All 8 in full- GET /v1/sandboxes/{id}/portsList this sandbox's preview URLs.
- POST /v1/sandboxes/{id}/portsMint a preview URL for a port in the sandbox.
- DELETE /v1/sandboxes/{id}/ports/{token_or_port}Revoke one URL by its token, or every URL for a port by the port number.
- GET /v1/sandboxes/{id}/ptyA WebSocket terminal, relayed byte for byte.
- GET /v1/sandboxes/{id}/sshThe keys this sandbox will accept over SSH, and for how long.
- POST /v1/sandboxes/{id}/sshAuthorise an
ssh-ed25519public key for this sandbox and mint the short-lived credential an SSH connection to it uses. - DELETE /v1/sandboxes/{id}/ssh/{key_id}Revoke one key by id or fingerprint, and the access credential issued with it.
- GET /v1/sandboxes/{id}/ssh/connectA WebSocket carrying the SSH byte stream, relayed byte for byte.
Secrets
All 3 in full- GET /v1/secretsThe calling key's project's secret names and shapes, and for a bound secret its
urlsandenvasPOST /v1/secretsanswers them. - POST /v1/secretsStore a secret in the calling key's project (the same name may hold a different value in another project).
- DELETE /v1/secrets/{name}Remove a secret.
Organisation
All 24 in full- GET /v1/invitesInvites waiting for your verified email address, from any organization.
- POST /v1/invites/{invite_id}/acceptAccept an invite and join the organization.
- POST /v1/invites/{invite_id}/declineDecline an invite sent to your verified email address.
- GET /v1/meWho you are, what your limits are, what you have spent, and what trial credit is left.
- GET /v1/me/ledgerThe rows behind
spend: usage recorded while each sandbox runs and when it ends. - GET /v1/me/outcomesWhat the organization got this month, measured from its own runs and sandboxes: runs and sandboxes started, the median time to ready, failures on our side that were not billed and what was given back, hours paused and what they would have cost running, failed sandboxes kept to reopen, and for runs of two or more sandboxes how many times faster than one after another and the time not spent waiting.
- GET /v1/orgsThe organizations you belong to, your role in each, and which one is your default.
- POST /v1/orgsCreate an organization.
- PATCH /v1/orgs/{org}Rename an organization.
- DELETE /v1/orgs/{org}Delete an organization.
- GET /v1/orgs/{org}/auditThe organization's audit log, newest first: keys minted and revoked, invites, role changes, removals, projects, billing events, suspension, and what support did to the organization (shown as
Promigence support). - POST /v1/orgs/{org}/defaultMake this organization the one a sign-in and the CLI use when none is named.
- GET /v1/orgs/{org}/invitesPending invites to an organization.
- POST /v1/orgs/{org}/invitesInvite an email address.
- DELETE /v1/orgs/{org}/invites/{invite_id}Revoke a pending invite.
- POST /v1/orgs/{org}/invites/{invite_id}/resendSend a pending invite's email again: the same invite, its 14 days counted again from now.
- GET /v1/orgs/{org}/membersThe members of an organization and their roles.
- PATCH /v1/orgs/{org}/members/{user_id}Change a member's role.
- DELETE /v1/orgs/{org}/members/{user_id}Remove a member (owners, signed in), or leave (your own user id, or
me). - GET /v1/orgs/{org}/projectsAn organization's projects, each with what it was billed this calendar month (UTC):
monthandmonth_spend_usd, the figure its monthly cap is checked against. - POST /v1/orgs/{org}/projectsCreate a project.
- PATCH /v1/orgs/{org}/projects/{project}Rename a project, set its monthly cap, or both.
- DELETE /v1/orgs/{org}/projects/{project}Delete a project with no working keys, no snapshots, no builds running, no failure captures and no sandbox that has not ended.
- POST /v1/orgs/{org}/projects/{project}/moveMove a project to another organization: its snapshots and secrets go with it, its past runs stay with the organization that paid for them, in its default project, and every key of the project is revoked (mint new ones there).
Billing
All 10 in full- GET /v1/billingPlan, card on file, this month's usage, and the upcoming invoice preview.
- POST /v1/billing/checkoutStart a card-setup checkout.
- GET /v1/billing/historyYour billing history, newest first: every credit you received, with what is left of it and when it expires, and every payment (invoices paid, payments that failed, refunds).
- POST /v1/billing/portalA link to the billing portal.
- POST /v1/credits/claimThe claim the page sends: the link's token, the accepted terms version and the page's browser check.
- POST /v1/credits/claim-linkA single-use link (15 minutes) to the page where the free credit is claimed (
promigence credits claimopens it). - GET /v1/credits/offerYour account's free credit: claimable (and how much, until when), claimed, being checked, needs a card, or none.
- GET /v1/pricingYour organization's price list, read live on every call from what billing charges: the per-second rates, every sandbox shape on sale and its price (a snapshot build is billed at its tier's), the limits, the free credit and the plan they come from (
scope: organization). - GET /v1/referralsYour referral code and its offer ("Give $X, get $Y"), your referrals by status, what they earned you, the referral you came through, and your credit that expires.
- POST /v1/referrals/applyApply a referral code to your organization: before its first paid invoice and within the offer's window after the organization was created.
Keys and sign-in
All 12 in full- GET /v1/auth/claimThe page where the free credit is claimed, in a browser: the offer, the terms to accept, and the Claim button.
- GET /v1/auth/cli-callbackThe page a browser lands on after sign-in; in headless mode it shows the code to paste back.
- POST /v1/auth/cli/requestsStart
promigence loginthrough the console: register the sign-in request (the PKCE S256 challenge, the state, the local port the CLI listens on or none to paste the code, the computer's name and the new key's name) and get the authorize page to open for it. - POST /v1/auth/cli/tokenFinish
promigence loginafter you authorized the CLI in the browser: exchange the one-time code the authorize page gave the CLI, with the PKCE verifier whose S256 challenge the CLI sent there, for an API key (api_key, in this answer only). - GET /v1/auth/configWhat
promigence loginneeds to start a sign-in: the identity domain, client id and scopes, and the console page where you authorize the CLI (console_authorize_url, when it is offered). - POST /v1/auth/inviteRedeem an invite code for one sign-in attempt, before the sign-in starts (
promigence signupdoes it for you). - POST /v1/auth/invite/releaseGive back the place a redeemed invite code holds for a sign-in attempt that ended without an account (it timed out or was cancelled), so the next attempt can redeem the code at once (
promigence signupdoes it for you). - POST /v1/auth/sessionExchange an identity token for a Promigence API key.
- GET /v1/keysList your org's keys: with an API key, its own project's; with an identity token, every project's.
- POST /v1/keysMint a key.
api_keyis in the 201 response and is never retrievable again. - DELETE /v1/keys/{key_id}Revoke a key, and every key it minted (and every key those minted):
also_revokedlists them. - POST /v1/orgs/{org}/keysMint a key in an organization you belong to, in one of its projects.