Skip to main content

CAIP Workflows API Reference

Overview

The CAIP Workflows API lets teams define, version, stage, configure, and execute machine-learning and data-processing pipelines on the Connected AI Platform. Workflows are persisted in a database (the single source of truth) and translated into Argo Workflow specifications only when a run is created.

  • Base URL: https://workflow.prod.caip.api.orbit.eu-central-1.aws.cloud.bmw/
  • Version: 1.0.0

All endpoints are served under a versioned, space-scoped prefix:

/v1/spaces/{spaceId}

spaceId provides multi-tenant isolation and maps to the Kubernetes namespace at run time.

Runnable examples

For working, forkable calls against every endpoint described here, see the example repository.

Authentication

All endpoints require authentication using a JWT bearer token or M2M token in the Authorization header:

Authorization: Bearer <your-jwt-token>

Error Handling

Errors share a single JSON shape:

{ "detail": "Human-readable explanation of the error" }

The 422 validation error for unresolved configuration references uses a list of objects instead:

{ "detail": [ { "msg": "Unresolved configuration reference: ${config.BUCKET}" } ] }

Common HTTP Status Codes

Status CodeDescription
200OK - Successful read, update, or idempotent no-op
201Created - Resource created (or upserted for the first time)
204No Content - Successful delete
400Bad Request - Invalid input (e.g. unknown stage, malformed identifier)
401Unauthorized - Invalid or missing webhook signature
404Not Found - Resource (or a referenced parent) does not exist
409Conflict - Operation blocked by state (e.g. deleting a version with active runs, or a configuration still bound to a stage)
422Unprocessable Entity - A ${config.KEY} reference could not be resolved at run creation
500Internal Server Error - An error occurred on the server

Custom Method (colon) Verbs

Some actions use the Google AIP-style custom-method syntax, where a verb is appended to a resource with a colon — e.g. …/versions:validate, …/runs/{runId}:cancel, …/runs/{runId}:retry, …/triggers/{triggerId}:invoke, …/triggers/{triggerId}:rotate-secret.

Idempotency

Run creation and retry accept an optional Idempotency-Key header to de-duplicate submissions. Upserts (PUT) are inherently idempotent.

Pagination

List endpoints that support pagination use cursor-based parameters:

  • pageSize — integer, 1–100 (default 20).
  • pageToken — opaque cursor returned as nextPageToken in the previous page.

Table of Contents

  1. Workflows
  2. Workflow Versions
  3. Stage Assignments
  4. Configurations
  5. Runs
  6. Triggers
  7. Webhooks

Endpoints

Workflows

Endpoints for managing workflows within a space.

Create or Update a Workflow

  • Idempotent upsert of a workflow.
  • Endpoint: PUT /v1/spaces/{spaceId}/workflows/{workflowId}
  • Authentication: Required

Request Body:

{
"name": "ML Training Pipeline"
}

Response:

{
"spaceId": "space_12345",
"workflowId": "ml-training-pipeline",
"name": "ML Training Pipeline",
"createdAt": "2025-06-18T14:00:00.000Z",
"updatedAt": "2025-06-18T14:00:00.000Z"
}

Example Request:

curl -X PUT "https://workflows.api.caip.bmw.cloud/v1/spaces/space_12345/workflows/ml-training-pipeline" \
-H "Authorization: Bearer <your-jwt-token>" \
-H "Content-Type: application/json" \
-d '{ "name": "ML Training Pipeline" }'

Response Codes:

  • 201: Workflow created or upserted
  • 400: Invalid identifier

List Workflows

  • Endpoint: GET /v1/spaces/{spaceId}/workflows
  • Authentication: Required

Response:

{
"items": [
{
"spaceId": "space_12345",
"workflowId": "ml-training-pipeline",
"name": "ML Training Pipeline",
"createdAt": "2025-06-18T14:00:00.000Z",
"updatedAt": "2025-06-18T14:00:00.000Z"
}
],
"totalCount": 1
}

Get a Workflow

  • Endpoint: GET /v1/spaces/{spaceId}/workflows/{workflowId}
  • Authentication: Required

Response Codes:

  • 200: Workflow returned
  • 404: Workflow not found

Delete a Workflow

  • Endpoint: DELETE /v1/spaces/{spaceId}/workflows/{workflowId}
  • Authentication: Required

Response Codes:

  • 204: Workflow deleted
  • 409: Workflow has active runs

Workflow Versions

A workflow version captures the task DAG of a workflow at a point in time. Task arguments values may embed ${config.KEY} placeholders, resolved at run time from the effective configuration.

Each task's templateRef points to a platform-provided task template by name and version. The following templates are available:

