Run Covecta workflows from your own systems

Submit a document set for analysis, then collect the result — over plain REST, or over MCP if you are integrating an agent. Both drive the same engine, behind one OAuth authentication step.

Spec OpenAPI 3.1Auth OAuth 2.0 · private_key_jwtBase URL region-specific

Overview

What the API does, and what it deliberately does not.

Covecta analyses documents you have already ingested and returns structured findings — a valuation review, a credit summary, a completed assessment. The API is the machine entry point to that: you start a workflow run against documents and an account, and read back its status and outcome.

Requests are reference-shaped. You send identifiers and parameters — a document id, an account id, workflow input — never document bodies and never raw personal data. Covecta already holds the documents, and a run’s input is stored in plaintext by the workflow engine, so keeping submissions to references is a requirement, not a preference.

Every call is scoped to your tenant by the token you present. There is no parameter that widens that scope.

Your base URL

Covecta runs regionally, and your base URL is the one we give you when we register your client — every path in this documentation is relative to it. In production that is https://api.prod.covecta.io (EU) or https://api.produs.covecta.io (US). Staging and development environments have their own hosts.

The base URL is also the token aud and the OAuth issuer, so it is worth reading from configuration rather than hard-coding it in more than one place.

How integration works

Four steps, once each. Steps 1 and 2 you do once per environment; steps 3 and 4 are the loop your integration runs.

STEP 1
Register a client

You generate a key pair and send us the public key. We register a client bound to your tenant and return a client id and key version.

STEP 2
Mint an access token

Sign a short-lived JWT with your private key and exchange it at the token endpoint for a bearer token.

STEP 3
Submit a run

Name a workflow and the documents to process. You get a run id back immediately — the work has not finished.

STEP 4
Poll for the outcome

Read the run until its status is terminal, then collect the decision and any generated document.

The asynchronous model

Read this before you build

Runs take minutes, not milliseconds. Submitting a run returns 202 Accepted with an id and nothing else — there is no synchronous variant and no request you can hold open to wait for a result. Every integration polls.

A run moves from RUNNING to exactly one terminal status. Stop polling when you reach one:

RUNNINGCOMPLETEDFAILEDINTERRUPTEDESCALATED

INTERRUPTED means the run paused for a human decision; ESCALATED means it was referred to a person. Both are final as far as your integration is concerned — no further polling will change them.

The one edge case worth handling

There is a short gap between a run being accepted and becoming readable. A poll in that window returns 404. Treat an early 404 as not started yet and keep polling — not as unknown run. Only give up after your own timeout.

Authentication

One token, accepted by every surface below.

Covecta uses the OAuth 2.0 client_credentials grant with a signed private_key_jwt client assertion (RFC 7523). You hold the private key and we hold only its public half, so there is no shared secret that can leak from either side — and nothing to rotate in a hurry if one of your other vendors is breached.

The token you receive is a short-lived JWT bound to your tenant. Send it as a bearer token on every subsequent call.

The client assertion

Build a JWT, sign it with your registered private key, and set kid in the header to the key version we issued you. These claims are required and verified:

Claim Value
iss Your client id.
sub Your client id — the same value as iss.
aud Our issuer, or the token endpoint URL. Either is accepted.
iat Issued-at, in seconds.
exp Expiry. No more than 300 seconds after iat; a longer-lived assertion is rejected outright.
Signing algorithm — the usual mistake

Assertions must be signed with ES256 or PS256. RS256 is rejected, even though most JWT libraries default to it. Our profile follows FAPI 2.0, which excludes RSASSA-PKCS1-v1.5 signatures. Generate an EC P-256 key unless you have a reason to prefer RSA-PSS.

Requesting a token

pythonbuild the assertion, exchange it for a token
import time, jwt, requests

CLIENT_ID   = "6f3a91c4-2b7e-4a05-9d18-8c5e2f7b0a44"
KEY_VERSION = "1"                        # the kid we issued with your client
ISSUER      = "https://api.prod.covecta.io"   # your region's base URL — see Overview
PRIVATE_KEY = open("covecta-client-key.pem").read()

now = int(time.time())
assertion = jwt.encode(
    {
        "iss": CLIENT_ID,
        "sub": CLIENT_ID,
        "aud": ISSUER,
        "iat": now,
        "exp": now + 300,      # 5 minutes is the hard maximum
    },
    PRIVATE_KEY,
    algorithm="ES256",         # ES256 or PS256 — never RS256
    headers={"kid": KEY_VERSION},
)

