Skip to content

Agent API

Call AssayDesk queries over HTTP. Paid queries use x402: the server answers 402 with a price, the client signs a USDC authorisation and retries.

The 402 flow

  1. POST the query with no payment header.
  2. Receive 402 with a base64 PAYMENT-REQUIRED header listing price, network, asset and payTo.
  3. Sign an EIP-3009 TransferWithAuthorization for that amount.
  4. Retry the same request with the base64 payload in PAYMENT-SIGNATURE.
  5. Receive 200 with the data and a base64 PAYMENT-RESPONSE settlement receipt.

If a payment verifies but the query fails, the payment is not settled and you are not charged. Each authorisation nonce can be used once; retrying an already-settled payment returns the data again without charging. Agent keys with prepaid credit skip x402 and are charged once per identical query.

Free query

curl -s -X POST https://assaydesk.app/v1/query/register_counts \
  -H 'content-type: application/json' -d '{}'

Paid query

# 1. Ask without paying: expect HTTP 402 and a PAYMENT-REQUIRED header
curl -si -X POST https://assaydesk.app/v1/query/mica_query_casp \
  -H 'content-type: application/json' -d '{"country":"DE","limit":5}' \
  | grep -i '^payment-required:' | cut -d' ' -f2 | tr -d '\r' | base64 -d

# 2. Or pay from prepaid credit with an agent key
curl -s -X POST https://assaydesk.app/v1/query/mica_query_casp \
  -H "authorization: Bearer $ASSAYDESK_KEY" \
  -H 'content-type: application/json' -d '{"country":"DE","limit":5}'

TypeScript agent (viem)

The agent wallet needs Base Sepolia test USDC. Keep its private key on the agent's machine.

import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as `0x${string}`);
const url = "https://assaydesk.app/v1/query/mica_query_casp";
const body = JSON.stringify({ country: "DE", limit: 5 });
const b64 = (v: unknown) => Buffer.from(JSON.stringify(v)).toString("base64");

const first = await fetch(url, { method: "POST", headers: { "content-type": "application/json" }, body });
if (first.status !== 402) throw new Error("expected 402");
const pr = JSON.parse(Buffer.from(first.headers.get("PAYMENT-REQUIRED")!, "base64").toString());
const req = pr.accepts[0];

const now = Math.floor(Date.now() / 1000);
const authorization = {
  from: account.address, to: req.payTo, value: req.amount,
  validAfter: String(now - 600), validBefore: String(now + req.maxTimeoutSeconds),
  nonce: `0x${crypto.getRandomValues(new Uint8Array(32)).reduce((s, b) => s + b.toString(16).padStart(2, "0"), "")}` as `0x${string}`,
};
const signature = await account.signTypedData({
  domain: { name: req.extra.name, version: req.extra.version, chainId: Number(req.network.split(":")[1]), verifyingContract: req.asset },
  types: { TransferWithAuthorization: [
    { name: "from", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" },
    { name: "validAfter", type: "uint256" }, { name: "validBefore", type: "uint256" }, { name: "nonce", type: "bytes32" } ] },
  primaryType: "TransferWithAuthorization",
  message: { ...authorization, value: BigInt(authorization.value), validAfter: BigInt(authorization.validAfter), validBefore: BigInt(authorization.validBefore) },
});

const paid = await fetch(url, {
  method: "POST", body,
  headers: { "content-type": "application/json", "PAYMENT-SIGNATURE": b64({ x402Version: 2, resource: pr.resource, accepted: req, payload: { signature, authorization } }) },
});
console.log(await paid.json(), Buffer.from(paid.headers.get("PAYMENT-RESPONSE")!, "base64").toString());

Discovery

curl -s https://assaydesk.app/.well-known/x402
curl -s https://assaydesk.app/api/public/pricing