Template (templateRef.name)VersionPurpose
fetch-git-codev1Clone a Git repository from BMW code hosting into the shared workspace.
fetch-git-code-atcv1Variant of fetch-git-code that authenticates with the ATC GitHub App credentials.
run-python-codev1Run a Python script in a container (single node, no Ray cluster). Chain after fetch-git-code.
submit-ray-jobv1Submit a distributed Ray job to a Ray cluster.

The Version column shows v1, a floating alias that always resolves to the latest v1.x.x release of the template. See Template versioning (SemVer) for the other reference forms.

Template versioning (SemVer)

Task templates follow Semantic Versioning. A templateRef.version accepts exactly three forms:

FormExampleMeaning
vMAJORv1Floating alias — always resolves to the latest vMAJOR.*.* release.
vMAJOR.MINORv1.2Floats to the latest patch within that minor.
vMAJOR.MINOR.PATCHv1.2.3Immutable pin — an exact release, for reproducible runs.

Every task-template version is v-prefixed; there is no bare form. A bare 1.2.3 (without the v) is rejected.

When a template publisher cuts a new version, the bump signals intent: MAJOR is a breaking change (consumers must opt in explicitly), MINOR adds backward-compatible functionality (e.g. a new optional argument), and PATCH is a backward-compatible bug fix.

Create or Update a Version

  • Idempotent upsert of the version's task DAG.
  • Endpoint: PUT /v1/spaces/{spaceId}/workflows/{workflowId}/versions/{versionId}
  • Authentication: Required

Request Body:

{
"tasks": [
{
"name": "fetch-git-code",
"templateRef": { "name": "fetch-git-code", "version": "v1" },
"arguments": {
"git-url": "git@github.com:example/repo.git",
"git-branch": "main"
},
"dependencies": []
},
{
"name": "ray-training",
"templateRef": { "name": "submit-ray-job", "version": "v1" },
"arguments": {
"entrypoint": "train.py",
"output-bucket": "s3://${config.BUCKET}/output",
"worker-replicas": "${config.RAY_REPLICAS}"
},
"dependencies": ["fetch-git-code"]
}
]
}

arguments is a flat string-to-string map. Values may contain ${config.KEY} references resolved at run time.

Response:

{
"workflowId": "ml-training-pipeline",
"versionId": "1.0.0",
"tasks": [ /* ... */ ],
"createdAt": "2025-06-18T14:00:00.000Z",
"updatedAt": "2025-06-18T14:00:00.000Z"
}

Response Codes:

  • 201: Version upserted
  • 404: Parent workflow not found

Validate a Version

  • Validates a task DAG without persisting it.
  • Endpoint: POST /v1/spaces/{spaceId}/workflows/{workflowId}/versions:validate
  • Authentication: Required

Request Body:

{ "tasks": [ /* same shape as create */ ] }

Response:

{
"valid": false,
"errors": ["Task 'ray-training' references unknown dependency 'fetch'"]
}

List Versions

  • Endpoint: GET /v1/spaces/{spaceId}/workflows/{workflowId}/versions
  • Authentication: Required
  • Supports pagination (pageSize, pageToken).

Get a Version

  • Endpoint: GET /v1/spaces/{spaceId}/workflows/{workflowId}/versions/{versionId}
  • Authentication: Required

Response Codes:

  • 200: Version returned
  • 404: Version not found

Delete a Version

  • Deletes the version and also removes any stage assignment that references it.
  • Endpoint: DELETE /v1/spaces/{spaceId}/workflows/{workflowId}/versions/{versionId}
  • Authentication: Required

Response Codes:

  • 204: Version deleted
  • 409: Version has active (non-terminal) runs

Stage Assignments

A stage assignment promotes a specific version into a stage (test, int, e2e, prod) and optionally binds a configuration to it.

Assign a Version to a Stage

  • Endpoint: PUT /v1/spaces/{spaceId}/workflows/{workflowId}/stages/{stage}
  • Authentication: Required

Request Body:

{
"versionId": "1.0.0",
"configurationId": "prod-config"
}

configurationId is optional. When set, runs and triggers on this stage resolve ${config.KEY} references from that configuration.

Response:

{
"workflowId": "ml-training-pipeline",
"stage": "prod",
"versionId": "1.0.0",
"configurationId": "prod-config",
"assignedAt": "2025-06-18T14:00:00.000Z",
"assignedBy": null
}

Response Codes:

  • 201: New assignment created
  • 200: Existing assignment updated
  • 404: Referenced versionId or configurationId does not exist
  • 400: Invalid stage

