Skip to content

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.
  • curl and a shell. Every command in this guide also has Python, Go, and JavaScript variants in API code samples.
bash
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:

bash
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:

bash
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:

bash
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:

bash
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_TOKEN

Plaintext 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:

bash
# 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

bash
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.jar

A few notes on what's actually happening:

  • The -L is 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-Reason in the headers and a JSON body with block_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:

bash
# 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:

yaml
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.
  • orbital CLI — once you've outgrown shell pipelines, this is the same flow with one binary and no jq.
  • Webhook events — drive Slack / Teams / PagerDuty / your own systems off the same data the UI surfaces.

Released under the Apache-2.0 License.