tapstateDocs
Developers and AICLI and APIs

REST API preview

Call the authenticated tapstate control-plane endpoints and interpret preview responses

The preview server exposes an HTTP control plane. This API exists only after a separate tapstate server is running; installing the CLI does not start the server or expose an HTTP port. The server image includes the web application at the server's root URL.

The API has no stable OpenAPI publication or versioned URL namespace yet; pin the tapstate release and treat request and response compatibility as prerelease.

Use the web application for interactive work. Use REST for integrations that handle authentication, coded errors, and contract changes.

Base URLs and authentication

Public endpoints and self-hosted sign-in endpoints:

MethodPathPurpose
GET/healthzProcess/engine liveness: 200 ok, or 503 broken after engine loss.
GET/versionReport the server release and its versioned contracts.
GET/.well-known/tapstateRead issuer, cluster identity, API version, and authentication modes before presenting a cached credential.
GET/connector-icons/{id}Read icon bytes for a registered connector; returns 404 if unavailable.
POST/auth/bootstrapCreate the first administrator from loopback, once.
POST/auth/loginExchange username/password for a short-lived bearer token; it can also create a revocable user session.

Read the server version

GET /version is available before authentication so a client can identify the server before it logs in or reasons about supported operations:

curl -sS http://127.0.0.1:8080/version
{
  "version": "0.7.0",
  "dslVersions": ["tapstate/v1"],
  "dataVersion": 12
}

version identifies the server release. dslVersions lists the accepted authoring grammars, and dataVersion identifies the control-store schema. The example shows v0.7.0 after its startup migrations. Use the reported values when checking an upgrade; installing a newer CLI does not change them.

Use /healthz for process/engine liveness. An engine lost to out-of-memory returns 503 with broken. A 200 response does not verify source, target, or MongoDB health and does not identify the running build.

