Skip to main content

Covia REST API

The Covia REST API provides HTTP endpoints for interacting with venues on the Grid. All venues expose a consistent API that enables clients to manage assets, invoke operations, and monitor jobs.

Base URL

The API is available at /api/v1/ on any Covia venue. For example:

https://venue-3.covia.ai/api/v1/

Authentication

Authentication requirements vary by venue. See COG-3: Authentication for supported authentication methods.

Public venues may allow unauthenticated access to read operations, while write operations typically require authentication.

Bearer tokens

Authenticated requests carry a JWT in the standard header:

Authorization: Bearer <jwt>

A venue accepts three bearer forms: a self-issued EdDSA JWT (signed with the caller's own Ed25519 key, identifying a did:key or a named venue user), a venue-signed JWT (issued after OAuth login), or an external provider JWT (verified against the provider's JWKS). See the operator authentication guide for how venues configure these.

Presenting UCAN capability proofs

Delegated authority is presented as UCAN proof tokens alongside the caller's identity. There are three transport channels, merged by the venue:

ChannelWhere it worksForm
Authorization: Bearer <ucan-jwt>Any requestA UCAN JWT as the bearer token itself
ucans body fieldPOST requests (/invoke, /run)JSON array of UCAN JWT strings
X-Covia-Ucans headerBody-less job requests (GET /api/v1/jobs/{id}, SSE)Comma-separated UCAN JWTs

The X-Covia-Ucans header exists because a GET has no body to carry the ucans array: it is how delegated and federated job observation presents proofs. The job-free values routes do not yet consult it, so a delegated read of another principal's path goes through the covia:read/covia:list operations with ucans (each a job record) until that lands. Capability enforcement is identical on every channel.

Content Type

All API requests and responses use JSON:

Content-Type: application/json

Endpoints

Status

GET /api/v1/status

Returns venue status information including DID, available assets, and operational statistics.

Response:

{
"url": "https://venue-3.covia.ai",
"ts": 1781255878753,
"status": "OK",
"name": "Covia Venue (EC2)",
"did": "did:key:z6MkovQ9NpjTsbVrSaAKEX2d3zXztSnYHjNxTi5oFs8qcrwx",
"stats": {
"assets": 130,
"users": 0,
"ops": 116
}
}

The did is the venue's persistent did:key identity, also published in its DID document.


Assets

Across the API and in operation inputs (e.g. asset:get, the file:write asset field, grid operation references), an asset can be referenced by bare hex hash, by a/<hash>, or by /a/<hash>; these are equivalent. The a/ form matches the per-user namespace convention used elsewhere (w/, o/).

GET /api/v1/assets

Lists the venue-level asset catalog (content-addressed ids). Pass scope=own (alias mine) to list the authenticated caller's own assets instead: the per-user a/ namespace populated by asset:store and asset:pin, read job-free (covia 0.9.2). The venue's operation catalog is discovered via GET /api/v1/operations, not here.

Query Parameters:

ParameterTypeDescription
offsetintegerStarting index (0-based). Default: 0
limitintegerMaximum results (max 1000). Default: all
scopestringown or mine: list the caller's own a/ assets instead of the venue catalog

Response:

{
"total": 42,
"offset": 0,
"limit": 10,
"items": [
"0x119e30db8a4ea8b33723603743591a5f8229684e6236d89ef1966a72d7293607",
"0x7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b"
]
}

POST /api/v1/assets

Registers a new asset with the venue.

Request Body: Asset metadata as JSON

{
"name": "My Dataset",
"description": "A sample dataset",
"content": {
"contentType": "text/csv",
"sha256": "119e30db8a4ea8b33723603743591a5f8229684e6236d89ef1966a72d7293607"
}
}

Response: 201 Created

"0x7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b"

The response header includes Location pointing to the new asset.

GET /api/v1/assets/{ref}

Retrieves metadata for a specific asset.

Path Parameters:

ParameterTypeDescription
refstringAsset reference: a bare CAD3 hash, a content-addressed a/<hash> path, or another resolvable asset reference (covia 0.9.3 resolves any reference form here)

