Skip to content

Start here

OverviewConnect your agentClient onboarding

Guides

WorkspacesScan manualFix ReviewScheduled scans

Reference

CLI guideService API

For agents

docs/agents.mdllms.txt
Apex/Docs
⌘K
Connect your agent

Start here

OverviewConnect your agentClient onboarding

Guides

WorkspacesScan manualFix ReviewScheduled scans

Reference

CLI guideService API

For agents

docs/agents.mdllms.txt
Docs/Reference/Service API

Apex service API

Run Apex from production MCP clients, serverless functions, cron jobs, and internal audit hubs with an organization-scoped service key.

14 min readUpdated August 24, 2026Platform engineers

01Choose the right integration

Use the local CLI or stdio MCP when a person or coding agent needs direct access to a repository checkout. Use hosted MCP when an agent needs a production HTTPS endpoint with a static organization credential. Use the REST API when an application needs explicit HTTP and JSON control, including local archive uploads.

CLI + local MCPRepository-local developer and coding-agent workflows with device login
Hosted MCPProduction agents over Streamable HTTP at https://ai.cantina.xyz/mcp
Service APIServerless workers, cron jobs, CI, and internal audit hubs
AuthenticationAuthorization: Bearer apex_sk_live_… or X-API-Key
Findings formatGitLab SAST for scan exports; normalized JSON for workspace exports

The machine-readable contract is available at /api/apex/v1/openapi.json.

02Prerequisites

  • For remote scans, connect the GitHub repository to the same Apex organization that owns the service key. The first API scan can create and link its workspace automatically.
  • For remote scans, use a full 40- or 64-character Git commit SHA. Branch names and moving refs are not accepted.
  • For local or non-Git directories, package the source as tar.gz. The API creates the workspace and signed archive upload; no Git remote or website scan is required.
  • Generate or revoke keys as an Apex organization manager or administrator.
  • Store the key in a secret manager. Apex displays the raw secret only once and stores only its hash.

Version-one repository support

The first service API release accepts GitHub repositories already connected to your Apex organization. GitLab service-account access will follow once connection ownership can be made explicit.

03Generate a service key

Where Settings is available

The Settings flow is available on hosted, non-demo company tenants to active, verified company managers and Apex administrators. Active, verified personal-tenant owners use the scripted CLI instead. Demo tenants and on-prem deployments do not support hosted service API key management.

  1. 1
    Open company Settings
    From the company dashboard, open Settings and find Service API keys.
  2. 2
    Generate the key
    Select Generate key, give the key a descriptive name, choose the scopes it needs, and set an expiration.
  3. 3
    Save the one-time secret
    Copy the secret into your secret manager before closing the dialog. Apex cannot display it again.

Prefer the smallest set of scopes the integration needs. Use only scans:read and findings:read when a key polls existing scans and imports findings; add scans:create when it must start or cancel scans.

CLI alternative

Managers and administrators can also generate, list, and revoke service keys from their own terminal with Apex CLI.

terminal
$ apex update
$ apex login
# Replace this placeholder with a future ISO-8601 timestamp from your rotation policy:
$ FUTURE_ISO_8601_TIMESTAMP="YYYY-MM-DDTHH:MM:SSZ"
# Read-only key with an explicit expiration:
$ apex service-key create --name "Audit hub" --scope scans:read,findings:read --expires-at "$FUTURE_ISO_8601_TIMESTAMP"
# Omit --scope only when this key must also trigger scans:
$ apex service-key create --name "Audit hub" --expires-at "$FUTURE_ISO_8601_TIMESTAMP"
# Optional machine-readable output:
$ apex service-key create --name "Audit hub" --scope scans:read,findings:read --expires-at "$FUTURE_ISO_8601_TIMESTAMP" --json

Apex CLI 0.1.16 and later automatically uses your company when only one is available. If you belong to multiple companies, it asks you to choose. Use --company <id-or-handle> to skip that prompt, or when a non-interactive command cannot prompt. As in Settings, copy the secret into your secret manager when it is created. It cannot be retrieved later.

