---
title: "HTTP API reference"
source: "https://ottom.io/developers/api"
summary: "The Ottom HTTP API starts a scan of your store from a release pipeline and returns the result as JSON: the score, each journey stage, the findings your plan includes, the regression verdict and the release gate result. It has two endpoints, authenticated with a store scoped CI token, and is described by an OpenAPI document generated from the same schemas the API validates with."
---

# HTTP API reference

The Ottom HTTP API starts a scan of your store from a release pipeline and returns the result as JSON: the score, each journey stage, the findings your plan includes, the regression verdict and the release gate result. It has two endpoints, authenticated with a store scoped CI token, and is described by an OpenAPI document generated from the same schemas the API validates with.

OpenAPI document: https://ottom.io/developers/openapi.json

## Authentication

Send the store's CI token as `Authorization: Bearer <token>`. Base URL: https://ottom.io

## Versioning

The API is at version 2. The version moves only when a field is removed or changes meaning, or when a pipeline is expected to branch on something new. New fields are added without a version change, so parse responses in a way that ignores fields you do not know.

## Start a scan

`POST /api/ci/runs`

Starts a scan of the token’s store, or of its staging origin. Always a new scan: a pipeline asks because something changed. Counted against the store’s daily scan limit.

### Request body

| Field | Type | Description |
| --- | --- | --- |
| `url` | `string` | The store to scan. Must be the token’s store or that store’s staging origin. |
| `mode` (optional) | `"regression" \| "absolute"` | `regression` fails on what got worse since the last scan. `absolute` also fails on standing problems. |
| `failOn` (optional) | `"critical" \| "high" \| "medium" \| "low" \| "info"` | The lowest finding severity that counts as a failure. |
| `ref` (optional) | `string` | A label for the run, such as a commit SHA or branch. Recorded, never interpreted. |

### Response, 202

| Field | Type | Description |
| --- | --- | --- |
| `ok` | `true` | Always true on success. |
| `apiVersion` | `integer` | The API version that answered. See the versioning note. |
| `runId` | `string` | Poll this id for the result. |
| `storeOrigin` | `string` | The origin being scanned, after normalising the URL you sent. |
| `reused` | `boolean` | True when a run already in flight was joined instead of starting another. |

## Read a scan

`GET /api/ci/runs/{runId}`

The scan’s status and, once it has finished, its scores, findings, verdict and release gate result. Poll until `status` is final.

### Response, 200

| Field | Type | Description |
| --- | --- | --- |
| `ok` | `true` | Always true when the scan is known. |
| `apiVersion` | `integer` | The API version that answered. |
| `runId` | `string` | The scan’s id. |
| `storeOrigin` | `string` | The origin that was scanned. |
| `originAccess` | `"public" \| "storefront_password" \| "connector" \| "merchant_password"` | How the agent reached the store. Anything but `public` means the run did not cross your public CDN or bot protection. |
| `status` | `"queued" \| "running" \| "completed" \| "failed" \| "cancelled"` | `queued` and `running` mean poll again. `completed`, `failed` and `cancelled` are final. |
| `finishedAt` | `string \| null` | When the scan finished, once it has. |
| `error` | `string \| null` | Why the scan failed, when it did. |
| `score` | `number \| null` | The scan’s own score from 0 to 100, over the stages it measured. |
| `headline` (optional) | `object` | The headline score shown in the report, which may carry a stage from a scan in the last 7 days. Beside `score`, never instead of it. |
| `stages` | `object[]` | Each journey stage with its verdict and score. |
| `measuredStages` | `"discover" \| "evaluate" \| "compare" \| "buy"[]` | The stages this scan measured itself. |
| `partial` | `boolean` | True when the scan could not measure every stage. |
| `countsBySeverity` | `object` | How many findings of each severity. A missing severity means none. |
| `journey` | `object \| null` | The browser journey: how far the agent got and its verdict. |
| `findings` | `object[]` | What the agent hit. `locked` findings carry no detail unless the account’s plan includes it. |
| `unlocked` | `object` | Which report sections the token’s account can see. |
| `telemetry` | `object` | What the scan cost to run: tokens, browser time and an estimate in US dollars. |
| `baseline` | `object \| null` | The earlier scan this one was compared with, if there was one. |
| `verdict` | `object` | The regression verdict: whether this scan regressed, and the checks behind it. |
| `gate` (optional) | `object` | The release gate result. Fail the build on `fail`, poll again on `pending`. |
| `reportUrl` | `string` | A link to the full report, for a ticket. |

## Error codes

| Code | HTTP status | What it means |
| --- | --- | --- |
| `invalid_request` | 400 | The body or query did not validate. Not worth retrying unchanged. |
| `unauthorized` | 401 | The token is missing, wrong or revoked. Mint a new one. |
| `forbidden` | 403 | The store has asked not to be scanned, or its scans are on hold. A retry will not help. |
| `store_mismatch` | 403 | The token belongs to a different store than the URL you sent. |
| `not_found` | 404 | No scan with that id that this token can see. |
| `rate_limited` | 429 | The store has had its scans for today. Try again tomorrow. |
| `unavailable` | 500 or 503 | Ottom could not answer just now. Retry later. |

## Questions

### Where do I get a token?

An owner or admin mints a CI token for the store on its workspace page. It is shown once. A token reaches one store and that store’s staging origin, nothing else.

### How often should I poll a scan?

Every 15 to 30 seconds is plenty. A scan waits in a queue before it runs and usually takes a few minutes. Stop when status is completed, failed or cancelled.

### Does the API fetch my store?

No. The API queues a scan, and the scan runs on Ottom. Every request to your store comes from Ottom’s published addresses with its published user agent, wherever you called the API from.

### Can I generate a client from the OpenAPI document?

Yes. It is OpenAPI 3.0 and works with the usual generators. Generate with unknown fields allowed, because fields are added without a version change.