Response: 200 OK

Returns the asset metadata JSON as originally registered.

GET /api/v1/assets/{id}/content

Retrieves the content of an artifact asset.

Path Parameters:

ParameterTypeDescription
idstringAsset ID (hex string)

Query Parameters:

ParameterTypeDescription
inlinebooleanSet Content-Disposition to inline

Response: 200 OK

Returns the binary content with appropriate Content-Type header based on the asset metadata.

PUT /api/v1/assets/{id}/content

Uploads content for an artifact asset. The content hash must match the content.sha256 value in the asset metadata.

Path Parameters:

ParameterTypeDescription
idstringAsset ID (hex string)

Request Body: Binary content

Response: 200 OK

"0x119e30db8a4ea8b33723603743591a5f8229684e6236d89ef1966a72d7293607"

Invocation

POST /api/v1/invoke

Invokes an operation and creates a job to track execution.

Request Body:

{
"operation": "v/ops/http/get",
"input": {
"url": "https://example.com/data"
}
}

The operation field is a resolvable reference:

  • A catalog path: v/ops/<adapter>/<op> (e.g. v/ops/http/get). The usual form; list them via GET /api/v1/operations.
  • A user pin: o/<name> from your workspace
  • An Asset ID: a/<hash> or bare hex
  • A DID URL: an operation on a remote venue

The short adapter:op style (e.g. http:get) is the operation's name as used in documentation and adapter metadata; it is not a resolvable reference and will be rejected.

Response: 201 Created

{
"id": "0x12345678901234567890123456789012",
"status": "PENDING",
"created": 1706367600000,
"operation": "0x7a8b9c0d..."
}

Invocation is asynchronous by default: the response is the job record, and you poll GET /api/v1/jobs/{id} (or subscribe via .../sse) until the job reaches a terminal status carrying its output. The Location header names the job to poll.

Waiting for the result inline. Pass wait (query parameter ?wait=… or a body field) for a synchronous response:

waitBehaviour
absent / falseAsynchronous: 201 with a job record to poll (the default)
trueBlock up to the 120s cap; return the finished record with 200 if it completes
<integer>Block up to that many milliseconds (clamped to the 120s cap)

If the job finishes within the window you get the completed record (200); otherwise the current record (201) and you continue polling. A malformed wait value is rejected with 400. The 120s cap is a server resource limit; for longer waits, poll or use SSE.

# Fire-and-poll (default)
curl -X POST .../api/v1/invoke -d '{"operation":"v/ops/http/get","input":{"url":"..."}}'

# Wait inline, up to 30 seconds
curl -X POST '.../api/v1/invoke?wait=30000' -d '{"operation":"...","input":{...}}'

POST /api/v1/run

Runs an operation and returns its result, with no job handle in the response. Use this when you only want the output; use /invoke when you need to track, stream, pause, or cancel the execution.

Request Body: identical to /invoke: operation (resolvable reference), optional input, optional ucans proof array.

curl -X POST .../api/v1/run \
-H "Content-Type: application/json" \
-d '{"operation": "v/ops/schema/infer", "input": {"value": {"name": "Ada"}}}'

Response: 200 OK with the operation output as the body, 400 for a malformed request, 403 if the operation is not authorised.

Execution still uses the normal job lifecycle internally: mutating or unclassified operations are recorded as durable jobs, while an operation marked readOnly: true may run as a transient job that is never persisted. The HTTP request remains open until the operation completes, so bound long-running work with /invoke and polling instead.


Jobs

GET /api/v1/jobs

Lists the caller's own job ids (the per-user j/ namespace).

Response:

[
"0x12345678901234567890123456789012",
"0xabcdef01234567890abcdef012345678"
]

GET /api/v1/jobs/{id}

Gets the current status of a job.

Path Parameters:

ParameterTypeDescription
idstringJob ID

Response: 200 OK

{
"id": "0x12345678901234567890123456789012",
"status": "COMPLETE",
"created": 1706367600000,
"updated": 1706367601000,
"operation": "0x7a8b9c0d...",
"output": {
"result": "Success",
"data": {}
}
}