On a self-hosted server, authenticate /api/** requests with:

Authorization: Bearer <token>

See Bootstrap and authenticate for the request sequence.

Cloud uses the managed browser session instead of the self-hosted bearer and local-user flow. Endpoint availability depends on the deployment mode.

MethodPathPurpose
GET/api/auth/meRead the authenticated identity; the response is not cacheable.
GET/api/cluster/contextRead the cluster context from the current Cloud session; Cloud only.

Cloud sign-in also exposes these system routes:

MethodPathPurpose
GET/auth/exchangeExchange a provider-issued code, set the Cloud session cookie, and redirect to /.
POST/auth/invalidate-sessionProvider back-channel callback to invalidate a Cloud session.

The invalidation callback requires jti, ts, nonce, and sign parameters verified against the provider's signature over POST|ts|nonce|jti. A user bearer token does not authorize this callback. Both routes are Cloud-only and return non-cacheable responses.

Create and use a user session

These endpoints manage local user sessions on a self-hosted server.

MethodPathPurpose
POST/auth/sessionExchange a saved user session for a fresh bearer token.
POST/auth/logoutRevoke a saved user session.

POST /auth/login remains compatible with callers that send only username and password: its token is the short-lived bearer credential for /api/**. Set createSession to true only when the client needs a revocable user session that it can later exchange for fresh bearer credentials:

{
  "username": "admin",
  "password": "<password>",
  "createSession": true
}

When requested, the response also includes sessionToken, sessionIdleExpiresAt, and sessionAbsoluteExpiresAt. Keep the session token secret. It is not a Bearer token and must not be sent to /api/**.

To obtain a fresh access token, send the session token to POST /auth/session:

Authorization: TapstateSession <sessionToken>

The response has the ordinary access-token fields (token, accessExpiresAt, issuer, principal, and scopes); use its token as the Bearer value for /api/**. Send the same TapstateSession header to POST /auth/logout to revoke the session. A successful logout returns HTTP 204.

Artifact endpoints

MethodPathPurpose
POST/api/artifacts:applyParse, validate, and apply a batch of raw YAML drafts.
POST/api/artifacts:validateParse and validate a batch of raw YAML drafts without storing them.
GET/api/artifactsList stored artifacts; optional kind query parameter.
GET/api/artifacts/{id}Read one stored artifact.
DELETE/api/artifacts/{id}Permanently remove one stored artifact; requires If-Match with the current content hash.

Artifact deletion requires a quoted 64-character content hash from the artifact's current representation:

If-Match: "<64-hex-content-hash>"

Read the artifact first to obtain the hash. The contentHash field is the unquoted 64-character value returned by GET /api/artifacts/{id} (and by the artifact list); wrap that value in quotes only when forming the If-Match header:

{
  "id": "orders_source",
  "kind": "source",
  "canonicalForm": "...",
  "contentHash": "<64-hex-content-hash>"
}

The server returns 428 when the header is missing or malformed, 412 when the hash is stale, and 204 after the artifact is removed.

Source artifacts have a display-only canonicalForm: connector config is omitted, while contentHash still identifies the full stored resource. Do not use this output as a backup or reapply it as a complete Source definition. Use the structured Source endpoints to edit an existing connection.

Apply request:

{
  "drafts": [
    {
      "source": "source/orders_source.tap.yml",
      "content": "version: tapstate/v1\nkind: source\nid: orders_source\nconnector: mysql\nconfig:\n  host: mysql.invalid\n  port: \"3306\"\n  database: appdb\n  username: tapstate\nmode: snapshot\n"
    }
  ]
}

The server reparses the YAML and returns one CREATED, UPDATED, or UNCHANGED outcome per draft.

The validate endpoint returns a validation report. HTTP 200 can contain valid: false and entries in diagnostics; check valid before treating the workspace as accepted. For example, a v0.6 View that still includes schema reports dsl.unknown-field in that list.

Apply can return dsl.upsert-needs-key after source discovery. Its params identify the affected source, table, and resource path. Select a source table with a discovered primary key, or use append only for insert-only delivery. A unique index, target-side key, or tables[].pk does not satisfy the current runtime check.

Preview pipeline data

MethodPathPurpose
POST/api/artifacts:previewExecute a bounded, no-write sample preview and stream NDJSON events.

The request contains pipelineId and a drafts array in the apply-request shape. Optional fields are outputId, rootLimit, and sampleId. A draft can include expectedContentHash to guard against a changed stored resource. The request is limited to 256 drafts and 4 MiB.

Send Accept: application/x-ndjson, application/json to accept both the event stream and JSON errors returned before streaming starts. The successful response content type is application/x-ndjson. Read each event's kind and payload; HTTP 200 means the stream opened, not that the preview succeeded. run.completed and run.failed are terminal events. Closing the connection cancels the preview.

Preview has a 45-second deadline and uses a default and maximum rootLimit of 100 root samples. It neither applies the candidate resources nor writes to the target. Results can use mongo-extended-json when the MongoDB target's codec can render them, or logical-json otherwise; inspect the result format. Sampling does not verify continuous CDC delivery. For draft compilation without sample reads, use draft:preview below.

Connector and connection endpoints

MethodPathPurpose
POST/api/connectors:registerRegister a base64-encoded connector JAR.
GET/api/connectorsList the bundled and registered online catalog.
GET/api/connectors/{id}/iconRead the registered connector icon, or 404 if unavailable.
GET/api/connectors/{id}Read one connector's normalized fields and capabilities.
POST/api/connections:testRun and store a connector connection test synchronously.
GET/api/connections/{id}/test-resultRead the latest stored test report.
POST/api/connections:discover-schemaRun and store source discovery synchronously.
GET/api/connections/{id}/schemaRead the latest stored discovered model.

Use tapstate register instead of manually base64-encoding large JARs when possible.

Connection test request:

{
  "id": "orders_source",
  "connectorId": "mysql",
  "settings": {
    "deploymentMode": "standalone",
    "host": "mysql.invalid",
    "port": "3306",
    "database": "appdb",
    "username": "tapstate"
  }
}

A connector that completes its checks normally returns HTTP 200 even when the report has outcome: "FAILED". Inspect the outcome and checks. A connector that throws during testing returns a coded error response instead.

Connection test and discovery execute synchronously and can take longer than ordinary reads. Configure the caller and any reverse proxy with an appropriate timeout, and do not retry a write blindly.

The CLI and MCP gateway wait for connection tests and discovery without a fixed request deadline. A timeout imposed by another caller or proxy does not prove that the server stopped the operation; read its stored result before retrying.

Source endpoints

MethodPathPurpose
POST/api/sources:draftValidate a structured Source draft and render canonical YAML without storing it.
POST/api/sourcesCreate one structured source.
GET/api/sourcesList structured sources.
GET/api/sources/{id}Read a secret-redacted source and its ETag.
PUT/api/sources/{id}Replace a source using If-Match.
DELETE/api/sources/{id}Delete a source using If-Match.
GET/api/sources/{id}/schemaRead the discovered schema scoped specifically to the source's selected tables.

GET /api/sources/{id}/schema filters the discovered schema to only the tables declared in the source's tables configuration. To inspect the full catalog of all discovered tables across the database, use GET /api/connections/{id}/schema.

When running POST /api/connections:test or POST /api/connections:discover-schema for an existing stored source, omitting secret fields (such as password) automatically reuses the credentials saved on the server.

POST /api/sources:draft is an authoring check, not a create operation. It returns a redacted Source view and canonical YAML only when the live connector catalog can supply the complete connector contract; it creates no artifact or audit record.

Update and delete require the quoted 64-character content hash returned in ETag. Missing or stale preconditions return HTTP 428 or 412. Secret values are not returned; use the structured secret-clearing contract deliberately when replacing a source.

Use one write model consistently. Prefer /api/artifacts:apply for resource authoring and pipeline batches. Use the structured source endpoints when a client needs source-specific draft and ETag workflows.

Sample sources and State Store setup

MethodPathPurpose
GET/api/sample-sourcesList deployment-configured sample sources.
GET/api/sample-sources/catalogRead sample definitions and availability.
POST/api/sample-sources:installInstall the deployment's default sample set synchronously.
POST/api/sample-sources/installationsStart a background installation for selected sample IDs.
GET/api/sample-sources/installations/{jobId}Read the initiating user's installation progress.
POST/api/state-storeTest and save a user-supplied MongoDB Atlas State Store connection; Cloud only.
GET/api/viewsList collections maintained by pipelines.

To install selected samples, send {"ids":["<available-sample-id>"]}. Select 1–10 distinct IDs whose catalog entries are available. The initial response contains a job id, per-item stages, and finished: false. Poll the job until finished is true, then inspect every item's stage and errorCode. A finished job can contain failures. Jobs are in-memory and can expire; use the same authenticated identity when polling.

POST /api/state-store accepts the Atlas connector configuration directly. It requires a passed connection test before creating the atlas-store Source with the store: "true" label, and refuses a second configured State Store. This user connection is separate from Cloud's internal metadata and operator state databases. The endpoint is unavailable in self-hosted mode.

Read data through a declared source

The data-browser endpoints inspect the data behind an existing source. Each request resolves the source's configured connection; callers cannot supply a database URI, database name, or database-native query command.

Data access boundary: These endpoints are read-scoped, but they can return source records and do not create audit events in this preview. There is no separate data-browser permission, so any authenticated caller with read access can use them. Keep the REST control plane off untrusted networks, use HTTPS for remote access, and issue a dedicated read-scoped token to each integration.

MethodPathPurpose
GET/api/sources/{sourceId}/collectionsList collections held by a declared source's database.
GET/api/sources/{sourceId}/collections/{collection}/statsRead connector-reported metadata for one collection.
POST/api/sources/{sourceId}/collections/{collection}:findRead a bounded preview of rows from one collection.

Collection listing returns the database's actual collections, not only resources declared in a workspace. The stats response can include numOfRows, storageSize, and avgObjSize; unavailable values are not zero. numOfRows is an estimate from connector metadata rather than a counted total.

The find request body is optional. It can contain filter, sort, and limit:

{
  "filter": {
    "field": "status",
    "op": "eq",
    "value": "paid"
  },
  "sort": {
    "field": "total",
    "dir": "desc"
  },
  "limit": 25
}

filter is either one {field, op, value} term or one all or any combination of terms. Supported operators are eq, ne, gt, gte, lt, lte, in, exists, and contains. in requires a non-empty list, exists requires a Boolean value, and contains requires text. The API rejects database-native operators and arbitrary nested Boolean groups.

Use asc or desc for sort.dir. A literal dotted field name must escape the dot in the JSON value, for example "price\\.usd". Such a field can be filtered but cannot be sorted; the server returns data-browser.unorderable-field.

find is a one-shot preview, not a paginated query. The default is 10 rows and the maximum is 200. The response includes rows, approximateTotal, and moreAvailable; it has no cursor or next-page token. approximateTotal might be unavailable for a filtered read.

In this preview, row previews are available only for sources that use the mongodb connector. Collection listing and statistics can still be available for other connectors. A row read against another connector returns data-browser.connector-not-browsable.

Stream collection changes

Connect to this WebSocket endpoint to stream changes from one collection:

/api/data-browser/{source}/{collection}/tail

On a self-hosted server, the WebSocket handshake requires the same bearer credential as other /api requests. To filter the stream, provide the structured filter as URL-encoded JSON in the filter query parameter. Each message contains a kind of INSERT, UPDATE, or DELETE, an at timestamp, and any available before or after row.

The stream is supported only for MongoDB sources in this preview. It reports changes as they reach the store, so rapid intermediate updates might not appear as separate messages. A stream with no displayed change for 10 minutes closes with data-browser.follow-idle; a deleted source or a connector stream failure also ends the connection with a coded reason.

Machine-token endpoints

On a self-hosted server, these endpoints require an administrator credential:

MethodPathPurpose
POST/api/tokensCreate a machine token with read, write, or admin scope.
GET/api/tokensList secret-free token descriptors.
POST/api/tokens/{id}:revokeRevoke a machine token immediately.

The create response returns the bearer value exactly once. Store it in a secret manager or process environment; later list responses do not return it. Scope is ordered read < write < admin, and a token can call operations at or below its own grade.

Create a write-scoped token with an administrator credential:

curl --fail-with-body --silent --show-error \
  -X POST "$TAPSTATE_URL/api/tokens" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  --data '{"scope":"write"}'

The response has this shape. The token value is returned only in this create response:

{
  "tokenId": "<token-id>",
  "scope": "WRITE",
  "token": "<one-time-bearer-value>",
  "createdAt": "<timestamp>"
}

List secret-free token metadata with GET /api/tokens. Revoke a token with POST /api/tokens/<token-id>:revoke; revocation returns HTTP 204 and takes effect immediately.

Pipeline lifecycle endpoints

MethodPathPurpose
POST/api/pipelines/{id}:startRequest a transition to RUNNING.
POST/api/pipelines/{id}:stopRequest a transition to STOPPED; requires a purgeState request body.
POST/api/pipelines/{id}:pauseRequest a transition to PAUSED.
POST/api/pipelines/{id}:resumeRequest a transition to RUNNING from PAUSED.

Stopping a pipeline requires a JSON body stating whether to clear internal checkpoint offsets and operator state:

{
  "purgeState": false
}
  • Set purgeState to false to keep checkpoints and operator state intact so a future start resumes where it left off.
  • Set purgeState to true to purge this pipeline's checkpoint position and operator state so the next start has no position to carry on from (on a dedicated source connection, this restarts from the source beginning; on a shared mining chain where other pipelines are running, shared chain state and initial-load records are preserved while this pipeline starts fresh).
  • Omitting the body or purgeState returns HTTP 400 with code lifecycle.purge-state-not-stated.

Each lifecycle response returns desired state:

{
  "pipelineId": "orders_to_operational_state",
  "targetState": "RUNNING",
  "revision": "<content-hash>"
}

The response proves that the intent was accepted. Read observed status and destination data before reporting execution success.

Pipeline layout endpoints

Persist and retrieve visual node coordinates and canvas viewport state for pipeline editors.

MethodPathPurpose
GET/api/pipelines/{id}/layoutRead node canvas positions and viewport zoom for one pipeline.
PUT/api/pipelines/{id}/layoutSave node positions and viewport.

Example layout document:

{
  "nodes": {
    "orders_source": { "x": 120.0, "y": 80.0 },
    "shape_order": { "x": 360.0, "y": 80.0 },
    "operational_state": { "x": 600.0, "y": 80.0 }
  },
  "viewport": {
    "x": 0.0,
    "y": 0.0,
    "zoom": 1.0
  }
}
  • Path identity: The request body omits the pipeline ID; the URL path owns resource identity.
  • Diagnostics: Returns 200 OK with the saved layout view. If {id} does not exist, returns HTTP 404 (pipeline.not-found).

Pipeline definition endpoints

Create or replace complete pipeline resource definitions:

MethodPathPurpose
GET/api/pipelinesList the unified catalog, including draft-only pipelines; accepts optional offset and limit.
GET/api/pipelines:artifactsList artifact-backed pipeline views; accepts optional offset and limit.
GET/api/pipelines/{id}Read one structured pipeline view and its ETag.
POST/api/pipelinesCreate a pipeline resource. Returns 201 Created with ETag.
PUT/api/pipelines/{id}Replace a pipeline definition. Requires optimistic concurrency control with If-Match.
  • Concurrency control: PUT /api/pipelines/{id} enforces optimistic concurrency control using the If-Match header. Pass the quoted SHA-256 ETag from GET (e.g. If-Match: "<hash>"). Omitting or misformatting If-Match returns HTTP 428 (pipeline.precondition-required). If the ETag does not match the server's current stored hash, the request returns HTTP 412 (pipeline.version-conflict).
  • Duplicate ID rejection: POST /api/pipelines returns HTTP 409 (pipeline.already-exists) if {id} is already registered.

Pipeline draft endpoints

A draft stores authoring state separately from the published pipeline artifact. Saving or compiling a draft does not start a pipeline.

MethodPathPurpose
GET/api/pipeline-draftsList draft summaries; accepts optional offset and limit.
GET/api/pipelines/{id}/draftRead the saved draft and its revision ETag.
POST/api/pipelines/{id}/draftCreate a draft; returns 201 and its ETag.
PUT/api/pipelines/{id}/draftReplace a draft using If-Match.
DELETE/api/pipelines/{id}/draftDiscard a draft using If-Match; returns 204.
POST/api/pipelines/{id}/draft:previewCompile the saved draft or an unsaved candidate to an artifact and DSL.
POST/api/pipelines/{id}/draft:publishCompile and apply a draft at the stated revision.
POST/api/pipelines/{id}/draft:rebaseUpdate the draft's base artifact hash after an explicit conflict review.

Draft bodies include pipelineId, mode (graph or wizard), and the matching graph or wizard document. pipelineId must match the path. The server owns revision increments, timestamps, and updatedBy.

Draft ETag values are quoted positive integer revisions, such as "3". They differ from artifact SHA-256 hashes. Copy the draft's current ETag into If-Match for replacement or deletion. Missing or malformed preconditions return 428 (pipeline-draft.precondition-required); a changed revision returns 412 (pipeline-draft.revision-conflict).

draft:preview with no body compiles the current saved revision. A body can specify revision, and optionally candidate containing an unsaved draft. It returns artifact and dsl, without applying resources or reading data.

Publish with {"revision":3,"artifactHash":"<current-artifact-hash>"}. For a new pipeline, the hash can be null. A changed underlying artifact returns 409 (pipeline-draft.artifact-conflict); review the newer artifact before rebasing with an explicit revision and artifactHash (string or null). Rebase preserves the draft edits; it does not merge them automatically. Publishing does not start or restart the pipeline. Discarding a draft does not delete its published artifact.

Pipeline position endpoints

Inspect and manage checkpoint resume positions across mining chains:

MethodPathPurpose
GET/api/pipelines/{id}/positionRead checkpoint resume tokens for each mining chain feeding the pipeline.
PUT/api/pipelines/{id}/positionUpdate checkpoint resume positions for a stopped pipeline.

Position document shape:

{
  "pipelineId": "orders_to_operational_state",
  "chains": [
    {
      "chainId": "chain_mysql_appdb",
      "sourceId": "orders_source",
      "tables": ["orders"],
      "resumeFrom": {
        "token": "<opaque-resume-token>"
      },
      "recordedAt": "2026-09-17T03:00:00Z",
      "targetAcked": null,
      "sharedWith": []
    }
  ]
}
  • Per-chain granularity: Positions belong to physical mining chains rather than individual tables. sharedWith lists any other pipelines reading the same chain.
  • Modifications require stopped state: PUT /api/pipelines/{id}/position modifies resumeFrom.token. The pipeline and all pipelines sharing its mining chains must be stopped; otherwise, the write is refused with HTTP 409 (position.write-back-while-live).

Schema evolution and derived schema endpoints

MethodPathPurpose
GET/api/pipelines/{id}/derived-schemaCompare recorded, derived, and target column models across pipeline steps.
POST/api/pipelines/{id}:accept-derived-schemaSynchronize the pipeline to current source schemas and lock the next start to the derived output.

GET /api/pipelines/{id}/derived-schema returns a list of step reports comparing three column models:

  • recorded: The schema previously recorded for that step (omitted for uninferrable steps such as custom scripts).
  • derived: The current schema worked out from stored discovery models through transforms.
  • target: The column layout declared on the destination table.

POST /api/pipelines/{id}:accept-derived-schema copies stored source discovery models into the pipeline definition and adopts the derived columns as the baseline for future runs (run source schema discovery first when upstream database structures change). Attempting to accept a derived schema while the pipeline is active is rejected with HTTP 500 (actuation.schema-sync-while-running).

Observation endpoints

MethodPathBoundary
GET/api/pipelines/{id}/statusLifecycle intent, display phase, and available runtime observation
GET/api/pipelines/{id}/metricsOpen metric map and optional perTableOffset
GET/api/pipelines/{id}/metrics/historyBounded output-rate and selected-table lag history
GET/api/pipelines/{id}/explainExplanation derived from current observation evidence
GET/api/pipelines/{id}/snapshotPer-table initial-load progress when available
GET/api/pipelines/{id}/logsBounded node-local log tail

The status response separates lifecycle intent from runtime evidence:

FieldMeaning
stateObserved lifecycle state, or NEW before the first runtime observation.
desiredStateRequested lifecycle intent, when one exists.
displayStateCatalog display phase, including DRAFT and STARTING.
hasArtifactWhether a published pipeline artifact exists.
observedState, observedAt, observedAgeMillisRuntime state and its age; omitted before the first observation.
failureObserved failure with code, params, and message; omitted when absent.
plan, awaitingRebalanceExecution-plan details and pending placement changes, when available.

A saved draft can have displayState: "DRAFT" and hasArtifact: false. A start request can produce displayState: "STARTING" while state is still NEW. Metrics, snapshot, and explain require an applied pipeline and a runtime observation. An applied pipeline without an observation returns monitor.no-observation; a draft without a published artifact returns lifecycle.unknown-pipeline.

WebSocket endpoints:

PathBehavior
/api/pipelines/{id}/status/watchEmits status when the observed state changes.
/api/pipelines/{id}/logs/followEmits newly observed node-local log lines.

On a self-hosted server, the WebSocket handshake uses the same bearer header. It returns HTTP 401 or 403 instead of upgrading when authentication or authorization fails.

Status watch waits for runtime observations; it does not invent a first frame for a known but unobserved pipeline. An unknown pipeline is refused with a coded error.

Pipeline history

GET /api/pipelines/{id}/metrics/history accepts:

ParameterRequiredContract
fromYesInclusive RFC3339 timestamp with a UTC offset.
toYesExclusive RFC3339 timestamp with a UTC offset; later than from, at most 15 days apart.
resolutionNoauto (default), raw, PT5M, PT30M, PT1H, PT3H, or PT6H.
limitNoPage point budget, 1–1000; default 240.
tableNoRepeat for each table whose lag is needed; at most 20. Omit for output rates only.
cursorNoOpaque nextCursor from the preceding page of the same query.

For example, with a bearer token held in TAPSTATE_TOKEN:

curl --fail-with-body --get "$TAPSTATE_SERVER/api/pipelines/orders/metrics/history" \
  -H "Authorization: Bearer $TAPSTATE_TOKEN" \
  --data-urlencode 'from=2026-09-29T01:00:00Z' \
  --data-urlencode 'to=2026-09-29T02:00:00Z' \
  --data-urlencode 'resolution=PT5M' \
  --data-urlencode 'table=orders' \
  --data-urlencode 'table=shipments'

Set TAPSTATE_SERVER to your server's HTTP or HTTPS base URL. Use a range containing your pipeline run.

The response includes the requested and effective ranges, retentionCutoff, effectiveResolution, status, consistency, segments, gaps, unavailable, and nullable nextCursor. Rates describe target-confirmed records and bytes. Table lag is the age of the last acknowledgement. Do not turn gaps, counter resets, or unavailable values into zeroes.

NO_RETAINED_SAMPLES can be a successful empty result. Samples default to a 60-second interval and 15-day retention. Pagination is EVENTUAL: it is not a frozen audit snapshot. To continue, repeat all query parameters unchanged and add cursor. A signed cursor lasts 10 minutes; nextCursor: null ends the walk.

CodeHTTPResponse
control.malformed-request400Correct the time range, resolution, or limit.
monitor.invalid-cursor400Repeat the original query with its cursor, or start a new query.
monitor.cursor-expired410Start a new query without the expired cursor.
monitor.query-budget-exceeded400Reduce the range or selected tables; a request can scan at most 25,000 raw samples.

Pipeline explanations

GET /api/pipelines/{id}/explain interprets the current observation. It does not run a new pipeline or inspect every target row. NO_MATCH means no explanation rule matched. Missing observations return monitor.no-observation; see Observe a pipeline.

Cluster topology

MethodPathBoundary
GET/api/cluster/membersAuthenticated cluster membership and pipeline placement read

The response contains clusterId, nullable topologyRevision, members, and pipelines. Pipeline placement is included in this response; there is no separate pipeline topology endpoint. A missing revision means unknown, not revision zero. A refused read is not an empty healthy cluster. See Configure a cluster.

Error responses

Coded failures use:

{
  "code": "lifecycle.illegal-transition",
  "params": {
    "from": "RUNNING",
    "verb": "start"
  },
  "message": "..."
}

Treat code as the stable machine key. message is reader-facing and can change.

engine.job-failed is a pipeline failure code, not a request-level HTTP error. When it appears as failure.code in a status response or status watch frame, it means that the pipeline stopped because its job failed. Inspect failure.message and the pipeline logs for the underlying cause.

Common status mappings:

HTTP statusTypical meaning
400Malformed JSON, DSL validation failure, or a request rejected as bad input
401Missing, invalid, expired, or rejected credential
403Authenticated but insufficient scope
404Artifact, source, connector, pipeline, test result, schema, or observation not found
409Closed bootstrap, an illegal/incompatible lifecycle state, or an artifact refusal such as artifact.in-use
412Source or artifact content hash, or draft revision, is stale
428An update or delete omitted a valid version precondition
500Coded server or connector failure without a client-attributable mapping
501Endpoint is present but not implemented

Artifact errors

Artifact edits and deletions use a shared artifact.* error domain:

The GET /api/artifacts/{id} read path currently returns HTTP 404 without a structured error body when the ID is absent. Handle the HTTP status first and do not require an artifact.not-found JSON envelope for that read; the coded error is used by artifact mutation/precondition paths.

CodeMeaning
artifact.not-foundThe requested artifact does not exist. A delete that already succeeded returns this code if it is attempted again.
artifact.precondition-requiredThe request did not provide a valid version precondition (428).
artifact.version-conflictThe supplied content hash is stale (412).
artifact.in-useAnother resource still refers to the artifact (409).
artifact.pipeline-not-stoppedThe artifact's pipeline is not stopped, so removal is refused (409).
artifact.reclaim-incompleteThe deletion succeeded, but dependent cleanup did not finish. Do not retry the deletion. Record the reason and residue parameters and contact the server operator; a retry returns artifact.not-found.

Lifecycle and execution errors

CodeMeaning
lifecycle.purge-state-not-statedA stop request omitted the mandatory purgeState boolean field (400).
lifecycle.illegal-transitionThe requested transition is forbidden from the pipeline's current lifecycle state (409).
lifecycle.incompatible-revisionThe pipeline's target revision is older than its latest applied revision (409).
position.write-back-while-liveAttempted to update checkpoint positions while a pipeline reading the chain is active (409).
position.chain-not-readThe position document specifies a chain not consumed by this pipeline (400).
position.field-not-editableAn immutable position attribute other than resumeFrom.token (such as resumeFrom.epoch or resumeFrom.seq) was altered (400).
actuation.schema-sync-while-runningAttempted to accept a derived schema while the pipeline is active (500).

Unexpected server errors can return HTTP 500 without the coded envelope. Preserve the status, response body, method, path, and tapstate version when reporting one.

Version boundary

  • The preview supports both interactive connected sessions and one-shot connected commands seeded with -c and -u, plus explicit creation and exchange of a revocable user session.
  • No v1 API prefix or compatibility guarantee exists yet.

On this page