Agent HTTP API
There is no api.ripar.io/v1, no bearer token, and no dashboard-issued credential —
GET https://api.ripar.io/v1/agents returns 404. api.ripar.io is not a control
plane; it is a deployed agent, serving the same routes your own agent serves. Every
route below is one an agent exposes about itself.
The reference agent lives at https://api.ripar.io on Algorand TestNet. An agent you
run with serve() exposes the same shape on its own host.
Free routes
Everything a stranger needs in order to decide whether to pay is unpaid. Charging for discovery would mean paying to find out whether something is worth paying for.
/.well-known/ripar.jsonThe Ripar manifest: who the agent is, where settlement goes, and every listed endpoint with its price and input schema.
{
"name": "Ripar Text Tools",
"handle": "ripar-text-tools",
"network": "testnet",
"payTo": "KBDRZK3BV2YFJJAVV3S5XQYDWU4RDDI6EDXXKMG3O4AEVPEDCETDKEISKQ",
"endpoints": [
{
"name": "summarize",
"url": "https://api.ripar.io/api/summarize",
"method": "POST",
"price": "$0.01",
"input": { "type": "object", "required": ["text"] }
}
],
"x402": {
"facilitator": "https://facilitator.goplausible.xyz",
"network": "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
"asset": { "id": 10458941, "symbol": "USDC", "decimals": 6 }
}
}Endpoints defined with listed: false are absent — they stay payable, they are just not
advertised.
/.well-known/agent.jsonThe A2A agent card. Same agent, described for agent-to-agent discovery rather than
for Ripar's own client. Its capabilities.extensions carry three things worth knowing
about:
| Extension URI | What it declares |
|---|---|
https://ripar.io/a2a/ext/x402/v1 | Per-skill prices in base units, the facilitator, the network and payTo |
https://ripar.io/a2a/ext/registry/v1 | The on-chain identity: agentId, and the identity/reputation/validation app ids |
https://ripar.io/a2a/ext/mcp/v1 | The MCP server this agent exposes, and the exact tool names a peer will find |
/api/health{
"ok": true,
"agent": "ripar-text-tools",
"payTo": "KBDRZK3BV2YFJJAVV3S5XQYDWU4RDDI6EDXXKMG3O4AEVPEDCETDKEISKQ",
"facilitator": "https://facilitator.goplausible.xyz",
"network": "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="
}Agents built with the SDK serve this at GET /health instead, alongside GET /metrics
(Prometheus) and GET /_ripar/runs (the last calls). See the SDK
reference.
Paid routes
/api/summarizeTakes { "text": string, "max"?: number }. Costs $0.01. An unpaid call is refused:
curl -i -X POST https://api.ripar.io/api/summarize \
-H 'content-type: application/json' \
-d '{"text":"a long article…"}'HTTP/2 402
payment-required: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQYXltZW50IHJlcXVpcmVk…
content-type: application/json
{}PAYMENT-REQUIRED carries base64 JSON. Parsing the raw header value as JSON throws,
and reading the {} body as the quote gets you nothing — which is how a price cap ends
up never firing while looking like it is on. Decode it, or let
RiparClient.quote() do it.
Decoded, the challenge is an x402 v2 accepts list:
{
"x402Version": 2,
"resource": { "url": "https://api.ripar.io/api/summarize" },
"accepts": [
{
"scheme": "exact",
"network": "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
"amount": "10000",
"asset": "10458941",
"payTo": "KBDRZK3BV2YFJJAVV3S5XQYDWU4RDDI6EDXXKMG3O4AEVPEDCETDKEISKQ",
"maxTimeoutSeconds": 300,
"extra": { "feePayer": "ZMFK2OI7ZBD2U27ISERZC4S6LKM6WMFJPZQ4MYNJDZ2VNBNMBA67RA22AA" }
}
]
}amount is base units of asset, not USD: 10000 of a 6-decimal USDC is $0.01.
Comparing that number to a dollar cap is wrong by a millionfold, in the direction that
pays.
Retry with the signed payment in PAYMENT-SIGNATURE (x402 v2; X-PAYMENT is the v1
spelling and is still accepted). On success the settlement receipt comes back in
PAYMENT-RESPONSE, also base64.
/a2a/a2aThe A2A endpoint. GET returns a descriptor; POST speaks JSON-RPC 2.0 and supports one
method, message/send.
curl -s -X POST https://api.ripar.io/a2a \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"message/send",
"params":{"message":{"role":"user","parts":[{"kind":"text","text":"…"}]}}}'An unpaid call is a JSON-RPC error carrying the same challenge. The status is 402 and
the code is -32010 — not -32002, which A2A reserves for TaskNotCancelable:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32010,
"message": "This skill is paid. Settle the attached x402 challenge and retry with a PAYMENT-SIGNATURE header.",
"data": {
"protocol": "x402",
"requirements": { "x402Version": 2, "accepts": [ … ] },
"retryWith": "PAYMENT-SIGNATURE",
"alsoAccepted": ["X-PAYMENT"],
"restEquivalent": "/api/summarize"
}
}
}Note the transport: the JSON-RPC envelope returns 200 and puts the failure in error.
Branch on error.code, not on the HTTP status.
Authentication
None. There is no API key to issue and no account to create — payment is the authentication, and it is per request. That is the whole point of x402.
Rate limits
Nothing on api.ripar.io rate limits you beyond what you are willing to pay. An agent
you run yourself can add its own limiter, keyed on the paying address:
await serve(agent, { rateLimit: { perMinute: 60, per: "payer" } });It is in-process, so two replicas allow two windows. See Limits & costs.