---
title: "Pipeline API"
description: "Create, inspect, cancel, append to, and resume workflows."
category: "Reference"
---

Base URL: your SDK-facing API, locally `http://localhost:8081`. Read [authentication and conventions](/docs/api) first. [Download OpenAPI 3.0](/openapi.yaml) for the full machine-readable contract.

## POST /pipelines

Create a pipeline using the legacy non-idempotent contract

Preserved for existing SDK clients. Each successful request creates a
new pipeline, even when the payload is identical. New integrations that
must safely retry an unknown HTTP outcome should use
`/pipelines/idempotent`.


**Authentication:** No header security scheme; read the operation description for body credentials or delivery-token requirements.

### Request body

Required.

Content type: `application/json`. [LegacyPipelineCreateRequest](/docs/api/schemas#legacypipelinecreaterequest)

```yaml
$ref: "#/components/schemas/LegacyPipelineCreateRequest"
```

### Responses

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Pipeline created | [PipelineResponse](/docs/api/schemas#pipelineresponse) |
| `400` | Invalid request | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `401` | Invalid API key in the request body | — |
| `500` | Pipeline creation failed | — |

## POST /pipelines/idempotent

Atomically create or resolve a pipeline by idempotency key

The key is scoped to the application authenticated by `X-API-Key` and
is retained for the lifetime of the pipeline. Sequential and concurrent
requests with the same key and the same creation intent return the same
pipeline ID. Tracing metadata may change between retries. Reusing the
key for a different creation intent returns `409`.


**Authentication:** `ApiKeyAuth`

### Request body

Required.

Content type: `application/json`. [IdempotentPipelineCreateRequest](/docs/api/schemas#idempotentpipelinecreaterequest)

```yaml
$ref: "#/components/schemas/IdempotentPipelineCreateRequest"
```

### Responses

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | The existing pipeline for this application and key was returned | [IdempotentPipelineCreateResponse](/docs/api/schemas#idempotentpipelinecreateresponse) |
| `201` | A new pipeline was created | [IdempotentPipelineCreateResponse](/docs/api/schemas#idempotentpipelinecreateresponse) |
| `400` | Validation error | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `401` | Missing or invalid X-API-Key header | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `409` | The key was already used for a different creation intent | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `500` | Pipeline creation failed | [ProblemDetails](/docs/api/schemas#problemdetails) |

## POST /pipelines/by-idempotency-key

Find a pipeline by its application-scoped idempotency key

Uses a request body so the idempotency key is not placed in the URL or
routine access logs.


**Authentication:** `ApiKeyAuth`

### Request body

Required.

Content type: `application/json`. [PipelineIdempotencyLookupRequest](/docs/api/schemas#pipelineidempotencylookuprequest)

```yaml
$ref: "#/components/schemas/PipelineIdempotencyLookupRequest"
```

### Responses

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Pipeline status and stage details | [PipelineResponse](/docs/api/schemas#pipelineresponse) |
| `400` | Validation error | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `401` | Missing or invalid X-API-Key header | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `404` | No pipeline exists for this application and key | [ProblemDetails](/docs/api/schemas#problemdetails) |

## GET /pipelines/{pipelineId}

Get pipeline status and stage execution details



**Authentication:** `ApiKeyAuth`

### Parameters

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `pipelineId` | path | Yes | integer | Pipeline ID (> 0) |

### Responses

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Pipeline status and stage details | [PipelineResponse](/docs/api/schemas#pipelineresponse) |
| `400` | Invalid pipeline ID | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `401` | Invalid API key | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `403` | Pipeline belongs to another application | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `404` | Pipeline not found | — |

## POST /pipelines/{pipelineId}/cancel

Cancel a non-terminal pipeline

Atomically marks the authenticated application's pipeline and its
unfinished stages as cancelled. Cancellation of an already-running
handler is cooperative; stale results are fenced by execution metadata.


**Authentication:** `ApiKeyAuth`

### Parameters

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `pipelineId` | path | Yes | integer | Pipeline ID (> 0) |

### Responses

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Pipeline cancelled | [PipelineResponse](/docs/api/schemas#pipelineresponse) |
| `400` | Invalid pipeline ID | — |
| `401` | Invalid API key | — |
| `404` | Pipeline not found for this application | — |
| `409` | Pipeline is already terminal and cannot be cancelled | — |
| `504` | Cancellation timed out | — |

## POST /pipelines/{pipelineId}/stages

Append stages to an existing pipeline



**Authentication:** `ApiKeyAuth`

### Parameters

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `pipelineId` | path | Yes | integer | Pipeline ID (> 0) |

### Request body

Required.

Content type: `application/json`. [AppendStagesRequest](/docs/api/schemas#appendstagesrequest)

```yaml
$ref: "#/components/schemas/AppendStagesRequest"
```

### Responses

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Stages appended | [AppendStagesResponse](/docs/api/schemas#appendstagesresponse) |
| `400` | Validation error | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `404` | Pipeline not found | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `409` | Pipeline is terminal, append is not allowed | [ProblemDetails](/docs/api/schemas#problemdetails) |

## POST /stages/{stageId}/resume

Resume a stage waiting for external approval



**Authentication:** `ApiKeyAuth`

### Parameters

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `stageId` | path | Yes | integer | Stage ID (> 0) |

### Request body

Required.

Content type: `application/json`. [ResumeStageRequest](/docs/api/schemas#resumestagerequest)

```yaml
$ref: "#/components/schemas/ResumeStageRequest"
```

### Responses

| Status | Meaning | Schema |
| --- | --- | --- |
| `204` | Stage resumed | — |
| `400` | Validation error | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `404` | Stage not found | [ProblemDetails](/docs/api/schemas#problemdetails) |
| `409` | Stage not waiting for approval or conflicting repeated decision | [ProblemDetails](/docs/api/schemas#problemdetails) |

## GET /version

Server version and licence tier



**Authentication:** No header security scheme; read the operation description for body credentials or delivery-token requirements.

### Responses

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Build metadata | [VersionInfo](/docs/api/schemas#versioninfo) |

## POST /logs

Record a legacy free-form log line

Legacy log ingestion. This handler does not authenticate request headers. An
optional body apiKey associates the log with an application when its hash
matches a non-disabled key. Missing or unmatched keys still permit an unscoped
log; this lookup does not check key expiry. It does not return 401 for key
validation. Prefer stage-result logs or worker events for worker integrations.


**Authentication:** No header security scheme; read the operation description for body credentials or delivery-token requirements.

### Request body

Required.

Content type: `application/json`. [LogRequest](/docs/api/schemas#logrequest)

```yaml
$ref: "#/components/schemas/LogRequest"
```

### Responses

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Log line stored | [LogResponse](/docs/api/schemas#logresponse) |
| `400` | Malformed body | — |
| `500` | Log or keywords could not be stored | — |