Job Status Values:

StatusCategoryDescription
PENDINGActiveJob created, waiting to execute
STARTEDActiveJob is currently executing
COMPLETETerminalJob finished successfully with output
FAILEDTerminalJob finished with an error
CANCELLEDTerminalJob was cancelled by client or venue
REJECTEDTerminalJob was rejected before execution (e.g. policy violation)
PAUSEDInteractiveJob execution suspended, awaiting resume
INPUT_REQUIREDInteractiveJob requires additional input from the client
AUTH_REQUIREDInteractiveJob requires authorisation or credentials

GET /api/v1/jobs/{id}/sse

Server-Sent Events endpoint for real-time job status updates.

Response: an SSE stream. Each event is named job-update and its data payload is the full JSON job record (the same shape as GET /api/v1/jobs/{id}), sent on every state change. When the job reaches a terminal status the final record is sent and the stream closes; subscribing to an already-terminal job yields one final frame, then close.

event: job-update
data: {"id":"0x1234...","status":"COMPLETE","output":{...}}

Delegated or federated observation presents proofs via the X-Covia-Ucans header (see Authentication). Note that the browser EventSource API cannot set an Authorization header. The SDKs therefore stream over fetch and parse the SSE body themselves, carrying normal auth headers (TypeScript venue.jobs.stream(), SDK 1.9.0); plain EventSource clients work unauthenticated on public venues, and otherwise poll GET /api/v1/jobs/{id}.

PUT /api/v1/jobs/{id}/cancel

Cancels a running job.

Response: 200 OK with final job status, or 404 Not Found.

PUT /api/v1/jobs/{id}/pause

Pauses a running job. Only valid when the job is in a non-terminal, non-paused state (PENDING, STARTED, INPUT_REQUIRED, AUTH_REQUIRED).

Response: 200 OK with updated job status, 404 Not Found, or 409 Conflict if the job is already finished or paused.

PUT /api/v1/jobs/{id}/resume

Resumes a paused job. Only valid when the job is in PAUSED state. The venue re-engages the adapter to continue execution.

Response: 200 OK with updated job status, 404 Not Found, or 409 Conflict if the job is not paused.

PUT /api/v1/jobs/{id}/delete

Deletes a job record.

Response: 200 OK or 404 Not Found.


Values (job-free lattice reads)

Six GET routes read the venue's lattice directly, without creating a job. They exist so that dashboards, pollers, and agents can read state repeatedly without minting an audit record per read; capability enforcement is identical to the operation path. Clients that poll (such as the Covia web app) should always read through these routes and reserve /invoke for explicit actions.

Every route takes a path query parameter addressing the lattice:

PrefixContentsNotes
a/The caller's content-addressed assets
o/The caller's named operation pins
j/The caller's job records
g/The caller's agentse.g. g/my-agent/status
w/The caller's durable workspacee.g. w/notes, w/memory
s/The caller's secret namesValues are never readable
h/The caller's human-in-the-loop inbox
n/Agent-scoped notesRequires agent parameter
t/Job-scoped temporary stateRequires agent and task parameters
c/Session-scoped scratchRequires agent and session parameters
v/Venue globals: v/ops, v/info, v/agentsPublic read

The scoping parameters accompany the virtual namespaces: agent is a bare agent id under the caller (or a full agent DID), task is the agent:request job id, and session is the session id. These routes read the caller's own namespaces; delegated reads of another user's paths currently use the covia:read/covia:list operations with ucans proofs.

GET /api/v1/values/read

Reads the literal value at a path.

ParameterTypeDescription
pathstringRequired. Lattice path, e.g. w/notes, v/info/adapters
maxSizeintegerByte guard (default 1,000,000): a larger value is withheld

Response always carries an explicit existence flag, so a stored null is distinguishable from an absent path:

{ "exists": true, "value": { "theme": "dark" } }
ShapeMeaning
{"exists": true, "value": <value>}Path has data
{"exists": true, "value": null}Path holds a stored null
{"exists": false, "value": null}Path is absent
{"exists": true, "value": null, "truncated": true, "size": <bytes>}Value exceeds maxSize; use slice, list, or a larger guard