response = requests.post(
    f"{ISSUER}/oauth/token",
    data={
        "grant_type": "client_credentials",
        "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
        "client_assertion": assertion,
        "scope": "run:invoke run:read",
    },
    timeout=10,
)
token = response.json()["access_token"]

Cache the token until shortly before expires_in elapses and reuse it. Do not mint one per request — you will be signing a JWT and paying a round trip for no benefit.

Endpoints

POST/oauth/tokenNo token required

OAuth 2.0 token endpoint (RFC 6749 §3.2). Authenticate with a signed private_key_jwt client assertion (RFC 7523) and receive a short-lived ES256-signed access token bound to the client's registered tenant. The supported grants and algorithms are advertised in the RFC 8414 metadata document.

Body

FieldDescription
scopestring · nullSpace-delimited scopes to grant, each within the client's registered allowlist. Omitted: the full allowlist is granted.
resourcestring · nullRFC 8707 resource indicator selecting the token's audience. Must be a resource registered with this authorization server; omitted: the issuer itself.
grant_type requiredstringAlways client_credentials — the machine-to-machine grant.
client_assertion_type requiredstringFixed RFC 7523 assertion type.
client_assertion requiredstringA JWT signed with the client's registered private key: iss = sub = client_id, aud = this server's issuer or token endpoint, kid = the registered keyVersion, exp within 5 minutes of iat.

Response · 200

FieldDescription
access_token requiredstringShort-lived KMS-signed JWT access token (RFC 9068 at+jwt).
token_type requiredstringAlways Bearer — send the token as Authorization: Bearer <access_token>.
expires_in requiredintegerAccess-token lifetime in seconds.
scope requiredstringSpace-delimited scopes actually granted.

Status codes

StatusMeaning
200A token was issued. Cache it until it expires.
400Malformed request, unsupported grant type, scope outside the client's allowlist (invalid_scope), or an unregistered resource (invalid_target).
401Client authentication failed (invalid_client) — unknown client, bad signature, wrong audience, expired or over-long assertion, or a suspended/revoked client.
503The authorization server is not configured in this environment.
GET/.well-known/oauth-authorization-serverNo token required

RFC 8414 metadata — the machine-readable description of this server's token endpoint, grants, client-authentication methods, and scopes.

Response · 200

FieldDescription
issuer requiredstringThe iss claim of every token this server mints; exact-match verified.
token_endpoint requiredstringWhere token requests are POSTed.
jwks_uri requiredstringWhere the token-verification JWK Set is published.
grant_types_supported requiredarray of stringGrants the token endpoint accepts.
token_endpoint_auth_methods_supported requiredarray of stringClient authentication methods the token endpoint accepts.
token_endpoint_auth_signing_alg_values_supported requiredarray of stringJWS algorithms accepted on client assertions.
response_types_supported requiredarray of stringAlways empty — this server issues no authorization-endpoint responses (M2M only).
scopes_supported requiredarray of stringScopes a client may be granted.

Status codes

StatusMeaning
200The server's grants, auth methods, algorithms and scopes.
503The authorization server is not configured in this environment.

Read the discovery document at startup rather than hard-coding endpoints — it names the token endpoint, the grants and client-authentication methods we accept, the signing algorithms allowed on assertions, and the scopes a client may hold.

GET/.well-known/jwks.jsonNo token required

The JWK Set (RFC 7517) access tokens verify against. Keys rotate: resolve the token's kid against this document and refetch on an unknown kid.

Response · 200

FieldDescription
keys requiredarray of objectOne JWK per active or rotating-out signing key. Tokens carry the signing key's kid; verifiers must tolerate unknown kids by refetching this document.

Status codes

StatusMeaning
200The current key set. Refetch when you meet an unknown kid.
503The authorization server is not configured in this environment.

Use the JWK Set if you want to validate our access tokens yourself. Keys rotate: resolve a token’s kid against this document, and refetch when you meet a kid you do not recognise.

Discovery

Every run submission references ids — which workflow to run, which account it belongs to, which documents to process. These read-only endpoints resolve those ids, so you never have to leave the API to find what to submit. All three require the run:read scope and return only your own tenant’s data.

List workflows

The workflows your tenant can invoke. Submit a run with the studio_workflow_id you get here.

GET/v1/workflowsBearer token

List the workflows the caller may invoke. Submit a run with workflow_id set to autonomous-valuation-analysis and studio_workflow_id set to a value returned here.

Response · 200

FieldDescription
workflows requiredarray of WorkflowSummaryWorkflows the caller's tenant may invoke.

Status codes

StatusMeaning
200The workflows the caller's tenant may invoke.
401Missing or invalid credentials.
403The caller's role or token scope does not permit this action.

