Skip to main content

COG-21: REST API

Status: Draft
Version: 0.1
Created: 2026-10-31
Updated: 2026-10-31
Authors: Mike Anderson
Related specifications

This document specifies the HTTP binding of a venue's core interactions: the routes, what each answers, and what an HTTP status means. What operations, jobs and messages are is specified in COG-7: Operations, COG-8: Jobs and COG-9: Agent Messaging; how callers authenticate in COG-3: Authentication Mechanisms and COG-10: Venue Authentication & Access Control. Other bindings of the same interactions, such as COG-14: A2A Agent Interoperability, have their own specifications.

This standard specifies the REST API through which clients invoke operations on a venue, observe and control the resulting jobs, deliver messages to them, and read and store assets. Its central rules are that an HTTP status reports how the venue's API layer handled the request, never how an operation that the venue accepted turned out, and that each route's statuses are part of its contract, chosen by what the client can do next and not by how the venue fails internally.

Purpose​

A client of a venue needs to tell apart three situations that a single error code would conflate:

  • The request was refused. It was malformed, unauthenticated, not permitted, or the venue could not take it now. Nothing ran; the client may correct the request or retry.
  • The request was accepted and the operation failed. A job exists and records why it failed. The failure belongs to the operation, not to the HTTP exchange.
  • The venue failed. Something went wrong that neither the request nor the operation explains.

Status codes carry the first and third; job records carry the second. Keeping them apart lets clients, and the language models acting through them, react correctly: fix the request, read the job, or report a venue fault.

Terminology​

See COG-1: Architecture for definitions of Grid terminology. Additional terms used in this specification:

  • API layer: The venue's handling of a request before any operation runs: parsing the request, authenticating the caller, admission control, resolving the operation, and checking that the caller may make the request.
  • Accepted invocation: An invocation the API layer has admitted, for which the venue has created a job (COG-8).
  • Reason: A human-readable statement of what failed and why, complete on its own, suitable for showing to a person or a language model.

Specification​

All routes in this specification are relative to the base path /api/v1. Request and response bodies are JSON (Content-Type: application/json) unless a route states otherwise.

Status Codes​

Each route in this specification lists the situations it answers and the status for each. A venue MUST answer the status a route specifies when it is in the situation that row describes, and 500 in any situation the route does not describe. A situation is what the client can observe: its request, its own identity and authority, and the state of the resource the route addresses. How a venue represents a failure internally MUST NOT determine a status.

Statuses are deliberately coarse. The reason in the error body says what specifically happened, so a row describes a situation, not every cause that can produce it.

An HTTP status reports the API layer's handling of the request:

  • A 2xx status means the API layer accepted and answered the request.
  • A 4xx status means the API layer refused the request for a reason the caller can address.
  • A 5xx status means the venue could not handle the request: it failed, or a remote party it relayed to failed, or it is temporarily unavailable.

Once an invocation is accepted, how its operation ends is the job's outcome (COG-8). A venue MUST NOT report the failure of an accepted invocation as an API-layer refusal. In particular, an authority check that an operation makes while it runs, and that fails, fails the job; it is not a 403 response. The /run route, which returns the operation's result in place of a job, answers a failed job as specified there.

The statuses have the following meanings in every route, each stated as what the client can do:

StatusMeaning
200 OKThe request was answered; the body is the route's result
201 CreatedThe request created a resource, named by the Location header
202 AcceptedThe request was queued; its effect is observed through the job
400 Bad RequestThe request must change before it can succeed: it is malformed, or names something it cannot use, such as an operation that does not resolve
401 UnauthorizedThe caller must authenticate: the request carries no credential where one is required, or its credential is invalid
403 ForbiddenThe caller is identified and may not make this request; repeating it will not help
404 Not FoundThe resource the route addresses, such as a job or an asset, does not exist for this caller
406 Not AcceptableThe route cannot produce a representation the Accept header allows
409 ConflictThe resource the route addresses is in a state that does not allow the request, such as a finished job; repeating it will not help until that resource changes
429 Too Many RequestsThe caller must slow down; Retry-After gives the wait in seconds
500 Internal Server ErrorThe venue failed; the request is not known to be at fault
502 Bad GatewayA remote venue or service that the venue relayed the request to failed or gave an unusable answer
503 Service UnavailableThe venue cannot handle the request now and may later; the client may retry

