# The REST API

Everything the BitAssure portal does goes through a REST API at `https://api.bitassure.app`. With an API key, a script, a pipeline or an AI assistant can start runs and read their results.

## Authentication

Create a key under **Settings → API keys** (it starts with `ba_` and is shown once), and send it as a bearer token:

```
Authorization: Bearer ba_your_key
```

An API key acts as a **tester**: it can read everything in the account and start runs, but cannot add or remove members, create keys, delete apps or close the account. Revoke a key on the same page; it stops working at once.

## Finding ids

Every project, platform, journey and run has an id, which is the last part of its address in the portal:

| Page | Address |
|---|---|
| Project | `https://bitassure.app/projects/{projectId}` |
| Platform | `https://bitassure.app/targets/{platformId}` |
| Journey | `https://bitassure.app/journeys/{journeyId}` |
| Run | `https://bitassure.app/runs/{runId}` |

Or list them through the API:

```
GET /api/projects/{projectId}/targets       the project's platforms
GET /api/targets/{platformId}/journeys      a platform's journeys
GET /api/targets/{platformId}/runs?size=25  a platform's runs, newest first (paged)
```

## Starting a run

```
POST /api/journeys/{journeyId}/runs         one journey
POST /api/targets/{platformId}/runs         every journey on a platform, as one run
Content-Type: application/json

{"triggeredBy": "CI"}
```

`triggeredBy` is optional and is shown on the run as who started it. The answer is `201 Created` with the run, at once, while it waits its turn:

```json
{
  "id": "4f3d927e-30fb-4d84-b02a-098c330948a7",
  "status": "PENDING",
  "kind": "FUNCTIONAL",
  "triggeredBy": "CI",
  "journeyId": "2abff37d-7c00-4a17-a5bd-9a9556c2a67e",
  "targetId": "647edf85-8fe2-48cf-a4b9-1ffe69a48a26",
  "createdAt": "2026-10-15T12:22:25.898914Z",
  "startedAt": null,
  "finishedAt": null,
  "stoppedBecause": null
}
```

The credits are taken when the run is accepted. A run that errors gives them back.

### Several journeys as one

```
POST /api/pipeline-runs                      {"projectId": "…"}, {"platformId": "…"} or {"journeyIds": ["…"]}
GET  /api/pipeline-runs/{id}                 its status, and each run in it
GET  /api/pipeline-runs/{id}/junit.xml       every run as one JUnit file
GET  /api/pipeline-runs/{id}/report.pdf      a cover page, then each run's report
```

A whole app or platform means the journeys that are there when it is started. Add `"notifyOnChange": true` to email the team when the result turns. This is what the CI step uses; see [Running from CI](/docs/ci).

## Following a run

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

`status` moves from `PENDING` to `RUNNING` and ends as one of:

| Status | Means |
|---|---|
| `PASSED` | Every check held. |
| `FAILED` | A check did not hold, or the run stopped before the end. `stoppedBecause` then says where and why. |
| `ERROR` | Nothing was measured, for example a browser or phone that never came up. Not charged. |

Poll every 10 seconds or so. A run takes at most 10 minutes on a website or API and 15 on a phone. `GET /api/runs/{runId}/progress` says what it is doing right now, such as which screen it is checking.

## Reading the results

```
GET /api/runs/{runId}/results      every check, as JSON
GET /api/runs/{runId}/junit.xml    the same as JUnit XML, for CI
GET /api/runs/{runId}/report.pdf   the PDF report
GET /api/runs/{runId}/video        the run's recording, as MP4
```

Each result in `/results` reads:

```json
{
  "passed": false,
  "scenario": "Verify Basket",
  "elementName": "Total",
  "ruleType": "TEXT_EQUALS",
  "expectedValue": "$64.97",
  "actualValue": "$64.98",
  "sentence": "Total should read “$64.97”",
  "reason": "Found “$64.98” instead",
  "evaluatedAt": "2026-10-15T12:22:33.068717Z"
}
```

`sentence` and `reason` are the words a person reads on the run page and in the PDF. For an AI assistant reporting a failure, they are usually all that is needed.

## Reading a journey

```
GET /api/journeys/{journeyId}            its name and settings
GET /api/journeys/{journeyId}/feature    the journey as Given, When and Then: its scenarios, their steps and their checks
```

## Errors

Every error answer is JSON with one sentence in `message`:

```json
{"message": "Run on WEB costs 1 credit and this account has 0. Top up to run it."}
```

| Status | Means |
|---|---|
| `400` | The request was malformed, for example a missing field. |
| `401` | No key, or a key that is wrong or revoked. |
| `402` | Not enough credits. The message says how many are needed and how many are left. |
| `403` | The key is valid but may not do this, for example deleting something. |
| `404` | There is no such thing in this account. |
| `409` | It cannot be done right now, for example the JUnit file of a run still in progress. |
| `5xx` | Something went wrong on our side. Try again shortly. |

## Limits

- The account's runs at once (two, four on Monthly credits) apply to API runs too. Extra runs wait their turn.
- A key belongs to the account, not to a person, so runs it starts say "API key" followed by its name as who started them.

## Using BitAssure from an AI assistant

The easiest way is the MCP server, which gives an assistant tools to run, record and write journeys directly: see [AI assistants (MCP)](/docs/mcp). An assistant with only a shell or HTTP tool can use this API instead: give it an API key and point it at this page, or at [/llms.txt](/llms.txt) for all the docs.