List accounts

The accounts in your tenant. Submit a run with the account_id you get here.

GET/v1/accountsBearer token

List the accounts in the caller's tenant, newest first. Follow next_cursor to page; a null next_cursor is the last page. Submit a run with the account_id you get here.

Query parameters

ParameterDescription
limitinteger · nullMaximum accounts to return in one page (1-100); default 50.
cursorstring · nullOpaque page cursor — pass a prior response's next_cursor to page.

Response · 200

FieldDescription
accounts requiredarray of AccountSummaryOne page of accounts in the caller's tenant.
next_cursorstring · nullPass back as cursor for the next page; null when this is the last page.

Status codes

StatusMeaning
200One page of the caller's tenant's accounts.
400The cursor is not a valid page cursor.
401Missing or invalid credentials.
403The caller's role or token scope does not permit this action.

List an account’s documents

An account’s source documents. Each carries ingested: a run against a document that has not finished ingesting has no content to read yet, so wait for ingested: true before submitting it.

GET/v1/accounts/{account_id}/documentsBearer token

List an account's source documents. Pass a returned document_id in the documents of a POST /v1/runs; only ingested documents have content to analyse.

Path parameters

ParameterDescription
account_id requiredstringId of the account whose source documents to list.

Response · 200

FieldDescription
documents requiredarray of DocumentSummaryThe account's source documents.

Status codes

StatusMeaning
200The account's source documents.
400The account id is malformed.
401Missing or invalid credentials.
403The caller's role or token scope does not permit this action.

Workflow runs over REST

Submit a run, then poll it. Three endpoints in total.

Submit a run

POST/v1/runsBearer token

Accept a workflow invocation and return 202 with a pre-minted workflow_run_id. Execution is asynchronous: poll GET /v1/runs/{id} for status and result. A poll in the gap before the run's first fact projects returns 404 — treat an early 404 as 'not started yet', not 'unknown'.

Body

FieldDescription
workflow_id requiredstringRouting slug of the workflow to run.
studio_workflow_idstring · nullIdentifies which published agent workflow to run, by its Studio id. Either this or input.vecta_workflow_slug is REQUIRED — a run carrying neither is accepted and then fails, because the engine cannot tell what to execute.
documentsarray of RunSubmitDocument · nullThe document series the run should process; alternative to document_id.
document_idstring · nullSingle document to process, for a one-document run. Pass this or documents, never both; a submission with neither is rejected.
account_idstring · nullAccount the run belongs to — the borrower, deal, or holding the documents concern. Scopes the run's output to that account.
inputobjectAdditional workflow input parameters, passed through to the run. Carries vecta_workflow_slug — the agent workflow to run — when you are not pinning a studio_workflow_id; one of the two is required. Keys must be non-empty and dot-free, and must not collide with the fields the runner stamps on every run (documents, document_id, account_id, tenant_id, workflow_id, workflow_run_id, studio_workflow_id, source_rule_id, correlation_id, causation_event_id). Reference-shaped values only — no document bodies, no raw PII.

Response · 202

FieldDescription
workflow_run_id requiredstringId of the accepted run. Minted at submission, so it is usable for polling immediately — though the run itself is not visible until it starts.
status requiredstringAlways ACCEPTED. Execution status comes from the polling endpoint.

Status codes

StatusMeaning
202Accepted for execution. Poll the run for its outcome; it has not run yet.
400The submission is unusable — neither documents nor document_id, both supplied, or an input key that collides with a field the runner stamps on every run.
401Missing or invalid credentials.
403The caller's role or token scope does not permit this action.
503Run submission is temporarily unavailable; retry after the Retry-After interval.
Name the agent workflow, or the run fails

Alongside workflow_id, every submission must identify which agent workflow to run: either studio_workflow_id, or vecta_workflow_slug inside input. A submission carrying neither is still accepted with 202 — and then fails, because the engine cannot tell what to execute. We give you the value to use when we register your client.

jsonRequest — POST /v1/runs
{
  "account_id": "0190f3c1-8a4e-7c2b-9f10-1a2b3c4d5e6f",
  "documents": [
    {
      "doc_type": "valuation",
      "document_id": "0190f3c1-8a4e-7c2b-9f10-3d5e7a9b1c2d"
    }
  ],
  "input": {
    "vecta_workflow_slug": "valuation-review"
  },
  "workflow_id": "autonomous-valuation-analysis"
}
json202 Accepted — queued, not run
{
  "status": "ACCEPTED",
  "workflow_run_id": "0190f3c1-8a4e-7c2b-9f10-7b8c9d0e1f2a"
}