A 404 MUST NOT be used to report that an operation, asset or other reference named inside a request body does not resolve: the route itself exists, and the request is a 400.

Where a route allows both 409 and 503, the choice turns on whether the situation can clear while the addressed resource stays as it is: if it cannot, the answer is 409; if it can, 503.

Error Responses​

Every 4xx and 5xx response carries a JSON error body:

{"error": "<reason>"}

The reason MUST say what failed and why. It MUST NOT expose implementation internals such as stack traces. A 500 reason MAY name the kind of failure that occurred, since it reports a venue fault rather than something the caller did.

A 401 response MUST carry a WWW-Authenticate: Bearer header. A 429 response MUST carry a Retry-After header.

Authentication and Admission​

Callers present credentials as specified in COG-3. The API layer applies them before any route runs:

ConditionResponse
No credential, venue allows public access (COG-10)The request proceeds as the venue's public principal
No credential, venue does not allow public access401
Credential presented but invalid401; a venue MUST NOT fall back to the public principal
Credential valid, venue refuses to admit the caller403
Caller exceeds the venue's request rate or credential-failure limits429

Admission control for invocations (COG-1, COG-8) runs before a job is created. A refused invocation creates no job:

ConditionResponse
Caller has reached the venue's limit on concurrent jobs429
Venue is shutting down503

Invocation​

Invoke​

POST /api/v1/invoke

Invokes an operation and returns its job. The request body is the invoke request of COG-8: operation, input, wait and ucans. wait MAY also be given as a query parameter, which takes precedence over the body.

StatusConditionBody
201 CreatedInvocation accepted, no waitJob record; Location: /api/v1/jobs/{id}
200 OKwait given and the job reached COMPLETE within the windowJob record
201 Createdwait given and the job did not reach COMPLETE within the window, whether it is still running or has ended FAILED, CANCELLED or REJECTEDCurrent job record; Location as above
400Body is not JSON; operation is missing or not a string; wait is malformed; private is true, since an invocation always records its job; operation does not resolve or is not an operation; input does not satisfy the operation's input schemaError
403The API layer refuses the caller this invocationError
429Admission refusedError
502A remote operation definition could not be fetchedError
503Venue shutting downError

An accepted invocation always answers 200 or 201 with the job record, whatever the job's status. A client reads a failed operation's reason from the job record's error field.

A venue MUST validate wait before creating a job, so that a malformed wait creates none.

Run​

POST /api/v1/run

Runs an operation and returns only its result. The request body carries operation, input, ucans and an optional boolean private; private: true asks for an unrecorded job where the venue supports it (COG-8). Responses MUST carry Cache-Control: private, no-store, since a result may contain material the caller alone may see.

Because the response body is the operation's result, a failed job cannot be answered with a job record. A venue answers it with an error status whose reason is the job's error:

StatusConditionBody
200 OKThe operation completedThe operation's output
400The request is malformed, operation does not resolve or is not an operation, or private runs are requested and not supportedError
400The job failed with a reason, an authority check inside the operation includedError; the reason is the job's error
403The API layer refused the caller this run before the operation startedError
429Admission refusedError
500The job failed unexpectedlyError
502A remote operation definition could not be fetchedError
503Venue shutting downError

A 403, 429, 502 or 503 from /run means no operation ran. A 400 or 500 says the job failed; it does not guarantee that the operation had no side effects before it failed.

Jobs​

List Jobs​

GET /api/v1/jobs?offset={n}&limit={n}

Answers 200 with the caller's jobs as a page: {"items": [<job id>...], "total": <n>, "offset": <n>, "limit": <n>}. A malformed offset or limit is a 400.

