# API

> What you can call programmatically today: the release gate check for CI with a workspace API key, and the public evidence pack keys. The API is limited in beta.

Source: https://aicodeops.dev/docs/api

AI CodeOps is used in the browser. During the open beta, the programmatic API is deliberately small: one endpoint for CI, plus the public keys that let anyone check an evidence pack. Everything else in the console needs a signed-in browser session and is not a supported API. There is no published OpenAPI specification yet.

## Workspace API keys

Create a key under **Configure → API Keys**. Give it a name; the secret (it starts with `sk-`) is shown once, so copy it then. AI CodeOps stores only a hash.

- A key acts as the member who created it, with that member's current role. If the member is removed or their role loses a permission, the key loses it too.
- Owners, admins, developers and members can create keys. Viewers can't.
- Admins see every key in the workspace; others see their own.
- **Delete** revokes a key at once. Integrations using it stop working immediately.

Where your plan includes key scoping, an admin can choose **Scope** to limit a key to an IP allowlist (CIDR ranges), a rate limit per minute, an expiry date, and what it may be used for: model gateway calls, the release gate check, or both (the default).

Send the key as a Bearer token:

```http
Authorization: Bearer sk-...
```

## Release gate check

`POST /api/release/check` answers one question for your CI: may this pull request or commit ship? It applies the same checks as the [quality gates](https://aicodeops.dev/docs/quality-gates) across every session that touched the change. The key needs the `workspace:read` permission.

```bash
curl -sf -X POST "https://console.aicodeops.dev/api/release/check" \
  -H "Authorization: Bearer $WORKSPACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"pullRequestURL":"https://github.com/acme/api/pull/42"}'
```

Use the same host you open the console on.

### Request fields

| Field                | Type                                           | Default                      |
| -------------------- | ---------------------------------------------- | ---------------------------- |
| `pullRequestURL`     | URL                                            | one of these two is required |
| `commitSHA`          | string, 7+ characters                          |                              |
| `blockingSeverity`   | `critical`, `high`, `medium`, `low`, `info`    | `high`                       |
| `blockOnTestFailure` | boolean                                        | `true`                       |
| `minCoveragePct`     | number 0–100                                   | not gated                    |
| `requiredWorkflows`  | string array                                   | none                         |
| `disallowedLicenses` | string array, checked against the project SBOM | none                         |
| `requireDeployedTo`  | environment name, e.g. `staging`               | none                         |
| `blockOnInFlight`    | boolean: deny while a run is still going       | `false`                      |
| `lookbackMs`         | how far back to look, in milliseconds          | 14 days                      |

`GET /api/release/check` accepts the same names as query parameters (lists comma-separated).

### Response

The endpoint returns `200` for both outcomes. Read `allowed`:

- `allowed`: `true` or `false`
- `summary` and `reasons`: plain-English explanations
- `blockingFindings`, `testFailures`, `coverageShortfall`, `licenseViolations`, `licenseEvidenceMissing`: the details behind a deny (`licenseEvidenceMissing` lists projects with no SBOM to check when you pass `disallowedLicenses`)
- `runsConsidered`: the sessions the verdict was based on

A change with no matching sessions is denied ("No workflow runs found for …"). If the evaluation fails, you get `allowed: false` with the summary "Evaluation failed", so a broken check never passes a change.

### Errors

| Status | Meaning                                                                                           |
| ------ | ------------------------------------------------------------------------------------------------- |
| 400    | Invalid JSON or invalid fields                                                                    |
| 401    | Missing or invalid API key                                                                        |
| 403    | The key's scope or its creator's role doesn't allow this, or the key belongs to another workspace |
| 429    | More than 60 checks a minute for the workspace; see `Retry-After`                                 |

The same check is in the console under **Review & Gate → Release gate**.

## Evidence pack keys

`GET /api/evidence?action=keys` is public and needs no key. It returns the Ed25519 public keys that sign [evidence packs](https://aicodeops.dev/docs/evidence-packs), so you can verify a pack offline. Downloading a pack needs a signed-in member with audit access; it is not available with an API key.

## API reference

One page per endpoint, generated from the [OpenAPI spec](https://aicodeops.dev/docs/api/openapi.json):

- [Check whether a pull request or commit may ship](https://aicodeops.dev/docs/api/release-check)
- [The keys that sign evidence packs](https://aicodeops.dev/docs/api/evidence-keys)
- [Verify an evidence pack](https://aicodeops.dev/docs/api/evidence-verify)
- [Liveness and readiness](https://aicodeops.dev/docs/api/health)
- [Component status](https://aicodeops.dev/docs/api/status)

## Next steps

- [Quality gates](https://aicodeops.dev/docs/quality-gates)
- [Members and roles](https://aicodeops.dev/docs/members)
- [Troubleshooting](https://aicodeops.dev/docs/troubleshooting)

## Frequently asked questions

### Is there a full REST API for sessions and projects?

Not yet. During the open beta the console is the way to run sessions and manage projects. The endpoints on this page are the ones that accept programmatic access.

### Do API keys expire?

Not by default. A key works until it is deleted, its creator leaves the workspace, or an expiry date set in its scope passes.