GET /api/v1/values/list

Lists the keys and structure of a lattice node.

ParameterTypeDescription
pathstringRequired. Node to list, e.g. g, w
limitintegerMaximum keys to return (default 1000)
offsetintegerKeys to skip (default 0)
fieldsstringField projection: comma-separated subpaths (max 16) read from each listed child, returned as a values map of per-key {exists, value, truncated?} results. Keyed nodes only; applies after limit/offset
maxSizeintegerPer-projected-field byte guard when fields is given (default 1,000,000)

The fields projection lets a list view fetch each child's display fields (for example fields=status,meta/updated over g) in one request instead of an N+1 fan-out.

GET /api/v1/values/slice

Reads a paginated slice of a lattice sequence (for example a job history or an agent timeline).

ParameterTypeDescription
pathstringRequired. The vector to slice, e.g. g/my-agent/timeline
offsetintegerStarting element index (default 0)
limitintegerMaximum elements (default 100)
maxSizeintegerMaximum encoded bytes of the returned page (default 1,000,000). An oversize page is a 400: reduce limit. Slice returns exact values, never summaries

GET /api/v1/values/inspect

Budget-controlled JSON5 render of a value: shape and sample content within a byte budget, for previewing large or unknown structures.

ParameterTypeDescription
pathstringRequired. Path to render, e.g. g/my-agent
budgetintegerRender budget in bytes (default 500)
compactbooleanCompact rendering, no whitespace

GET /api/v1/values/count

Fast-path count of entries below a path.

ParameterTypeDescription
pathstringRequired. Collection path, e.g. j
depthintegerSteps below the path to count at (default 1)

Response: {"exists": true, "count": 1287}

GET /api/v1/values/aggregate

Counts entries at a depth, optionally partitioned by a field.

ParameterTypeDescription
pathstringRequired. Collection path, e.g. j
depthintegerSteps below the path to count at (default 1)
groupBystringField (relative subpath) to partition by; adds a groups breakdown

Response:

{ "exists": true, "count": 42, "groups": { "COMPLETE": { "count": 37 }, "FAILED": { "count": 5 } } }

Operations

GET /api/v1/operations

Lists all registered operations across all adapters.

Response:

[
{
"name": "covia:read",
"description": "Read a value at any lattice path",
"adapter": "covia",
"input": { ... }
}
]

GET /api/v1/operations/{name}

Gets details for a specific operation by name.

Path Parameters:

ParameterTypeDescription
namestringOperation name (e.g., covia:read, agent:create)

Schedules (job-free read)

GET /api/v1/schedules

Lists the authenticated caller's pending scheduled events, time-ordered, without creating a job (covia 0.9.2). Includes events queued by the caller's agents. Requires authentication (401 otherwise).

Response: an array of {handle, op, time} entries, where handle identifies the schedule for scheduler:cancel/scheduler:trigger, op is the target operation reference, and time is the next run in epoch milliseconds.


Agents (job-free reads)

Two GET routes read agent state without creating a job. Agent actions (create, chat, request, suspend, and the rest) remain operations under v/ops/agent/; see Agent Operations.

GET /api/v1/agents

Lists the authenticated caller's agents. Requires authentication (401 otherwise).

ParameterTypeDescription
statusbooleanfalse returns bare agent ids instead of the default annotated form
includeTerminatedbooleantrue includes terminated agents (hidden by default)

Response (default annotated form):

[
{ "agentId": "researcher", "status": "SLEEPING", "tasks": 2 },
{ "agentId": "assistant", "status": "RUNNING", "tasks": 0 }
]

Entries carry agentId, status, tasks, and error when present, so a list view needs no per-agent fan-out.

GET /api/v1/agents/{id}

Gets one of the caller's agents: the same payload as the agent:info operation. Requires authentication.

Response: 200 OK with the agent info record, or 404 Not Found for a missing agent id.


Secrets

GET /api/v1/secrets

Lists secret names for the authenticated user. Values are never returned.

Response:

