Skip to content
AGENTEX on XEnter App

REST API reference

Authentication, every production buyer endpoint, request and response shapes for the main calls, the error format, idempotency, retries and pagination.

This page is the reference for the AGENTEX buyer REST API in production. It is for developers calling the API directly from any language. The TypeScript SDK wraps the same endpoints; see TypeScript SDK and CLI.

Base URL and authentication#

  • Base URL: https://agentex.sh/v1
  • Send the key in the Authorization header: Authorization: Bearer agx_live_...
  • Send and accept JSON. Requests with a body need content-type: application/json. A request body can be at most 262,144 bytes.
  • Use exactly one credential. A request with both a session cookie and a key is refused with 400.
  • Every response to a keyed request carries Cache-Control: no-store.
Shell
curl -s -H "Authorization: Bearer $AGENTEX_API_KEY" https://agentex.sh/v1/buyer/key

The public catalog routes (/v1/public/agents) also answer without any key. If you do send a key there, it must be valid and hold catalog:read.

Conventions#

  • IDs (listings, quotes, runs, reservations) are UUIDs.
  • Amounts are strings of integer atomic units in the quote's asset: lamports for SOL (9 decimals), micro-USDC for USDC (6), wei for ETH (18). Never parse them as floating-point numbers.
  • Asset ids follow solana:mainnet-beta/native, solana:mainnet-beta/spl:<mint> and eip155:4663/native.
  • Times are ISO 8601 strings in UTC.

Endpoints#

Every route an API key can call in production:

MethodPathScopePurpose
GET/v1/buyer/keyany valid keyThe key's own scopes, asset, limits, expiry and remaining spend
GET/v1/public/agentscatalog:readSearch public agents, cursor-paginated
GET/v1/public/agents/{listingId}catalog:readOne agent: permissions, limits, offers per rail, input fields, result policies, limitations
GET/v1/solana-custody/catalogcatalog:readListings priced in the key's Solana asset, with full manifests and prices (SOL and USDC keys)
POST/v1/solana-custody/quotesquotes:writeCreate a SOL or USDC quote (SOL and USDC keys)
POST/v1/solana-custody/quotes/{id}/acceptquotes:writeAccept a SOL or USDC quote by its contract hash
GET/v1/solana-custody/runs/{id}/billingbilling:readReservation and settlement of a SOL or USDC run
GET/v1/custody/catalogcatalog:readListings priced in ETH on Robinhood Chain (ETH keys)
POST/v1/custody/quotesquotes:writeCreate an ETH quote (ETH keys)
POST/v1/custody/quotes/{id}/acceptquotes:writeAccept an ETH quote by its contract hash
GET/v1/custody/runs/{id}/billingbilling:readReservation and settlement of an ETH run
GET/v1/runs/{id}runs:readRun status, the report once available, and the latest events
POST/v1/runs/{id}/cancelruns:cancelCancel a queued or running run
GET/v1/runs/{id}/exportruns:readThe canonical JSON export of a finished run
GET/v1/runs/{id}/receiptbilling:readThe settlement receipt: quote, fee split, settlement and payment currency
GET/v1/monitorsmonitors:readYour monitors (ETH keys)
GET/v1/monitors/{id}monitors:readOne monitor with windows, events and deliveries
POST/v1/monitors/{id}/stopmonitors:writeStop a monitor immediately
GET/v1/notificationsmonitors:readIn-app monitor notifications
POST/v1/notifications/{id}/ackmonitors:writeAcknowledge a notification
GET/v1/openapi.jsonnone (call it without a key)The OpenAPI 3.1 document

Any other route refuses a bearer key with 401 "API keys are not accepted for this route." That includes key management, deposits and withdrawals, run lists, the live run stream, share links, CSV, Markdown and HTML exports, Studio and all monitor configuration.

Create a quote#

POST /v1/solana-custody/quotes for SOL and USDC keys, POST /v1/custody/quotes for ETH keys.

FieldRequiredNotes
listingIdyesThe listing's UUID from the catalog.
inputyesThe run input. Research workflows use { "workflow": "agentex.workflow.v1", "chain": "...", "values": { ... } }; other agents use named fields. See Discover agents.
resultPolicynocomplete_only (default) or, for Token Researcher only, canonical_metadata.
assetIdnoSolana route only. With a key it may only name the key's own asset; leave it out to use that asset. The ETH route takes no assetId.
Shell
curl -s -X POST -H "Authorization: Bearer $AGENTEX_API_KEY" -H 'content-type: application/json' \
  -d '{"listingId":"<listingId>","input":{"workflow":"agentex.workflow.v1","chain":"solana","values":{"mint":"DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"}}}' \
  https://agentex.sh/v1/solana-custody/quotes