Job Status​

GET /api/v1/jobs/{id}
StatusConditionBody
200 OKThe caller may read the jobCurrent job record (COG-8)
400{id} is not a job IDError
403The caller may not read this jobError
404No such job for this callerError

A caller MAY present delegation proofs for a job it does not own in the X-Covia-Ucans header, since the request has no body.

Job Events​

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

Answers 200 with a text/event-stream of the job's current records (COG-8); the stream closes after the record that reaches a terminal status. The statuses of Job Status apply before the stream opens, and 406 answers an Accept header that admits neither text/event-stream nor */*. If the job becomes unreadable after the stream opens, the venue sends an error event carrying an error body and closes the stream.

Message Delivery​

POST /api/v1/jobs/{id}

Delivers a message to the job's queue (COG-9). A body that is not a JSON object is delivered as {"content": <body>}.

StatusConditionBody
202 AcceptedThe message was queued{"status": "queued"}
400{id} is not a job ID, or the body is not JSONError
403The caller may not message this jobError
404No such job for this callerError
409The job does not take messages in its current state, or at all; the reason says whichError
502The job mirrors a job on a remote venue, which did not acknowledge the message; whether it was delivered is unknownError
503The venue cannot take messages for the job now, and may laterError

202 does not mean the message has been processed. Clients observe its effect through the job.

Job Control​

PUT /api/v1/jobs/{id}/cancel
PUT /api/v1/jobs/{id}/pause
PUT /api/v1/jobs/{id}/resume
PUT /api/v1/jobs/{id}/delete

Control a job as specified in COG-8. cancel accepts an optional body {"reason": "<text>"}, which becomes the job's error.

StatusConditionBody
200 OKcancel, pause or resume applied, or cancel on a job already finishedCurrent job record
200 OKdelete removed the jobEmpty
400{id} is not a job ID, or a cancel body is not JSONError
403The caller may not control this jobError
404No such job for this callerError
409The job's state does not allow the request: pause or resume where COG-8 refuses it, or any control of a job that has not finished but that this venue is no longer runningError

Assets and Content​

Asset Metadata​

GET /api/v1/assets/{ref}
POST /api/v1/assets

GET answers 200 with the asset's metadata (COG-5) and an ETag of its asset ID. {ref} is any asset reference: an asset ID, a catalog path or a DID URL. A bare asset ID names an asset in the caller's own namespace. An asset that does not exist is a 404; one the caller may not read is a 403.

POST stores the request body as metadata of an asset in the caller's namespace and answers 201 with the asset ID as a JSON string and Location: /api/v1/assets/{id}. A body that is not valid metadata is a 400; a caller who may not store assets, the unauthenticated caller included, receives 403.

Content​

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

GET answers 200 with the content of the asset that {ref} names, streamed, with the content type its metadata declares. An asset that does not exist, declares no content, or whose content is not held is a 404; a caller who may not read it receives 403.

PUT stores the request body as the content of asset {id} and answers 200 with the content's SHA-256 hash as a JSON string. The asset's metadata MUST declare the content's sha256, and the venue MUST refuse content that does not match it.

StatusCondition
400{id} is not an asset ID; the metadata declares no valid sha256; the content does not match it or exceeds the venue's size limit
403The caller may not store content for this asset
404No such asset, or its metadata declares no content

Venue Status​

GET /api/v1/status

Answers 200 with the venue's identity, version, statistics and access policy. The route is available to anonymous callers whether or not the venue allows public access; an invalid credential is still a 401.

Security Considerations​

Reasons and Disclosure​

Reasons are written for the caller. A venue MUST NOT place credentials, secret values, or another principal's data in a reason.

Refusal Before Execution​

API-layer refusals happen before a job is created. Every 4xx answer to /invoke, and a 403, 429, 502 or 503 answer to /run, therefore means that no operation ran and no side effect occurred, and the client MAY retry without risk of duplicating an effect. A 400 or 500 answer to /run gives no such guarantee: it may report a job that failed after its operation had side effects.