Settings defaults new keys to a 90-day expiration. The CLI leaves a key non-expiring when --expires-at is omitted, so prefer an explicit future ISO-8601 timestamp unless your credential-rotation policy intentionally requires no expiration.

When --scope is omitted, the key gets all three scopes: scans:create, scans:read, and findings:read. List metadata or revoke a key without exposing its secret:

terminal
$ apex service-key list
$ apex service-key revoke <key-id>

04Connect hosted MCP

Point any Streamable HTTP MCP client at https://ai.cantina.xyz/mcp and send the organization service key as Authorization: Bearer <TOKEN>. No local apex-mcp process, device login, browser, or TTY is required.

Keep the service key out of config files

Store the secret in APEX_SERVICE_KEYand use your MCP client's environment-variable expansion. Do not paste the raw key into committed configuration, command history, logs, or prompts.

Codex

terminal
$ export APEX_SERVICE_KEY='<service-key>'
$ codex mcp add apex --url https://ai.cantina.xyz/mcp --bearer-token-env-var APEX_SERVICE_KEY
$ codex mcp get apex

Claude Code

Add a user- or project-scoped .mcp.json entry. Claude Code expands ${APEX_SERVICE_KEY} from the process environment when it connects.

.mcp.json
{
  "mcpServers": {
    "apex": {
      "type": "http",
      "url": "https://ai.cantina.xyz/mcp",
      "headers": {
        "Authorization": "Bearer ${APEX_SERVICE_KEY}"
      }
    }
  }
}

Other clients use the same URL and Bearer header. A successful connection advertises apex-credits, apex-workspaces, apex-scan, apex-status, apex-cancel-scan, apex-findings, and apex-workspace-findings. Tool calls enforce the same scopes, organization isolation, rate limits, usage attribution, and idempotency rules as the REST API. When a REST operation supplies a Retry-After header, the MCP tool error preserves it as retryAfter and a normalized retryAfterSeconds value in structured content.

Hosted and local MCP have different jobs

Hosted MCP operates on repositories already connected to the service key's Apex organization and scans an exact full commit SHA. Use local apex-mcpwhen a tool needs the caller's filesystem, local archive preparation, device login, provider connection, or setup workflows.

05Trigger a scan

Send a unique, stable Idempotency-Key for each intended scan. Reuse that same value when retrying the same request.

Threat model first, scan second

By default, the API accepts the request with HTTP 202, generates a fresh unattended threat model in the background, and starts the scan automatically when generation completes. The initial status is preparing; poll the returned statusUrl through preparation and scanning. Scan credits are not reserved until threat-model generation succeeds. When you provide workspaceId, useExistingThreatModel defaults to true. Apex validates that the completed model exists before reserving credits. Requests without workspaceId still default to fresh generation for first-scan workspace creation. Set generateThreatModel to false only when you intentionally want to start without generation or validated reuse.

Apex reuses the active workspace already linked to the repository. If none exists, the same request creates a workspace named after the canonical GitHub repository and links it before starting the scan—no website scan is required. If more than one workspace matches, include its workspaceId in the body. The conflict response includes each matching workspace ID, prefix, and name so the caller can present a choice instead of failing with an undiscoverable ID.

Explicit workspaces stay explicit

When workspaceId is present, Apex will not rewrite that workspace to point at another repository. A mismatch returns a conflict so integrations cannot silently change an existing workspace.

Reuse an existing threat model

For a subsequent scan, provide the existing workspace's UUID;useExistingThreatModel is true by default. Set generateThreatModel to true when you want a fresh threat model instead; an explicit fresh generation request overrides the implicit reuse default. Apex fails closed with THREAT_MODEL_NOT_AVAILABLE if the workspace has no completed model; after choosing a different workspace or switching back to generation, submit the corrected request with a new Idempotency-Key.

POST /api/apex/v1/scans
curl --request POST https://ai.cantina.xyz/api/apex/v1/scans \
  --header "Authorization: Bearer $APEX_SERVICE_KEY" \
  --header "Idempotency-Key: 8c8d9e5d-4fa6-4b65-bd42-14ddb5874c83" \
  --header "Content-Type: application/json" \
  --data '{
    "repoUrl": "https://github.com/acme/payments",
    "commit": "0123456789abcdef0123456789abcdef01234567",
    "paths": ["src", "packages/auth"],
    "scanType": "standard",
    "generateThreatModel": true
  }'
