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.
OpenAPI 3.1Auth OAuth 2.0 · private_key_jwtBase URL region-specificOverview
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.
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.
Mint an access token
Sign a short-lived JWT with your private key and exchange it at the token endpoint for a bearer token.
Submit a run
Name a workflow and the documents to process. You get a run id back immediately — the work has not finished.
Poll for the outcome
Read the run until its status is terminal, then collect the decision and any generated document.
The asynchronous model
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:
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.
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. |
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
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
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
| Field | Description |
|---|---|
| scopestring · null | Space-delimited scopes to grant, each within the client's registered allowlist. Omitted: the full allowlist is granted. |
| resourcestring · null | RFC 8707 resource indicator selecting the token's audience. Must be a resource registered with this authorization server; omitted: the issuer itself. |
| grant_type requiredstring | Always client_credentials — the machine-to-machine grant. |
| client_assertion_type requiredstring | Fixed RFC 7523 assertion type. |
| client_assertion requiredstring | A 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
| Field | Description |
|---|---|
| access_token requiredstring | Short-lived KMS-signed JWT access token (RFC 9068 at+jwt). |
| token_type requiredstring | Always Bearer — send the token as Authorization: Bearer <access_token>. |
| expires_in requiredinteger | Access-token lifetime in seconds. |
| scope requiredstring | Space-delimited scopes actually granted. |
Status codes
| Status | Meaning |
|---|---|
| 200 | A token was issued. Cache it until it expires. |
| 400 | Malformed request, unsupported grant type, scope outside the client's allowlist (invalid_scope), or an unregistered resource (invalid_target). |
| 401 | Client authentication failed (invalid_client) — unknown client, bad signature, wrong audience, expired or over-long assertion, or a suspended/revoked client. |
| 503 | The authorization server is not configured in this environment. |
RFC 8414 metadata — the machine-readable description of this server's token endpoint, grants, client-authentication methods, and scopes.
Response · 200
| Field | Description |
|---|---|
| issuer requiredstring | The iss claim of every token this server mints; exact-match verified. |
| token_endpoint requiredstring | Where token requests are POSTed. |
| jwks_uri requiredstring | Where the token-verification JWK Set is published. |
| grant_types_supported requiredarray of string | Grants the token endpoint accepts. |
| token_endpoint_auth_methods_supported requiredarray of string | Client authentication methods the token endpoint accepts. |
| token_endpoint_auth_signing_alg_values_supported requiredarray of string | JWS algorithms accepted on client assertions. |
| response_types_supported requiredarray of string | Always empty — this server issues no authorization-endpoint responses (M2M only). |
| scopes_supported requiredarray of string | Scopes a client may be granted. |
Status codes
| Status | Meaning |
|---|---|
| 200 | The server's grants, auth methods, algorithms and scopes. |
| 503 | The 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.
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
| Field | Description |
|---|---|
| keys requiredarray of object | One 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
| Status | Meaning |
|---|---|
| 200 | The current key set. Refetch when you meet an unknown kid. |
| 503 | The 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.
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
| Field | Description |
|---|---|
| workflows requiredarray of WorkflowSummary | Workflows the caller's tenant may invoke. |
Status codes
| Status | Meaning |
|---|---|
| 200 | The workflows the caller's tenant may invoke. |
| 401 | Missing or invalid credentials. |
| 403 | The 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.
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
| Parameter | Description |
|---|---|
| limitinteger · null | Maximum accounts to return in one page (1-100); default 50. |
| cursorstring · null | Opaque page cursor — pass a prior response's next_cursor to page. |
Response · 200
| Field | Description |
|---|---|
| accounts requiredarray of AccountSummary | One page of accounts in the caller's tenant. |
| next_cursorstring · null | Pass back as cursor for the next page; null when this is the last page. |
Status codes
| Status | Meaning |
|---|---|
| 200 | One page of the caller's tenant's accounts. |
| 400 | The cursor is not a valid page cursor. |
| 401 | Missing or invalid credentials. |
| 403 | The 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.
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
| Parameter | Description |
|---|---|
| account_id requiredstring | Id of the account whose source documents to list. |
Response · 200
| Field | Description |
|---|---|
| documents requiredarray of DocumentSummary | The account's source documents. |
Status codes
| Status | Meaning |
|---|---|
| 200 | The account's source documents. |
| 400 | The account id is malformed. |
| 401 | Missing or invalid credentials. |
| 403 | The 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
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
| Field | Description |
|---|---|
| workflow_id requiredstring | Routing slug of the workflow to run. |
| studio_workflow_idstring · null | Identifies 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 · null | The document series the run should process; alternative to document_id. |
| document_idstring · null | Single document to process, for a one-document run. Pass this or documents, never both; a submission with neither is rejected. |
| account_idstring · null | Account the run belongs to — the borrower, deal, or holding the documents concern. Scopes the run's output to that account. |
| inputobject | Additional 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
| Field | Description |
|---|---|
| workflow_run_id requiredstring | Id 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 requiredstring | Always ACCEPTED. Execution status comes from the polling endpoint. |
Status codes
| Status | Meaning |
|---|---|
| 202 | Accepted for execution. Poll the run for its outcome; it has not run yet. |
| 400 | The 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. |
| 401 | Missing or invalid credentials. |
| 403 | The caller's role or token scope does not permit this action. |
| 503 | Run submission is temporarily unavailable; retry after the Retry-After interval. |
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.
{
"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"
}{
"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.
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
| Parameter | Description |
|---|---|
| workflow_run_id requiredstring | Id of the run, as returned when it was submitted. |
Response · 200
| Field | Description |
|---|---|
| workflow_run_id requiredstring | Id of this run, as returned at submission. |
| status requiredRunStatus | RUNNING 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 · null | Routing slug of the workflow that ran. |
| workflow_name requiredstring · null | Human-readable workflow name, when the run resolves to a published workflow; otherwise null. |
| started_at requiredstring · null | ISO 8601 timestamp the run began. Null until it starts. |
| finished_at requiredstring · null | ISO 8601 timestamp the run reached a terminal status; null while running. |
| account_id requiredstring · null | Account the run was scoped to, when one was supplied. |
| document_ids requiredarray of string | Documents the run processed. |
| generated_document_id requiredstring · null | Id of the document the run produced, once it has been rendered; null if the run generated no document. |
| decision requiredenum · null | APPROVED or REJECTED when the workflow reached a decision; null when it does not produce one, or has not yet. |
| error requiredstring · null | Failure reason when status is FAILED; null otherwise. |
Status codes
| Status | Meaning |
|---|---|
| 200 | The run as currently known; poll again unless the status is terminal. |
| 401 | Missing or invalid credentials. |
| 403 | The caller's role or token scope does not permit this action. |
| 404 | No run with this id is visible yet (not started, or unknown). |
{
"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
List the tenant's workflow runs (keyset-paginated), for missed-result reconciliation and status sweeps. Follow next_cursor to page.
Query parameters
| Parameter | Description |
|---|---|
| statusstring | Filter by run status (RUNNING, COMPLETED, FAILED, INTERRUPTED, ...). |
| workflow_idstring | Filter by workflow definition slug. |
| account_idstring | Filter by account. |
| triggered_bystring | Filter by run provenance (rule, conversation, ...). |
| fromstring | Only runs started at or after this ISO-8601 / epoch timestamp. |
| tostring | Only runs started at or before this timestamp. |
| cursorstring | Opaque keyset cursor — pass a prior response's next_cursor to page. |
| limitinteger | Maximum runs to return in one page. |
Response · 200
| Field | Description |
|---|---|
| runs requiredarray of PublicRunResponse | The runs on this page. |
| next_cursor requiredstring · null | Opaque cursor for the next page; pass it back as the cursor query parameter. Null on the last page. |
Status codes
| Status | Meaning |
|---|---|
| 200 | One page of runs, newest first. |
| 400 | A filter value is unusable — an unknown status, or a timestamp with no timezone offset. |
| 401 | Missing or invalid credentials. |
| 403 | The 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.
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
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
| Status | Meaning |
|---|---|
| 200 | The JSON-RPC result, or a JSON-RPC error object for a protocol-level failure. |
| 202 | A notification was accepted; notifications carry no response body. |
| 400 | The body was not a single JSON-RPC message. |
| 401 | Missing or invalid access token. Carries WWW-Authenticate naming the RFC 9728 protected-resource document below. |
| 403 | The token lacks the scope the called tool requires. |
| 503 | Token verification is temporarily unavailable. |
Tools
| Tool | What it does | Required input |
|---|---|---|
| invoke_workflow | Submit 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_run | Fetch 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_workflows | List the workflows the caller's tenant can invoke. Returns each workflow's studio_workflow_id (pass it to invoke_workflow), name, and description. | — |
| list_accounts | List 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_documents | List 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.
{
"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"
}
}
}{
"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
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
| Status | Meaning |
|---|---|
| 200 | The resource identifier, its authorization server, and accepted scopes. |
| 503 | The 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.
| Field | Description |
|---|---|
| error requiredstring | Why 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 requiredstring | Human-readable detail for the integrator; never sensitive. |
Retry guidance
- 400 — do not retry. The request is unusable; the
messagesays how. - 401 or 403 — do not retry. Fix the credentials, the assertion, or the requested scopes.
- 404 while polling — retry. The run has not become readable yet.
- 503 — retry, honouring
Retry-Afterwhere present. Back off exponentially.
Scopes
Request only what you need. A polling service that never submits should hold run:read alone, so
a leaked token cannot start work.
| Scope | Grants |
|---|---|
| run:invoke | Submit workflow runs for execution |
| run:read | Read 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.