Getting started with the API
This walks one full flow against the OrbitalReg REST API: sign in, mint a service-account token, push an artifact, pull it back, and end on a one-liner that drops the same flow into a .gitlab-ci.yml. By the end you'll have made every API call a real CI pipeline makes, and you'll know which page in this site to come back to for the rest.
Need to boot OrbitalReg first?
This page assumes a running deployment. If you don't have one, the local Quickstart brings up a full stack via Docker Compose in about five minutes — then come back here and point ORBITAL_HOST at http://localhost:8080.
What you need
- An OrbitalReg deployment URL (we'll call it
$ORBITAL_HOST). - An admin or org-admin account on that deployment. The first SAML / OIDC sign-in to a fresh deployment becomes the org admin.
curland a shell. Every command in this guide also has Python, Go, and JavaScript variants in API code samples.
export ORBITAL_HOST="https://orbitalreg.example.com"Step 1 — Sign in to the web UI
Open $ORBITAL_HOST and complete the SAML / OIDC sign-in. The first sign-in to a fresh deployment promotes that account to org admin and creates a default project called default. The browser cookie set during sign-in carries the same authority as a token and is what the SPA uses for every subsequent request.
You can confirm the deployment knows who you are with the unauthenticated public-config probe — handy for monitoring scripts that just want to confirm the API is up:
curl "$ORBITAL_HOST/api/public-config" | jq .The response carries the deployment's version, the help URL operators configured under Branding, and the active customer banner (if any).
Step 2 — Create a project and a repository
The web UI does this with two clicks under Projects → New project and Repository → New repo, but the REST shape is what your Terraform / CLI / pipelines will use. Create a project from your browser, then ask the API for its UUID:
PROJECT_ID=$(curl -fsS "$ORBITAL_HOST/api/v1/projects?slug=demo" \
--cookie-jar /tmp/cookies.txt --cookie /tmp/cookies.txt \
| jq -r '.[0].id')(For headless flows, swap the cookie jar for -H "Authorization: Bearer $ORBITAL_TOKEN" once you've minted one in step 3.)
Then create a Maven repo inside it:
curl -fsS -X POST "$ORBITAL_HOST/api/v1/repos" \
--cookie-jar /tmp/cookies.txt --cookie /tmp/cookies.txt \
-H "Content-Type: application/json" \
-d "{
\"projectId\": \"$PROJECT_ID\",
\"slug\": \"libs.staging\",
\"name\": \"libs.staging\",
\"kind\": \"local\",
\"format\": \"maven\"
}" | jq .
REPO_ID=$(curl -fsS "$ORBITAL_HOST/api/v1/repos?project_id=$PROJECT_ID" \
--cookie-jar /tmp/cookies.txt --cookie /tmp/cookies.txt \
| jq -r '.[] | select(.slug=="libs.staging") | .id')You now have a project ($PROJECT_ID) and a repository ($REPO_ID). A repo's (format, kind) decides the wire protocol clients can use against it — maven local accepts Maven-layout PUTs, npm remote proxies the public registry, and so on. The full matrix lives under Package formats.
Step 3 — Mint a service-account token
In a real pipeline you don't ship session cookies; you ship a service-account token. Create the SA from the web UI under Project → Service accounts → New (or call /api/v1/projects/{id}/service_accounts directly), then mint a publish-scope token against it:
SA_ID=$(curl -fsS -X POST \
"$ORBITAL_HOST/api/v1/projects/$PROJECT_ID/service_accounts" \
--cookie-jar /tmp/cookies.txt --cookie /tmp/cookies.txt \
-H "Content-Type: application/json" \
-d '{"name":"ci-publish","description":"CI publisher"}' | jq -r .id)
ORBITAL_TOKEN=$(curl -fsS -X POST \
"$ORBITAL_HOST/api/v1/projects/$PROJECT_ID/service_accounts/$SA_ID/tokens" \
--cookie-jar /tmp/cookies.txt --cookie /tmp/cookies.txt \
-H "Content-Type: application/json" \
-d '{"name":"first-token","bundle":"publish","expires_in_days":90}' \
| jq -r .plaintext)
export ORBITAL_TOKENPlaintext is returned exactly once
The plaintext field is only present in the issue response. If the shell session loses it, revoke the token from the UI and mint a new one — the API has no path to retrieve it after the fact. This matches the standard "the password is the user's, never the server's" invariant.
The bundle picker (Pull / Publish / Admin) is the recommended way to scope a token. Optional format_filter and repo_filter arrays narrow it further. See API tokens for the full bundle → granular scope mapping.
Step 4 — Push an artifact
The generic upload endpoint accepts any bytes, addressed by (repo_id, path). For a Maven artifact, the path is the standard Maven layout — groupId/artifactId/version/file:
# A 1-byte placeholder for the demo. In a real pipeline this is
# whatever your build produced.
echo -n 'x' > /tmp/sample-1.0.0.jar
curl -fsS -X PUT \
"$ORBITAL_HOST/api/v1/artifacts/$REPO_ID/com/example/sample/1.0.0/sample-1.0.0.jar" \
-H "Authorization: Bearer $ORBITAL_TOKEN" \
-H "Content-Type: application/java-archive" \
--data-binary @/tmp/sample-1.0.0.jar | jq .The response carries the new artifact's id, sha256, and created_at. Within a few seconds it appears in the web UI under Projects → demo → libs.staging, with a Detection banner attached once the bundled scanners have run against it.
If you're pushing through a build tool (mvn deploy, npm publish, twine upload, …), point the tool at $ORBITAL_HOST/<format>/<project-slug>/<repo-slug> instead — the per-format adapters speak the upstream wire protocol verbatim, so no config beyond the URL and the token is needed.
Step 5 — Pull it back
curl -fsSL -o /tmp/sample-pulled.jar \
"$ORBITAL_HOST/api/v1/artifacts/$REPO_ID/com/example/sample/1.0.0/sample-1.0.0.jar" \
-H "Authorization: Bearer $ORBITAL_TOKEN"
shasum -a 256 /tmp/sample-pulled.jarA few notes on what's actually happening:
- The
-Lis non-optional — if the deployment's S3 endpoint is browser-reachable, the API answers a 302 to a presigned URL. Without follow-redirects you'd just print the redirect target. - A 403 on this path means a security block fired. The response carries
X-Orbital-Block-Reasonin the headers and a JSON body withblock_id+ a public landing page URL. See why-blocked envelope. - A 451 (rare) means the deployment is configured to refuse the request for legal reasons (export control, takedown).
Step 6 — Search and inspect
Now that there's a real artifact in the repo, the search endpoint returns it and you can ask the detection surface what it found:
# Cross-repo search.
curl -fsS "$ORBITAL_HOST/api/v1/search?q=sample&limit=10" \
-H "Authorization: Bearer $ORBITAL_TOKEN" | jq .
# Open scan findings for the artifact.
ARTIFACT_ID=$(curl -fsS "$ORBITAL_HOST/api/v1/search?q=sample-1.0.0" \
-H "Authorization: Bearer $ORBITAL_TOKEN" \
| jq -r '.[] | select(.kind=="artifact") | .id' | head -n1)
curl -fsS \
"$ORBITAL_HOST/api/v1/detection/findings?artifact_id=$ARTIFACT_ID&status=open" \
-H "Authorization: Bearer $ORBITAL_TOKEN" | jq .That's the full pipeline contract: list, mint, push, pull, search, findings. Everything else in the API surface is a refinement of one of these — promotions are scoped pushes between repos, build-info is metadata about a push, retention policies prune them on a schedule.
And here's the same flow as a .gitlab-ci.yml snippet
If you've shipped to here, you're ready to wire the same calls into your CI. The repo ships a reusable GitLab CI components catalog that hides all of the curl below behind one include: block, but the bare-shell version stays useful for one-off pipelines:
publish-jar:
stage: deploy
image: curlimages/curl:8.4.0
variables:
ORBITAL_HOST: "https://orbitalreg.example.com"
id_tokens:
ORBITAL_OIDC_TOKEN:
aud: "$ORBITAL_HOST"
script:
# Step 1 — exchange the GitLab OIDC token for a short-lived
# OrbitalReg bearer. No long-lived PAT in CI variables.
- |
ORBITAL_TOKEN=$(curl -fsS -X POST \
"$ORBITAL_HOST/api/oidc/exchange/gitlab" \
-H "Content-Type: application/json" \
-d "{\"oidc_token\":\"$ORBITAL_OIDC_TOKEN\"}" \
| jq -r .access_token)
echo "ORBITAL_TOKEN=$ORBITAL_TOKEN" >> orbital.env
# Step 2 — push the build artifact.
- |
curl -fsS -X PUT \
"$ORBITAL_HOST/api/v1/artifacts/$REPO_ID/$ARTIFACT_PATH" \
-H "Authorization: Bearer $ORBITAL_TOKEN" \
-H "Content-Type: application/java-archive" \
--data-binary "@target/$ARTIFACT_FILE"The id_tokens block is the GitLab-side half of the OIDC token exchange — the runner fetches a JWT, hands it to OrbitalReg, and gets back a 5-minute bearer scoped to whatever the OIDC trust policy allows. Zero PATs in CI variables, zero rotation pain.
Where to go from here
- API code samples — the same operations in Python, Go and JavaScript, plus a few multi-call recipes (promote-on-clean-scan, block-on-CVE).
- GitLab CI components — the catalog that packages every snippet above into reusable pipeline templates, signed with cosign keyless.
- API reference — the conceptual tour: auth modes, pagination, error envelope, webhook events.
- API explorer — the full machine-rendered spec; deep-link to a single operation when filing a support ticket.
orbitalCLI — once you've outgrown shell pipelines, this is the same flow with one binary and nojq.- Webhook events — drive Slack / Teams / PagerDuty / your own systems off the same data the UI surfaces.