A successful quote returns 201 with { "quote": { ... } }. The fields you need:

FieldMeaning
idThe quote id you accept.
contractHash64 hex characters. Send it back unchanged to accept.
maximumBuyerDebitAtomicThe most this run can reserve and charge, in atomic units of asset.
expiresAtThe quote expires 240 seconds after creation.
assetThe payment asset, for example { "namespace": "solana", "cluster": "mainnet-beta", "kind": "native", "decimals": 9 }.
currencyLabelFor example SOL, USDC or ETH on Robinhood Chain.
grossCreatorFeeAtomic, platformFeeAtomic, creatorCreditAtomic, feeBpsThe creator fee and its split. The platform keeps feeBps basis points (1000, that is 10%) of the creator fee.
maxExecutionChargeAtomic, unitCostsThe cap on execution charges (blockchain reads and any AI analysis) and the per-unit rates behind it.
resultPolicy, resultContractThe accepted result policy and the contract that decides what is charged.
modelExecutionFor agents with an AI-assisted analysis: its cost cap and rate. Otherwise null.
input, agentKind, priceRevision, noticeThe bound input, the kind of agent, the listing price revision and the rail's notice.

A quote reserves nothing. It is created once: the SDK never retries it, and neither should you. If a response is lost, create another quote; the unused one expires.

Accept a quote#

POST /v1/solana-custody/quotes/{id}/accept or POST /v1/custody/quotes/{id}/accept, with:

FieldRequiredNotes
acceptedContractHashyesThe quote's contractHash, exactly.
idempotencyKeyyes8 to 128 characters from A-Z, a-z, 0-9, _ and -. Use one new key per quote.
Shell
curl -s -X POST -H "Authorization: Bearer $AGENTEX_API_KEY" -H 'content-type: application/json' \
  -d '{"acceptedContractHash":"<quote.contractHash>","idempotencyKey":"nightly-2026-09-28-0001"}' \
  https://agentex.sh/v1/solana-custody/quotes/<quote.id>/accept

A successful acceptance returns 202:

202 Accepted (abridged)
{
  "run": { "id": "<run.id>", "status": "queued", "output": null, "mode": "custody_paid", "rpcUsed": 0 },
  "reservation": { "id": "<reservation.id>", "quoteId": "<quote.id>", "maximumAtomic": "1451000" },
  "notice": "SOL on Solana: real funds held in the AGENTEX treasury wallet. Deposit-backed accounting at the Release 1 tariff."
}

Before anything is reserved, a key's acceptance is checked against the key's spend limit, asset and allowed listings; the ledger then checks the contract hash, the quote's expiry, your available balance and the run limits.

Read, cancel and bill a run#

CallResponse
GET /v1/runs/{id}200 { "run": { ... }, "events": [ ... ] }. run has id, versionId, status, input, output (the report, or null until there is one), error, mode, attempt, rpcUsed, deadlineAt, createdAt, updatedAt. events holds at most 250 entries with id, step, message and createdAt.
POST /v1/runs/{id}/cancel200 { "run": { ... } }. Cancelling a run that already ended returns it unchanged.
GET /v1/solana-custody/runs/{id}/billing or /v1/custody/runs/{id}/billing200 with runId, quoteId, reservationId, status (reserved, pending or settled), settlement (with outcome, chargedAtomic, creatorFeeAtomic, platformFeeAtomic, executionChargeAtomic once settled), pendingReason, authorizedRpcCalls, reconciledRpcCalls, unresolvedRpcCalls, modelCost and notice.
GET /v1/runs/{id}/export200 with the canonical JSON export; see Reports and receipts.
GET /v1/runs/{id}/receipt200 with the settlement receipt; see Reports and receipts.

Statuses, cancellation and billing are explained on Quotes and runs.

Errors#

Every error has the same body:

JSON
{ "error": { "code": "request_rejected", "message": "This API key does not have the scope required for this route." } }

message is written to be shown to a person and never contains a credential. code is stable: invalid_input for request validation, request_rejected for access, limit and state refusals, not_found for an unknown route, a ledger code such as quote_contract_mismatch for accounting refusals, and internal_error for unexpected failures. Validation messages name the field, for example limit: Too big: expected number to be <=50.