Get a Stage Assignment

  • Endpoint: GET /v1/spaces/{spaceId}/workflows/{workflowId}/stages/{stage}
  • Authentication: Required

Response Codes:

  • 200: Assignment returned
  • 404: No version assigned to the stage

List Stage Assignments

  • Endpoint: GET /v1/spaces/{spaceId}/workflows/{workflowId}/stages
  • Authentication: Required

Response:

{
"items": [
{
"workflowId": "ml-training-pipeline",
"stage": "prod",
"versionId": "1.0.0",
"configurationId": "prod-config",
"assignedAt": "2025-06-18T14:00:00.000Z",
"assignedBy": null
}
],
"totalCount": 1
}

Delete a Stage Assignment

  • Endpoint: DELETE /v1/spaces/{spaceId}/workflows/{workflowId}/stages/{stage}
  • Authentication: Required

Response Codes:

  • 204: Assignment removed

Configurations

A configuration is a named, flat key/value map (string → string) belonging to a workflow. Workflow version task arguments reference configuration values with ${config.KEY} syntax, resolved at run time. The same version can therefore run across all stages — only the bound configuration changes.

Create or Update a Configuration

  • Idempotent upsert of the configuration's data map.
  • Endpoint: PUT /v1/spaces/{spaceId}/workflows/{workflowId}/configurations/{configurationId}
  • Authentication: Required

Request Body:

{
"data": {
"BUCKET": "prod-bucket",
"MODEL_PATH": "/models/v3",
"RAY_REPLICAS": "4"
}
}

Response:

{
"configurationId": "prod-config",
"workflowId": "ml-training-pipeline",
"data": {
"BUCKET": "prod-bucket",
"MODEL_PATH": "/models/v3",
"RAY_REPLICAS": "4"
},
"createdAt": "2025-06-18T14:00:00.000Z",
"updatedAt": "2025-06-18T14:00:00.000Z"
}

Example Request:

curl -X PUT "https://workflows.api.caip.bmw.cloud/v1/spaces/space_12345/workflows/ml-training-pipeline/configurations/prod-config" \
-H "Authorization: Bearer <your-jwt-token>" \
-H "Content-Type: application/json" \
-d '{ "data": { "BUCKET": "prod-bucket", "RAY_REPLICAS": "4" } }'

Response Codes:

  • 201: Configuration created
  • 200: Existing configuration replaced

Get a Configuration

  • Endpoint: GET /v1/spaces/{spaceId}/workflows/{workflowId}/configurations/{configurationId}
  • Authentication: Required

Response Codes:

  • 200: Configuration returned
  • 404: Configuration not found

List Configurations

  • Endpoint: GET /v1/spaces/{spaceId}/workflows/{workflowId}/configurations
  • Authentication: Required

Response:

{
"items": [
{
"configurationId": "prod-config",
"workflowId": "ml-training-pipeline",
"data": { "BUCKET": "prod-bucket", "MODEL_PATH": "/models/v3", "RAY_REPLICAS": "4" },
"createdAt": "2025-06-18T14:00:00.000Z",
"updatedAt": "2025-06-18T14:00:00.000Z"
}
],
"totalCount": 1
}

Delete a Configuration

  • Endpoint: DELETE /v1/spaces/{spaceId}/workflows/{workflowId}/configurations/{configurationId}
  • Authentication: Required

Response Codes:

  • 204: Configuration deleted
  • 409: Configuration still referenced by a stage assignment (unbind it from all stages first)

Runs

A run is a single execution of a workflow version in a stage, backed by an Argo Workflow.

Create a Run

  • Triggers execution of a workflow version in a stage. versionId and stage are query parameters.
  • Endpoint: POST /v1/spaces/{spaceId}/workflows/{workflowId}/runs?versionId={versionId}&stage={stage}
  • Authentication: Required

Configuration resolution. If the version's task arguments contain ${config.KEY} references, they are resolved at run creation. Values are merged per key across up to three sources, highest priority first:

  1. Inline configuration — ad-hoc key/value map in the request body (one-off tests / overrides; nothing persisted).
  2. Inline configurationId — references an existing configuration object, in the request body.
  3. Stage-bound configurationId — from the stage assignment (normal production path).

The merge is per key: an inline source can override a single key while the rest fall back to the stage-bound configuration. If any ${config.KEY} remains unresolved, the run is rejected with 422. Triggers (webhook/schedule) always use the stage-bound configuration only.

Request Body (optional):

// Inline ad-hoc data (highest priority)
{ "configuration": { "BUCKET": "test-bucket", "RAY_REPLICAS": "2" } }
// Reference a saved configuration
{ "configurationId": "exp-config" }