The workflow_run_id is minted at submission, so you can record it against your own case immediately. It becomes readable a moment later, once the run starts.

Poll a run

This is the endpoint your integration polls. Read it until status is terminal.

GET/v1/runs/{workflow_run_id}Bearer token

Read one workflow run's status and, once terminal, its result. Returns 404 while the run has not yet projected its first fact.

Path parameters

ParameterDescription
workflow_run_id requiredstringId of the run, as returned when it was submitted.

Response · 200

FieldDescription
workflow_run_id requiredstringId of this run, as returned at submission.
status requiredRunStatusRUNNING while in flight; COMPLETED, FAILED, INTERRUPTED, or ESCALATED once terminal. INTERRUPTED means the run paused for a human decision; ESCALATED means it was referred to a person. Stop polling on any terminal status.
workflow_id requiredstring · nullRouting slug of the workflow that ran.
workflow_name requiredstring · nullHuman-readable workflow name, when the run resolves to a published workflow; otherwise null.
started_at requiredstring · nullISO 8601 timestamp the run began. Null until it starts.
finished_at requiredstring · nullISO 8601 timestamp the run reached a terminal status; null while running.
account_id requiredstring · nullAccount the run was scoped to, when one was supplied.
document_ids requiredarray of stringDocuments the run processed.
generated_document_id requiredstring · nullId of the document the run produced, once it has been rendered; null if the run generated no document.
decision requiredenum · nullAPPROVED or REJECTED when the workflow reached a decision; null when it does not produce one, or has not yet.
error requiredstring · nullFailure reason when status is FAILED; null otherwise.

Status codes

StatusMeaning
200The run as currently known; poll again unless the status is terminal.
401Missing or invalid credentials.
403The caller's role or token scope does not permit this action.
404No run with this id is visible yet (not started, or unknown).
json200 — a completed run
{
  "account_id": "0190f3c1-8a4e-7c2b-9f10-1a2b3c4d5e6f",
  "decision": "APPROVED",
  "document_ids": [
    "0190f3c1-8a4e-7c2b-9f10-3d5e7a9b1c2d"
  ],
  "finished_at": "2026-07-29T09:17:48.006Z",
  "generated_document_id": "0190f3d5-1c22-7a41-8e03-9f7b2c6d4e11",
  "started_at": "2026-07-29T09:14:02.481Z",
  "status": "COMPLETED",
  "workflow_id": "autonomous-valuation-analysis",
  "workflow_name": "Valuation review",
  "workflow_run_id": "0190f3c1-8a4e-7c2b-9f10-7b8c9d0e1f2a"
}

List runs

GET/v1/runsBearer token

List the tenant's workflow runs (keyset-paginated), for missed-result reconciliation and status sweeps. Follow next_cursor to page.

Query parameters

ParameterDescription
statusstringFilter by run status (RUNNING, COMPLETED, FAILED, INTERRUPTED, ...).
workflow_idstringFilter by workflow definition slug.
account_idstringFilter by account.
triggered_bystringFilter by run provenance (rule, conversation, ...).
fromstringOnly runs started at or after this ISO-8601 / epoch timestamp.
tostringOnly runs started at or before this timestamp.
cursorstringOpaque keyset cursor — pass a prior response's next_cursor to page.
limitintegerMaximum runs to return in one page.

Response · 200

FieldDescription
runs requiredarray of PublicRunResponseThe runs on this page.
next_cursor requiredstring · nullOpaque cursor for the next page; pass it back as the cursor query parameter. Null on the last page.

Status codes

StatusMeaning
200One page of runs, newest first.
400A filter value is unusable — an unknown status, or a timestamp with no timezone offset.
401Missing or invalid credentials.
403The caller's role or token scope does not permit this action.

Runs come back newest first. Paginate by passing the previous response’s next_cursor back as cursor; a null next_cursor means you have reached the last page.

When you would use this

Not for collecting results in the normal course — poll the run you submitted. Use the list to reconcile after an outage on your side, when you know a window of runs completed while you were not listening.

Workflow runs over MCP

The same engine as Model Context Protocol tools, for agent clients.

If you are integrating an agent rather than a backend service, use the MCP endpoint instead of the REST routes. It exposes the same run core as tools, so an agent can invoke Covecta as a capability without you writing an HTTP client.

POST /mcp is a stateless streamable-HTTP MCP endpoint: one JSON-RPC message per request, one JSON response, no session id and no event stream to hold open. Tool input schemas are generated from the same models the REST surface validates, so the two cannot disagree.

