๐Ÿงข CAP 1.0 Draft

Usage

How to call CAP. The specification is the contract.

1. Find the agent

GET https://harborgoods.example/agent.txt

Use the file only if it has a CAP line.

Protocol: CAP/1.0
Name: Harbor Goods
Chat: https://harborgoods.example/chat
CAP: https://harborgoods.example/cap/v1
OAuth: https://harborgoods.example/oauth

CAP is the base URL. A person uses Chat instead.

2. Send a message

Every request carries:

CAP-Version: 1.0
CAP-From: agent:your-agent-id
POST /cap/v1/messages

{
  "message": "Running shoes in size 12, black sweatpants, and a black hoodie",
  "context": { "style": "minimal, no logos", "sizes": { "bottoms": "L", "tops": "L" } }
}

context is a short brief from what your agent knows about the person. Not their profile, and not the company's API payload.

If the answer is ready, the response is 200:

{
  "id": "msg_c1fac527668c6694",
  "status": "done",
  "reply": "Here is what fits:\n- Running shoes: Stride Runner, black, size 12 in stock, $120. https://harborgoods.example/p/stride-runner\n...",
  "created_at": "2026-10-15T17:09:09.620Z",
  "finished_at": "2026-10-15T17:09:09.621Z"
}

3. Follow up

To continue the conversation, send only the new text and the id of the last message:

POST /cap/v1/messages

{ "message": "Something cheaper?", "previous_message_id": "msg_c1fac527668c6694" }

The owner answers with every earlier turn in mind, including the brief you sent with them. Do not resend earlier turns. Keep the newest id and pass it on the next follow-up. Leave previous_message_id off to start a new conversation.

404 means the previous message is gone or not yours. 409 means it is still working, or the thread is too long. Wait for it to finish, or start a new conversation.

4. Wait when it is still working

If the answer is not ready, the response is 202:

202 Accepted
Location: /cap/v1/messages/msg_22eec828b542bf17
Retry-After: 5
{ "id": "msg_22eec828b542bf17", "status": "working", "created_at": "2026-10-15T17:09:09.631Z", "finished_at": null }

Tell the person the company is still answering. Wait Retry-After seconds, then read it:

GET /cap/v1/messages/msg_22eec828b542bf17

Each working read sets a new Retry-After. Keep going until done or failed.

Send Idempotency-Key when a resend after a dropped connection must not ask twice. The same key from the same CAP-From, with the same token or none, returns the original message.

5. Log in for account questions

Without a token, an account question fails:

{
  "id": "msg_16dcccae07e01cce",
  "status": "failed",
  "error": { "code": "login_required", "message": "Log in to Harbor Goods to ask about your order." }
}

Send the person to the OAuth server from agent.txt. They log in there and consent. Your agent receives an access token bound to that account and to your agent. Store it, and send the message again:

POST /cap/v1/messages
Authorization: Bearer <access token>
CAP-Version: 1.0
CAP-From: agent:your-agent-id

{ "message": "Where's my order?", "previous_message_id": "msg_16dcccae07e01cce" }

previous_message_id is optional here. It keeps the thread going after the login. A thread can gain a token, but it cannot continue without one or on another account.

Only the same CAP-From and the same token can read that message later.

Errors

A request the owner will not accept returns an HTTP error:

{ "error": { "code": "rate_limited", "message": "Too many messages from this agent today." } }

400 is a bad request or version. 401 is an invalid, expired, or revoked token. 403 is a token issued to a different agent. 404 is a message that does not exist, has expired, or is not yours. 409 is a follow-up to a message still working, or a thread that is too long. 429 and 503 set Retry-After.

Client algorithm

res = POST {cap}/messages { message, context, previous_message_id: last_id }
if res is 409 or 404:
  res = POST {cap}/messages { message, context }   # start a new conversation
last_id = res.id
while res.status == working:
  tell the person the company is still answering
  wait res.retry_after
  res = GET {cap}/messages/{id}

if res.status == done:
  use(res.reply)
elif res.error.code == login_required:
  ask the person to log in, then send again with the token
else:
  stop                     # failed is final for this message

Conformance checklist

An owner conforms if it:

A caller conforms if it: