Consumer quickstart

Concat’s inference endpoint accepts standard chat-completions requests and requires an x402 payment. Any OpenAI-compatible client plus an x402 payment handler will work — no Concat-specific library needed.

Endpoint

POST https://concat.network/v1/chat/completions/:agentId

Requests are routed directly to the supplier identified by :agentId (their ERC-8004 agent ID). At launch, every request must specify a supplier — omitting the agent ID returns 400 agent_id_required. Auto-routing across the supplier pool will come later.

Browse active suppliers and copy agent IDs from the dashboard, or list them programmatically with GET /v1/suppliers.

Request and response bodies follow the OpenAI chat-completions schema. Drop the URL into any library or tool you already use for LLM inference.

Payment

Every request must carry an x402 payment. The flow is the standard x402 challenge/response:

  1. POST the request. The server replies 402 Payment Required with a Permit2 EIP-712 payload.
  2. Sign the payload with your wallet.
  3. Retry the request with the signed payment in the X-PAYMENT header. The server settles on-chain and returns the completion.

Full spec, reference clients, and example implementations are at x402.org. Any x402-aware client works with Concat out of the box.

Channel cache and deposits

For launch traffic, configure the batch-settlement client with persistent channel storage. The installed x402 client defaults to in-memory storage and a deposit multiplier of 5; Concat’s recommended production shape is persistent storage plus depositMultiplier: 10.

import {
  BatchSettlementEvmScheme,
  FileClientChannelStorage,
} from '@x402/evm/batch-settlement/client';

const scheme = new BatchSettlementEvmScheme(signer, {
  storage: new FileClientChannelStorage({
    directory: './.x402-channels/base-sepolia',
  }),
  depositPolicy: { depositMultiplier: 10 },
  rpcUrl: process.env.BASE_RPC_URL,
});

Keep the channel cache stable per wallet, network, and environment. Use in-memory storage only in tests or scripts where a fresh channel is acceptable. The x402 library requires configured multipliers to be integers of at least 3; use 3 only when intentionally forcing fresh/top-up behavior during testing.

Example

Initial request (no payment):

curl -X POST https://concat.network/v1/chat/completions/42 \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role": "user", "content": "Hello"}]
  }'

Response: 402 Payment Required with a Permit2 payload in the body describing the amount, token, and domain to sign. Sign it (see x402.org for how to produce the X-PAYMENT header), then retry:

curl -X POST https://concat.network/v1/chat/completions/42 \
  -H "Content-Type: application/json" \
  -H "X-PAYMENT: <signed payload>" \
  -d '{
    "messages": [{"role": "user", "content": "Hello"}]
  }'

Supported request fields

The server validates request bodies strictly — unknown fields are rejected with a 400. Accepted fields:

FieldNotes
messagesRequired. System, user, assistant, tool roles. User content may be a string or an array of text / image_url parts.
temperatureOptional.
toolsOptional. Function tool definitions.
tool_choiceOptional. none, auto, or required.
streamNot yet supported — requests with stream: true return 400.

Other OpenAI fields (top_p, max_tokens, presence_penalty, n, etc.) are not passed through today. Request body size is capped at 2 MB.

Responses follow the OpenAI chat-completions schema: id, object, created, model, choices, and usage.

Errors

Errors are returned as standard OpenAI error envelopes:

{
  "error": {
    "message": "...",
    "type": "invalid_request_error" | "server_error",
    "code": "..."
  }
}
StatusCodeMeaning
400Request body failed schema validation.
400agent_id_requiredRequest was sent to /v1/chat/completions without an agent ID.
400streaming_not_supportedstream: true is not yet supported.
402Payment required; body contains the Permit2 payload to sign.
404agent_not_foundThe requested agent ID is not currently connected.
503queue_timeoutThe targeted supplier didn’t become available within 2 minutes.
504request_timeoutSupplier accepted the request but didn’t respond within 2 minutes.

Queue behavior

Each supplier handles one request at a time. If the supplier you’re targeting is busy, your request queues behind their current work until they become idle.

Both the queue wait and the active request have a default 2-minute timeout. A request that waits longer returns queue_timeout; one that stalls after dispatch returns request_timeout.