standard (default)Consumes the configured standard scan-credit cost (200 credits by default).
auditConsumes 2,000 standard credits.
liteConsumes Lite credits when the Lite-credit ledger is enabled; otherwise consumes the configured standard-credit cost.
local archivesCurrently support standard scans and use the same standard credit reservation path.

Paths are focus hints

The optional paths array prioritizes those files in Apex prompts, but it is not a hard repository boundary. The scan may inspect or report code outside those paths. Omit it if you do not need focus hints; version one does not yet provide strict path isolation.

Scan a local or non-Git directory

Raw filesystem paths cannot cross an HTTP boundary. Create a tar.gz archive, calculate its SHA-256 plus compressed and uncompressed byte counts, then use the upload → complete → scan flow below. A directory does not need to be a Git repository; omit the optional git object for non-Git source.

upload → complete → scan
# 1. Package the source without embedding the output archive:
tar --exclude="./apex-source.tar.gz" -czf apex-source.tar.gz .
# Calculate SHA256, COMPRESSED_BYTES, and UNCOMPRESSED_BYTES in your uploader.

# 2. Create the workspace and signed upload session:
curl --request POST https://ai.cantina.xyz/api/apex/v1/source-uploads \
  --header "Authorization: Bearer $APEX_SERVICE_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "workspaceName": "Payments local",
    "uploads": [{
      "displayName": "payments",
      "relativePath": ".",
      "archiveFormat": "tar.gz",
      "sha256": "<sha256>",
      "compressedBytes": <compressed-bytes>,
      "uncompressedBytes": <uncompressed-bytes>
    }]
  }'

# 3. PUT the archive to uploads[0].upload.putUrl using its returned headers:
curl --request PUT --header "Content-Type: application/gzip" \
  --upload-file apex-source.tar.gz "<put-url>"

# 4. Verify the upload; this returns archiveId and sha256:
curl --request POST \
  --header "Authorization: Bearer $APEX_SERVICE_KEY" \
  --header "Content-Type: application/json" \
  --data '{"workspaceId":"<workspace-id>"}' \
  https://ai.cantina.xyz/api/apex/v1/source-uploads/<upload-id>/complete

# 5. Start the first scan with a fresh threat model:
curl --request POST https://ai.cantina.xyz/api/apex/v1/scans \
  --header "Authorization: Bearer $APEX_SERVICE_KEY" \
  --header "Idempotency-Key: local-payments-2026-07-22" \
  --header "Content-Type: application/json" \
  --data '{
    "workspaceId": "<workspace-id>",
    "scanType": "standard",
    "generateThreatModel": true,
    "scanSources": [{
      "sourceKind": "local_archive",
      "displayName": "payments",
      "relativePath": ".",
      "archiveId": "<archive-id>",
      "sha256": "<sha256>"
    }]
  }'

Generate a model for a new local workspace

A newly created local workspace has no completed threat model to reuse. Keep generateThreatModel: true on its first scan. Omitting it makes a request with workspaceIddefault to existing-model reuse and can return THREAT_MODEL_NOT_AVAILABLE.

Archive limits

Each archive must be at most 500 MiB compressed and 2 GiB uncompressed, and one scan may include at most 20 archives. Signed upload URLs are credentials: do not log or persist them beyond the upload.

A successful request returns HTTP 202:

202 Accepted
{
  "scanId": "3e559b86-f524-4b27-a175-fb4944c53f1b",
  "workspaceId": "2abf03ae-768b-4717-9d14-43e1a8d8d9a1",
  "status": "preparing",
  "statusUrl": "/api/apex/v1/scans/3e559b86-f524-4b27-a175-fb4944c53f1b",
  "findingsUrl": "/api/apex/v1/scans/3e559b86-f524-4b27-a175-fb4944c53f1b/findings"
}

06Poll scan status