Omit the body entirely to use the stage-bound configuration.

Response:

{
"workflowId": "ml-training-pipeline",
"runId": "ml-training-pipeline-xyz123",
"stage": "test",
"versionId": "1.0.0",
"configurationId": "exp-config",
"resolvedConfiguration": { "BUCKET": "test-bucket", "RAY_REPLICAS": "2" },
"status": "Running",
"createdAt": "2025-06-18T14:00:00.000Z",
"startedAt": "2025-06-18T14:00:05.000Z",
"completedAt": null,
"message": "Progress 2/5",
"tasks": []
}

resolvedConfiguration is a snapshot of the exact values used to resolve references for this run, kept for audit even though configurations are mutable. configurationId is the highest-priority persisted config that contributed (inline id, else stage-bound id, else null when only inline data was used).

Example Request:

curl -X POST "https://workflows.api.caip.bmw.cloud/v1/spaces/space_12345/workflows/ml-training-pipeline/runs?versionId=1.0.0&stage=test" \
-H "Authorization: Bearer <your-jwt-token>" \
-H "Content-Type: application/json" \
-d '{ "configuration": { "BUCKET": "test-bucket" } }'

Response Codes:

  • 201: Run created
  • 404: Workflow or version not found
  • 422: A ${config.KEY} reference could not be resolved

List Runs

  • Supports pagination (pageSize, pageToken) and filtering by status, versionId, and stage.
  • Endpoint: GET /v1/spaces/{spaceId}/workflows/{workflowId}/runs
  • Authentication: Required

Response:

{
"runs": [
{
"workflowId": "ml-training-pipeline",
"runId": "ml-training-pipeline-xyz123",
"stage": "test",
"versionId": "1.0.0",
"status": "Running",
"createdAt": "2025-06-18T14:00:00.000Z",
"startedAt": "2025-06-18T14:00:05.000Z",
"completedAt": null,
"message": "Progress 2/5",
"tasks": []
}
],
"nextPageToken": "",
"totalSize": 1
}

Get a Run

  • Returns a single run including live status and the resolved configuration snapshot.
  • Endpoint: GET /v1/spaces/{spaceId}/workflows/{workflowId}/runs/{runId}
  • Authentication: Required

Response Codes:

  • 200: Run returned
  • 404: Run not found

Cancel a Run

  • Requests cancellation of a running workflow. Idempotent.
  • Endpoint: POST /v1/spaces/{spaceId}/workflows/{workflowId}/runs/{runId}:cancel
  • Authentication: Required

Response Codes:

  • 200: Cancellation requested (or run already terminal)

Retry a Run

  • Retries failed tasks while preserving completed task results.
  • Endpoint: POST /v1/spaces/{spaceId}/workflows/{workflowId}/runs/{runId}:retry
  • Authentication: Required
  • Accepts an optional Idempotency-Key header.

Response Codes:

  • 200: Retry triggered
  • 409: Run is not in a Failed state


Triggers

A trigger binds a workflow to a stage and starts runs automatically — on an incoming webhook call or on a cron schedule. A trigger always runs the stage-bound version and configuration; it can never pass inline configuration. Each trigger has a triggerId that is unique within its workflow.

Create or Update a Trigger

  • Idempotent upsert of a trigger. A newly created webhook trigger's response includes a one-time webhookUrl; repeated PUTs do not rotate the secret (use :rotate-secret). A schedule trigger requires the target stage to have a stage assignment with both a version and a configuration, otherwise it is not created and the API responds 404; on success it schedules an Argo CronWorkflow, and enabled: false suspends it.
  • Endpoint: PUT /v1/spaces/{spaceId}/workflows/{workflowId}/triggers/{triggerId}
  • Authentication: Required

Request Body (webhook):

{
"type": "webhook",
"stage": "prod",
"enabled": true,
"config": { "signatureValidation": true }
}

Request Body (schedule):

{
"type": "schedule",
"stage": "prod",
"enabled": true,
"config": { "schedule": "0 2 * * *", "timezone": "Europe/Berlin" }
}

config.schedule (5-field cron) is required for schedule triggers and omitted for webhook triggers. config.timezone (IANA name) defaults to UTC. config.signatureValidation defaults to false.

Response (webhook create — includes the one-time webhookUrl):

{
"workflowId": "ml-training-pipeline",
"triggerId": "on-push",
"type": "webhook",
"stage": "prod",
"enabled": true,
"config": { "signatureValidation": true },
"webhookUrl": "https://workflows.api.caip.bmw.cloud/v1/webhooks/8f3c…",
"createdAt": "2025-06-18T14:00:00.000Z",
"updatedAt": "2025-06-18T14:00:00.000Z"
}

