Private beta

Request beta access

Interagentic is in private beta. Leave your email and we'll send you an access key when there's room.

Interagentic
Protocols

Services

How an API describes itself so agents can discover and call it — an index and one file per action, served statically. Specification v1.

Status v1 · Discovery root /.well-known/interagentic/

A service describes itself with static JSON. Clients fetch the index to learn what exists, then fetch one action file to learn how to call it. Nothing is generated at request time, so every file can be built once and served from a CDN.

1. Files

PathContents
/.well-known/interagentic/services.jsonIndex — identity, groups, action summaries
/.well-known/interagentic/services/<group>/<action>.jsonOne action — its arguments, how they map onto the request, and an example

An action file's path mirrors its command, one directory per segment: ["orders", "refunds", "create"] lives at services/orders/refunds/create.json.

Every file MUST be served over HTTPS with Content-Type: application/json. Clients SHOULD cache them and MAY probe the index with HEAD to test compatibility.

2. Index

{
  "name": "Popcorn Club",
  "description": "Popcorn, delivered.",
  "version": "1",
  "auth": "interagentic",
  "baseUrl": "/api/v1",
  "groups": [
    { "command": ["orders"], "description": "Place and track orders" }
  ],
  "actions": [
    {
      "command": ["orders", "create"],
      "endpoint": "/orders",
      "method": "POST",
      "description": "Place an order",
      "role": "user",
      "payment": "fixed"
    }
  ]
}
FieldTypeRequiredNotes
namestringyesHuman-readable service name
descriptionstringyesOne line
versionstringyes"1"
authstringyes"interagentic" — the service verifies agent tokens
baseUrlstringyesFixed prefix for every endpoint, e.g. /api/v1
groupsarraynoDescriptions for intermediate command nodes
actionsarrayyesAction summaries

The caller's identity travels in the JWT, so it MUST NOT appear in baseUrl or in an endpoint.

Action summary

FieldTypeRequiredNotes
commandstring[]yesSubcommand chain. See the command model.
endpointstringyesAppended to baseUrl. May contain templates.
methodstringyesGET, POST, PUT, PATCH or DELETE
descriptionstringyesHelp text for this command
rolestringnouser (default) or admin
paymentstringyesfree, fixed, budget or provider_access

3. Action detail

{
  "command": ["orders", "create"],
  "endpoint": "/orders",
  "method": "POST",
  "description": "Place an order",
  "role": "user",
  "payment": { "mode": "fixed", "amount": 9.0 },
  "args": ["qty"],
  "params": [
    {
      "name": "qty",
      "label": "Quantity",
      "description": "How many bags to send",
      "example": 2,
      "required": true,
      "type": { "type": "integer", "minimum": 1 }
    }
  ],
  "example": {
    "input": { "qty": 2 },
    "output": { "id": "ord_8fJ2", "status": "confirmed" }
  }
}

Parameters

params declares the arguments a caller can give — what they are called, what type they have, and whether they are required. Where each one ends up in the HTTP request is a separate concern, described by request templates.

FieldTypeRequiredNotes
namestringyesThe argument's name, and its --flag
labelstringyesHuman-readable label
descriptionstringyesWhat it does
requiredbooleanyes
typeobjectyesA JSON Schema fragment
exampleanynoUsed in generated help
placeholderstringnoFor form rendering

args lists parameter names that may be given positionally, in order. Anything not in args is a named flag.

Files

When an action accepts uploads, a top-level files object describes the rules:

{
  "bodyKey": "files",
  "format": { "path": "string", "contents": "string | base64DataString" },
  "dirs": false,
  "multiple": true,
  "maxFiles": null,
  "extensions": { "whitelist": [".png", ".jpg"], "blacklist": [] }
}

4. Request templates

Templates map a command's arguments onto the HTTP request. They are what let an API whose shape does not match its arguments one-to-one — a path parameter with a different name, a nested body, a query flag — be described without changing the API.