GET /api/apex/v1/scans/{scanId}
$ curl --header "Authorization: Bearer $APEX_SERVICE_KEY" \
  https://ai.cantina.xyz/api/apex/v1/scans/<scan-id>
  1. 1
    Start
    Treat the HTTP 202 response as accepted and retain its scanId and statusUrl.
  2. 2
    Poll
    Use bounded exponential backoff while status is preparing, queued, or running.
  3. 3
    Stop
    Stop polling on completed, failed, or cancelled.
  4. 4
    Collect
    Fetch findings after the scan reaches completed.

07Cancel a scan

POST /api/apex/v1/scans/{scanId}/cancel
$ curl --request POST \
  --header "Authorization: Bearer $APEX_SERVICE_KEY" \
  https://ai.cantina.xyz/api/apex/v1/scans/<scan-id>/cancel

Cancellation requires scans:create and stops the remaining Apex work for a queued or running scan. A successful response returns HTTP 200 with status: cancelled. Repeating the request after cancellation is safe and returns the same terminal status.

Terminal scans cannot be cancelled

Apex returns 409 when a scan is already completed or failed, or when it has not started in Apex yet. Poll the status URL to confirm the current terminal state.

08Fetch findings

GET /api/apex/v1/scans/{scanId}/findings
$ curl --header "Authorization: Bearer $APEX_SERVICE_KEY" \
  https://ai.cantina.xyz/api/apex/v1/scans/<scan-id>/findings

The response is a GitLab SAST report. Each vulnerability includes the persisted Apex finding ID, title, description, severity, and available file and line location. IDs remain stable when you fetch the same scan again, so downstream systems can deduplicate safely.

Findings retrieval also works for scans started in the Apex web app, so an integration can begin with this endpoint before automating scan creation.

Discover workspace IDs

A key with scans:create can list active workspaces before creating a scan. Provide a repository URL or GitHub owner/name to find every linked workspace without exposing unrelated workspace metadata. If more than one is returned, let the user choose one and send its workspaceId in a new scan request with a new Idempotency-Key.

GET /api/apex/v1/workspaces
curl --header "Authorization: Bearer $APEX_SERVICE_KEY" \
  'https://ai.cantina.xyz/api/apex/v1/workspaces?repository=Uniswap%2Fliquidity-launcher'

Fetch a whole workspace by prefix

To fetch the same finding set as the workspace findings browser, use its human-readable prefix such as BEDR9. The export uses non-archived scans and excludes PR scans, fix-review scans, and unfinished advanced-scan setup drafts. The service key still limits the lookup to its own organization, so a prefix from a different organization returns 404. Use the workspace UUID instead if a company has an ambiguous prefix.

GET /api/apex/v1/workspaces/{workspaceRef}/findings
curl --header "Authorization: Bearer $APEX_SERVICE_KEY" \
  'https://ai.cantina.xyz/api/apex/v1/workspaces/BEDR9/findings?state=open'

The response includes the resolved workspace, its active scans, and full finding records rather than only finding IDs. Set state=opento exclude invalid findings, duplicates, and findings marked fixed, false positive, won't fix, or skipped. The response includes each finding's reviewState, isDuplicate, and duplicateOfId. Repeat filter to combine facets. Supported forms include severity:high, status:valid, review:accepted, duplicate:unique, repo:example-app, source:lib/auth.ts, impact:high, likelihood:medium, visibility:draft, scan:<scan-id>, and !validation:invalid. Finding statuses are proposed, partially_valid, valid, invalid, or duplicate; open is a finding state, not a status value. Review state and validity include the current shared client feedback used by the workspace UI. Validation filters use the same client/viewer feedback precedence as the workspace UI, and each finding exposes the resulting validity as valid, invalid, or unknown. Invalid or unknown filter strings are ignored; the response's filters array shows the filters that were applied.

Findings access includes drafts

A key with findings:read can retrieve unpublished draft findings for scans in its organization so an external hub can perform triage. Treat the service key as a privileged credential and do not expose its findings payload to users who cannot review drafts in Apex.

The first request after a scan completes durably synchronizes the terminal finding set. If that sync is unavailable or has more pages, Apex returns 503 with Retry-After. Retry the same GET; after readiness is persisted, later requests serve the cached report without depending on a live scan process.