webhookUrl is returned only on create and rotate. It is shown exactly once — only its hash is stored and the plaintext is never recoverable. GET/list responses never include it.

Example Request:

curl -X PUT "https://workflows.api.caip.bmw.cloud/v1/spaces/space_12345/workflows/ml-training-pipeline/triggers/on-push" \
-H "Authorization: ******" \
-H "Content-Type: application/json" \
-d '{ "type": "webhook", "stage": "prod", "config": { "signatureValidation": true } }'

Response Codes:

  • 201: Trigger created
  • 200: Existing trigger updated
  • 404: Target stage has no version + configuration assignment (schedule triggers)

Get a Trigger

  • Returns a single trigger. The webhookUrl is never included.
  • Endpoint: GET /v1/spaces/{spaceId}/workflows/{workflowId}/triggers/{triggerId}
  • Authentication: Required

Response Codes:

  • 200: Trigger returned
  • 404: Trigger not found

List Triggers

  • Endpoint: GET /v1/spaces/{spaceId}/workflows/{workflowId}/triggers
  • Authentication: Required

Response:

{
"items": [
{
"workflowId": "ml-training-pipeline",
"triggerId": "nightly-retrain",
"type": "schedule",
"stage": "prod",
"enabled": true,
"config": { "schedule": "0 2 * * *", "timezone": "Europe/Berlin" },
"createdAt": "2025-06-18T14:00:00.000Z",
"updatedAt": "2025-06-18T14:00:00.000Z"
}
],
"totalCount": 1
}

Invoke a Trigger

  • Manually invokes a trigger for testing, starting a run from the trigger's stage version and configuration, tagged with the trigger id — the same path a schedule or webhook uses. Works for any trigger regardless of type or enabled state.
  • Endpoint: POST /v1/spaces/{spaceId}/workflows/{workflowId}/triggers/{triggerId}:invoke
  • Authentication: Required

Example Request:

curl -X POST "https://workflows.api.caip.bmw.cloud/v1/spaces/space_12345/workflows/ml-training-pipeline/triggers/nightly-retrain:invoke" \
-H "Authorization: ******"

Response Codes:

  • 201: Run created
  • 404: Trigger not found
  • 422: Target stage has no version assigned

Rotate a Webhook Secret

  • Mints a fresh secret (invalidating the old URL) and returns a new one-time webhookUrl. Webhook triggers only.
  • Endpoint: POST /v1/spaces/{spaceId}/workflows/{workflowId}/triggers/{triggerId}:rotate-secret
  • Authentication: Required

Response:

{
"workflowId": "ml-training-pipeline",
"triggerId": "on-push",
"type": "webhook",
"stage": "prod",
"enabled": true,
"config": { "signatureValidation": true },
"webhookUrl": "https://workflows.api.caip.bmw.cloud/v1/webhooks/2a91…",
"createdAt": "2025-06-18T14:00:00.000Z",
"updatedAt": "2025-06-18T15:30:00.000Z"
}

Response Codes:

  • 200: Secret rotated
  • 400: Trigger is not a webhook trigger
  • 404: Trigger not found

Delete a Trigger

  • Deletes the trigger. For a schedule trigger this also removes its Argo CronWorkflow.
  • Endpoint: DELETE /v1/spaces/{spaceId}/workflows/{workflowId}/triggers/{triggerId}
  • Authentication: Required

Response Codes:

  • 204: Trigger deleted

Webhooks

The webhook invocation endpoint is served under /v1 (not space-scoped). The secret in the path identifies the trigger — it is looked up by its SHA-256 hash.

Invoke a Webhook

  • Fires a webhook trigger, starting a run for the trigger's stage assignment. When the trigger has signatureValidation enabled, the secret is also the HMAC-SHA256 signing key: the caller must send an X-Signature-256 header holding an HMAC computed over the raw request body.
  • Endpoint: POST /v1/webhooks/{webhookSecret}
  • Authentication: The secret in the path; optionally an HMAC signature header.

Example Request (with signature validation):

curl -X POST "https://workflows.api.caip.bmw.cloud/v1/webhooks/8f3c…" \
-H "X-Signature-256: sha256=<hmac_hex>" \
-H "Content-Type: application/json" \
-d '{}'

Response Codes:

  • 201: Run created
  • 401: Invalid or missing signature
  • 404: No enabled webhook trigger matches the secret
  • 409: Target stage has no version assigned

For the always-up-to-date, machine-readable contract, use the interactive Swagger UI exposed by a running instance at /docs.