Skip to main content

Intent API

The Intent API parses a free-form swap request into a structured ParsedIntent that the Ophis frontend, or your own app or agent, can use to pre-fill a swap form or construct a deep link.

It is a text parser, separate from the MCP server, compatibility API and rebate/reward APIs. To place orders programmatically you use the standard CoW Protocol orderbook API per chain (see AI agent integration). The Intent API itself does not place, sign, or execute trades; order signing always happens in the user's wallet.

  • Base URL: https://ophis.fi
  • Endpoint: POST /api/intent
  • Auth: none, no API key required.
  • Backed by: LibertAI Qwen 3.6 27B (open-weights, on Aleph Cloud), behind a Cloudflare Pages Function proxy. The LibertAI key is held server-side and never reaches callers.
  • Machine-readable spec: openapi.json

Access control​

ControlBehaviour
Origin allowlistRequests with a non-null Origin header are checked against https://ophis.fi and https://swap.ophis.fi. Browser calls from other origins get 403 FORBIDDEN.
Non-browser callerscurl and server-side scripts that omit Origin entirely are allowed, subject to the rate limit. This is the path agents use.
Rate limit30 requests per IP per rolling 60-second window. Exceeding returns 429 with a Retry-After header.
CachingIdentical normalized text (lowercased + trimmed) within an origin bucket is served from a 5-minute edge cache. Cache hits return the header x-ophis-cache: hit and don't consume an upstream model call. The rate-limit counter still increments on a cache hit.
Origin is a speed bump, not a security boundary

The Origin check raises the bar for casual browser abuse but can be omitted by any non-browser client. The real protections are the per-IP rate limit and the fact that the endpoint only normalizes text, it moves no funds.

Request​

POST /api/intent with a JSON body:

{ "text": "swap 100 USDC for ETH on Base" }
FieldTypeConstraints
textstringRequired. 1–280 characters. The swap request in your own words. Case-insensitive.

The model uses temperature: 0 to reduce variation. This is not a guarantee of identical outputs across model runs or upgrades; validate every response.

Example​

curl -sS https://ophis.fi/api/intent \
-H 'content-type: application/json' \
-d '{"text":"swap 100 USDC for ETH on Base"}'

Response​

On success, 200 OK with a ParsedIntent:

{
"ok": true,
"data": {
"intent": "swap",
"entities": [
{ "type": "amount", "value": "100", "raw": "100", "start": 5, "end": 8 },
{ "type": "sellToken", "value": "USDC", "raw": "USDC", "start": 9, "end": 13 },
{ "type": "buyToken", "value": "ETH", "raw": "ETH", "start": 18, "end": 21 },
{ "type": "chain", "value": "base", "raw": "Base", "start": 25, "end": 29 }
]
}
}

ParsedIntent​

FieldTypeDescription
intent"swap" | "unknown"unknown (with an empty entities array) if the text isn't a swap request.
entitiesEntity[]The recognised entities, in any order.

Entity​

FieldTypeDescription
type"sellToken" | "buyToken" | "amount" | "chain"The kind of entity. sellToken is what you pay with; buyToken is what you want.
valuestringCanonical form (e.g. USDC, 0.5, optimism).
rawstringA case-insensitive match somewhere in the input.
startintegerModel-proposed, in-bounds start offset (inclusive); may be inaccurate.
endintegerModel-proposed, in-bounds end offset (exclusive); re-anchor raw before highlighting.

Token values use bounded symbol syntax: 2–12 letters/digits, at least one letter, excluding common non-token words. Values must derive from raw, which must occur in the input; exact offsets are not guaranteed. This is not contract verification; resolve the chain/address and request a quote before building an order.

Chain values are the parser's supported lowercase slugs. This set is separate from the app's network selector: arc is recognized, but the app blocks an explicit chain disabled in its deployment.

ethereum arbitrum arc avalanche base bnb gnosis
ink linea optimism plasma polygon robinhood unichain

Errors​

Errors return { "ok": false, "error": { "code", "message" } }:

StatuscodeWhen
400BAD_INPUTMissing/empty text, text over 280 chars, or an invalid JSON body.
403FORBIDDENThe Origin header is present but not on the allowlist.
429RATE_LIMITEDMore than 30 requests in 60s from your IP. Honour Retry-After.
500UPSTREAMOperator configuration error (the model key is unset).
502UPSTREAM / INVALID_JSONThe upstream parser was unreachable, returned a non-2xx, or produced output that failed schema validation.
504TIMEOUTThe upstream parser didn't respond within 5 seconds.

The full set of error codes is TIMEOUT | UPSTREAM | INVALID_JSON | BAD_INPUT | RATE_LIMITED | FORBIDDEN.

{ "ok": false, "error": { "code": "RATE_LIMITED", "message": "too many requests" } }

Response headers​

Every response sets cache-control: no-store, x-content-type-options: nosniff, x-frame-options: DENY, and referrer-policy: no-referrer. A cache hit additionally sets x-ophis-cache: hit.

Trust model​

Ophis is non-custodial. This endpoint does not place, sign, or execute trades, it only normalizes natural language into structured entities. Order signing always happens in the user's wallet on the frontend. To submit orders programmatically, use the CoW Protocol orderbook API directly against the relevant orderbook host.