["OPENAI_API_KEY", "ANTHROPIC_API_KEY"]

PUT /api/v1/secrets/{name}

Stores a secret value (encrypted per-user).

Request Body:

"sk-proj-abc123..."

Response: 200 OK

DELETE /api/v1/secrets/{name}

Deletes a secret.

Response: 200 OK or 404 Not Found.


Discovery Endpoints

GET /.well-known/did.json

Returns the DID document for the venue, following W3C DID specification.

Response: 200 OK

{
"@context": "https://www.w3.org/ns/did/v1",
"id": "did:key:z6MkovQ9NpjTsbVrSaAKEX2d3zXztSnYHjNxTi5oFs8qcrwx",
"service": [
{
"type": "Covia.API.v1",
"serviceEndpoint": "https://venue-3.covia.ai/api/v1"
}
],
"verificationMethod": [
{
"id": "did:key:z6MkovQ9...#z6MkovQ9...",
"type": "Multikey",
"controller": "did:key:z6MkovQ9...",
"publicKeyMultibase": "z6MkovQ9NpjTsbVrSaAKEX2d3zXztSnYHjNxTi5oFs8qcrwx"
}
],
"authentication": ["did:key:z6MkovQ9...#z6MkovQ9..."]
}

The document id is the venue's persistent did:key; the same key is also listed under assertionMethod, capabilityDelegation and capabilityInvocation. The venue remains reachable by did:web:<host> references; this endpoint is what resolves them to the API serviceEndpoint.

GET /.well-known/mcp.json

MCP server discovery endpoint.

GET /.well-known/agent-card.json

A2A agent card discovery endpoint.

GET /a/{id}/did.json

Returns the DID document for a specific asset.

GET /u/{id}/did.json

Returns the DID document for a user (did:web resolution).


MCP Endpoints

POST /mcp

MCP JSON-RPC endpoint for tool listing, tool calls, and notifications. See Venues as MCP Servers for details.

GET /mcp

MCP SSE session establishment for server-to-client notifications.

DELETE /mcp

Close an MCP session.


A2A Endpoint

POST /a2a

Agent-to-Agent JSON-RPC endpoint for federated agent operations.


DLFS (WebDAV)

When WebDAV is enabled (webdav.enabled: true in venue config), DLFS drives are accessible via standard WebDAV at /dlfs/:

MethodPathDescription
GET/dlfs/{drive}/{path}Read file
PUT/dlfs/{drive}/{path}Write/upload file
DELETE/dlfs/{drive}/{path}Delete file
MKCOL/dlfs/{drive}/{path}Create directory
PROPFIND/dlfs/{drive}/{path}List directory
MOVE/dlfs/{drive}/{path}Move/rename file
COPY/dlfs/{drive}/{path}Copy file
OPTIONS/dlfs/*WebDAV capability discovery

See DLFS Adapter for details.


Documentation Endpoints

EndpointDescription
GET /openapiOpenAPI 3.0 JSON schema
GET /swaggerSwagger UI (interactive API docs)
GET /redocReDoc UI
GET /llms.txtLLM capabilities file

Authentication Endpoints

Login

EndpointDescription
GET /loginLogin page listing configured OAuth providers
GET /auth/{provider}Initiate OAuth login
GET /auth/{provider}/callbackOAuth callback URL

Bearer Token

Authorization: Bearer <JWT>

Supported token types:

  • EdDSA self-issued tokens (did:key)
  • Venue-signed JWTs
  • OAuth provider RS256 tokens

See COG-10: Authentication for details.

UCAN Proofs

Operations can include UCAN capability proofs:

{
"operation": "v/ops/covia/write",
"input": { "path": "w/data", "value": {...} },
"ucans": ["<ucan-token>"]
}

See COG-13: Agent Capabilities for the capability model.

Error Responses

Errors return appropriate HTTP status codes with a JSON body:

{
"error": "Asset not found: 0x1234..."
}
StatusDescription
400Bad request (invalid parameters)
401Authentication required
403Forbidden (insufficient capabilities)
404Resource not found
409Conflict (invalid state transition, e.g. pausing a finished job)
500Server error