{
  "command": ["orders", "refunds", "create"],
  "endpoint": "/orders/{{$id}}/refunds",
  "method": "POST",
  "args": ["id"],
  "params": [
    { "name": "id", "label": "Order", "description": "The order to refund", "required": true, "type": { "type": "string" } },
    { "name": "reason", "label": "Reason", "description": "Why it is refunded", "required": true, "type": { "type": "string", "enum": ["damaged", "late", "other"] } },
    { "name": "amount", "label": "Amount", "description": "Partial refund. Omit to refund in full.", "required": false, "type": { "type": "number" } },
    { "name": "notify", "label": "Notify", "description": "Email the customer", "required": false, "type": { "type": "boolean" } }
  ],
  "request": {
    "query": { "notify": "{{$notify}}" },
    "body": {
      "reason": "{{$reason}}",
      "refund": { "amount": "{{$amount}}" }
    }
  }
}
npx interagentic popcorn.club orders refunds create ord_8fJ2 \
  --reason damaged --amount 4.5 --notify
POST /api/v1/orders/ord_8fJ2/refunds?notify=true
Content-Type: application/json

{ "reason": "damaged", "refund": { "amount": 4.5 } }

Syntax

{{$name}} is the value of argument name. {{$name.a.b}} reads a path inside a structured argument — one whose type is an object, given as JSON:

"body": {
  "shipping_city": "{{$address.city}}",
  "shipping_zip": "{{$address.zip}}"
}
npx interagentic popcorn.club orders create --qty 2 \
  --address '{"city": "Paris", "zip": "75001"}'

Templates always begin with $. The unprefixed {{NAME}} form is a credential placeholder, which only the proxy in Secure calls interprets; a client never substitutes it.

Rules

  1. Where templates may appear. In endpoint, and in any string value of request.query, request.headers and request.body, at any depth.
  2. Types are preserved. A value that is exactly one template takes the argument's typed value — "{{$amount}}" becomes the number 4.5, not the string "4.5". A template inside a longer string is interpolated as text.
  3. Escaping. Values substituted into endpoint or request.query are URL-encoded. Values in request.body are JSON-encoded.
  4. Missing arguments. When an optional argument is not given, every key whose value uses it is omitted — nothing is sent as null or as an empty string. Arguments used in endpoint MUST be required.
  5. request is complete when present. If an action has a request object, arguments it does not reference are not sent.
  6. Defaults when absent. Without a request object, arguments not used in endpoint are sent under their own names — as a JSON body for POST, PUT and PATCH, and as query parameters for GET and DELETE. Most actions need no templates at all.
  7. Reserved headers. Authorization is set by the client and MUST NOT be templated.

Literals are allowed anywhere a template is, so constants need no argument:

"request": {
  "headers": { "X-Api-Version": "2026-09" },
  "body": { "channel": "agent", "qty": "{{$qty}}" }
}

5. Payment modes

ModeMeaning
freeNo charge
fixedA known price, given in the action detail's payment.amount
budgetPriced at run time, within a limit the human approved
provider_accessAccess to a third-party provider the service resells

See Credits for what a service returns when payment is required.

6. Calling

<method> https://<domain><baseUrl><endpoint>
Authorization: Bearer <agent jwt>

The client validates the arguments against params, renders endpoint and request as described above, and sends the result. A command whose arguments fail validation is rejected before any request is made.

Response rendering

Clients append ?humanReadable=true unless asked for raw output. A service MAY answer that with Content-Type: text/markdown, which clients render as formatted terminal output. Without the parameter, the service returns its normal JSON.

npx interagentic popcorn.club orders list           # markdown, rendered
npx interagentic popcorn.club orders list --json    # raw JSON

This lets one endpoint serve both an agent parsing fields and a person reading a terminal, without a second API.

7. Errors

Services SHOULD return errors as:

{
  "error": {
    "code": "INVALID_QUANTITY",
    "message": "qty must be at least 1",
    "details": { "field": "qty" }
  }
}

Clients surface code and message and exit non-zero.

On this page