09Errors and retries

400Invalid repository URL, commit SHA, path hint, or request body
401Missing, malformed, expired, or revoked service key
402The selected scan credit or entitlement is exhausted
403The key lacks the required scope
404The scan does not exist in the key's organization
409Idempotency conflict, repository workspace mismatch, active scan, or a scan that cannot be cancelled
422Unsupported repository or scan-source input
429Rate limit reached; respect Retry-After
500Internal persistence or lookup failure; retry with backoff
502Transient provider or Apex scan failure; retry with the identical request when appropriate
503Workspace provisioning is in progress, an Apex operation timed out, scan finalization is incomplete, or the first terminal findings sync is incomplete; respect Retry-After

Retry safely

Threat-model generation and scan startup continue server-side after the creation request returns 202. Preparation failures appear as status: failed with an error code at the returned status URL; callers do not need to replay the creation POST merely because generation takes longer than one HTTP request.

GET requests and scan cancellation requests are safe to retry. After a transport error, timeout, or 5xx, retry a scan creation POST only with the original body and the same Idempotency-Key. Idempotency is organization-scoped, so replay remains safe if the service key is rotated between attempts. A definitive 4xx response is retained: after correcting its precondition, submit it as a new intended scan with a new Idempotency-Key. Never log the Authorization or X-API-Key header.

10Claude and Codex prompts

These prompts work in Claude Code or Codex. The first two keep key creation in a user-controlled Settings or terminal flow; the others use either the hosted MCP endpoint or the REST API for a headless runtime instead of launching a local stdio process there.

›Walk me through generating an Apex service key from my company Settings page. Help me choose least-privilege scopes, but do not ask me to paste the secret into this chat.
›Give me the exact Apex CLI command I should run in my own terminal to create a service key named Audit hub. Do not run it for me or ask me to paste the secret into this chat.
›Connect this MCP client to the hosted Apex endpoint at https://ai.cantina.xyz/mcp using APEX_SERVICE_KEY as a Bearer token. Keep the token in the environment, verify tools/list, and call apex-credits without printing the credential.
›Using APEX_SERVICE_KEY from the environment, write a serverless TypeScript adapter that starts an Apex scan for this repository and commit. Send a stable Idempotency-Key, poll until terminal, and return the GitLab SAST findings.
›Using APEX_SERVICE_KEY from the environment, package this local directory as tar.gz, create and complete an Apex source upload, then start a standard scan from its local_archive scanSources entry with generateThreatModel: true. A new workspace has no model to reuse. Keep the key and signed upload URL secret.
›Add Apex to our audit hub. Call POST /api/apex/v1/scans, poll GET /api/apex/v1/scans/{scanId} with bounded backoff, then fetch /findings. Never log the bearer token and retry only safe requests.
›Using APEX_SERVICE_KEY from the environment, cancel this active Apex scan with POST /api/apex/v1/scans/{scanId}/cancel. Confirm the returned status is cancelled and never log the credential.
›Given an Apex scanId for a scan started in the web app, fetch its GitLab SAST findings with APEX_SERVICE_KEY, preserve the raw response, and import each vulnerability once by stable ID. Do not trigger another scan or log the credential.
›Using APEX_SERVICE_KEY from the environment, fetch every finding shown in the workspace findings browser for BEDR9 and exclude findings whose client/viewer feedback validity is invalid. Keep the response as JSON and never print the credential.
›Diagnose this Apex API response without printing secrets. Explain whether it is a transport/5xx retry that must preserve the original Idempotency-Key, or a definitive 4xx that must be corrected and submitted as a new intended scan with a new key.
Was this useful?
Last updated August 24, 2026 · v2026.04
← PreviousApex CLI guide

On this page

  1. Choose the right integration
  2. Prerequisites
  3. Generate a service key
  4. Connect hosted MCP
  5. Trigger a scan
  6. Poll scan status
  7. Cancel a scan
  8. Fetch findings
  9. Errors and retries
  10. Claude and Codex prompts

Related

OpenAPI documentApex CLI guideClient onboarding