Supported protocol versions: 2025-03-26 2025-06-18

POST/mcpBearer token

Model Context Protocol over stateless streamable HTTP: one JSON-RPC message per POST, one JSON response, no session id and no SSE stream. Send initialize, then tools/list to discover the tools and tools/call to run one. Every message must carry a bearer access token; each tool additionally requires its own scope (run:invoke, run:read). The tool schemas are published alongside this contract as mcp-tools.json.

Status codes

StatusMeaning
200The JSON-RPC result, or a JSON-RPC error object for a protocol-level failure.
202A notification was accepted; notifications carry no response body.
400The body was not a single JSON-RPC message.
401Missing or invalid access token. Carries WWW-Authenticate naming the RFC 9728 protected-resource document below.
403The token lacks the scope the called tool requires.
503Token verification is temporarily unavailable.

Tools

ToolWhat it doesRequired input
invoke_workflowSubmit a Covecta workflow run for the caller's tenant. Returns a workflow_run_id immediately; runs take minutes, so poll get_run for the outcome. Input is reference-shaped (ids and parameters), never document content.workflow_id
get_runFetch one run by workflow_run_id: status (RUNNING / COMPLETED / FAILED / INTERRUPTED / ESCALATED), timing, and outcome fields. A run is visible once it has started — treat an early not-found as 'not started yet' and keep polling.workflow_run_id
list_workflowsList the workflows the caller's tenant can invoke. Returns each workflow's studio_workflow_id (pass it to invoke_workflow), name, and description.
list_accountsList the accounts in the caller's tenant (paginated: pass limit and a prior response's next_cursor). Returns each account_id (pass it to invoke_workflow) and name.
list_documentsList an account's source documents. Returns each document_id (pass it in invoke_workflow's documents), name, and whether it is ingested — only ingested documents have content to analyse.account_id

Scopes are enforced per tool at call time, so a token holding only run:read can poll but cannot submit.

jsonRequest — tools/call
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "invoke_workflow",
    "arguments": {
      "workflow_id": "autonomous-valuation-analysis",
      "document_id": "0190f3c1-8a4e-7c2b-9f10-3d5e7a9b1c2d",
      "account_id": "0190f3c1-8a4e-7c2b-9f10-1a2b3c4d5e6f"
    }
  }
}
jsonResult — the payload rides in structuredContent
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "structuredContent": {
      "workflow_run_id": "0190f3c1-8a4e-7c2b-9f10-7b8c9d0e1f2a",
      "status": "ACCEPTED"
    },
    "isError": false
  }
}

A tool that fails returns isError: true with an explanation, rather than a JSON-RPC error — that is the MCP convention, so your agent sees the failure as a tool result it can reason about.

Discovery and auth

GET/.well-known/oauth-protected-resourceNo token required

RFC 9728 metadata for the MCP endpoint: the resource identifier, the authorization server that issues tokens for it, and the scopes it accepts. An MCP client that receives a 401 reads this to discover the auth flow.

Status codes

StatusMeaning
200The resource identifier, its authorization server, and accepted scopes.
503The resource metadata is not configured in this environment.

A 401 from /mcp carries a WWW-Authenticate header pointing at this document. A spec-following MCP client uses that to discover the whole authentication flow without being configured for it — which is the fastest path to a working integration if your client supports it.

Errors

What to retry, and what to fix.

The token endpoint returns RFC 6749 error bodies: an error code from a fixed set, plus a human-readable error_description written for whoever is debugging the integration. It never contains sensitive detail.

RFC 6749 §5.2 error response.

FieldDescription
error requiredstringWhy the request was rejected. invalid_request — malformed: a missing parameter, or one supplied twice. invalid_client — authentication failed: unknown client, bad signature, wrong audience, expired or over-long assertion, or a suspended client. invalid_scope — a requested scope is outside the client's registered allowlist. unsupported_grant_type — a grant this server does not implement. invalid_target — the requested resource is not registered for this client.
error_description requiredstringHuman-readable detail for the integrator; never sensitive.

Retry guidance

Scopes

Request only what you need. A polling service that never submits should hold run:read alone, so a leaked token cannot start work.

ScopeGrants
run:invokeSubmit workflow runs for execution
run:readRead workflow runs and their results

About this document

The reference on this page — every operation, field table, status list and example — is generated from the running service’s own annotations, published as an OpenAPI 3.1 contract at /v1/openapi.json and an MCP tool list at /v1/mcp-tools.json. It describes deployed behaviour rather than an intention, and it cannot drift from the API: the build fails if it tries to.