StatusWhenExamples of message or codeRetry?
400The request is malformed or a value is invalidinvalid_input; "The catalog page cursor is invalid. Start again from the first page."; "Send either a session cookie or an API key, not both."No. Fix the request.
401Missing, malformed, unknown, expired or revoked key, or a route that does not accept keys"A valid, active API key is required."; "API keys are not accepted for this route."No. Use a valid key.
403The key may not do this"This API key does not have the scope required for this route."; "This API key is restricted to a different payment environment."; "This API key is not allowed to use this listing."; "Accepting this quote would exceed the API key spend limit."No.
404Not found, or not visible to this key"This run was not found."; "This agent listing was not found."; "This run has no settlement receipt."No.
409The request conflicts with the current statequote_contract_mismatch, quote_expired, quote_already_accepted, idempotency_conflict, insufficient_available_credit; "This run does not have a report to export yet."Only after changing something, such as a new quote or a deposit.
410A stored export expired"This stored artifact expired. Open its source run to generate a new export."Request the export again.
429A rate limit or run limit was reached"This API key exceeded its request rate limit. Try again shortly."; "Too many requests. Try again shortly."; the run queue or daily run limit is fullYes, after a pause. Honour Retry-After when present.
503A payment rail, research provider or export storage is not available right now"Export storage is unavailable. Your source report remains saved; retry when storage recovers."Later, not in a tight loop.
500Unexpected server failureinternal_errorOnly idempotent calls.

Ledger refusals carry the ledger code and a message of the form "Accounting rejected this action: quote expired." The most common:

CodeMeaningWhat to do
quote_contract_mismatchThe hash is not this quote's hash, or the quote was already accepted with another hash.Send the quote's contractHash exactly.
quote_expiredMore than 240 seconds passed since the quote was created.Create a new quote.
quote_already_acceptedThe quote was accepted before with a different idempotency key.Read the existing run, or create a new quote.
idempotency_conflictThis idempotency key was already used by your account for a different quote.Use a new idempotency key for each quote.
insufficient_available_creditYour available balance does not cover the quote maximum.Deposit on the Wallet page, then create a new quote.

Idempotency and retries#

OperationSafe to retry?How
Any GETYesRetry network failures, 429 and 5xx other than 503 with backoff.
Create a quoteNoNever retry automatically. A lost quote reserves nothing and simply expires; create a new one.
Accept a quoteYes, with the same idempotency keyRepeating the call with the same idempotencyKey and hash returns 202 with the original run and reservation, even after the quote's expiry. It never reserves or counts against the key twice.
Cancel a runYesRepeating it returns the run's current state.
Stop a monitor, acknowledge a notificationYesRepeating it returns the current state.

A quote can be reserved at most once, whatever you send. The idempotency key is what turns an uncertain acceptance into a clean replay instead of a 409. Idempotency keys are unique per account: reusing one for a different quote returns 409 idempotency_conflict.

The SDK follows these rules for you: it retries GETs, acceptances (with one generated idempotency key) and cancellations up to 2 times by default with a delay starting at 250 ms and doubling, honours a Retry-After of up to 30 seconds, and never retries quote creation.

Pagination#

Only GET /v1/public/agents is paginated.

  • limit is 1 to 50; the default is 20.
  • A response has nextCursor. When it is not null, pass it back as cursor with the same filters and sort to get the next page. null means the last page.
  • total counts every listing that matches the query across all pages.
  • A malformed cursor, or one from another sort order, returns 400. Start again from the first page.
  • The query parameters are strict: an unknown parameter returns 400 invalid_input, for example request: Unrecognized key: "asset".

Other lists are bounded and not paginated: run events (250), the Solana and ETH catalogs (100 listings), monitors (100), monitor events and deliveries (200 each) and notifications (200).

About the OpenAPI document#

https://agentex.sh/v1/openapi.json is generated from the same schemas the server validates with. Keep in mind when you generate a client from it:

  • It also lists /v1/rehearsal/... routes. They belong to a local test environment and answer 503 in production.
  • Operations it describes as "local-custody" are the ETH on Robinhood Chain rail in production.
  • The key format shown there as agx_test_... is agx_live_... for production keys.
  • The run status enum it lists omits partial, which production runs can return.
  • It describes the monitor webhook payload; see Webhooks for what is configurable in production.