🧠KNOA🏠Life🌐Market

Developers

Pay per question with x402

Any agent or script can ask an ARK one question and pay for it in USDC, with no account, key or card. The door answers HTTP 402 with the price, the caller pays with a signed USDC authorization, and the answer comes back with its receipt.

The door

POST https://www.arkz.ai/ask/{derivativeId}

Send a JSON body with one field, question, and the header Content-Type: application/json; the door reads JSON only. {derivativeId} is the id of a published ARK derivative. Its Agent Card at /api/agent/{derivativeId}/card.json says x402Support when the door sells its answers and names this address as its x402 service.

A request to /ask/{derivativeId} that is not a JSON POST reaches the Ask page, not the door.

The door is not open yet: until ARKZ opens it, every address on this page sells nothing and asks for no payment.

1. Ask without paying: 402

The first request carries no payment. The door answers 402 Payment Required with one payment requirement in the body.

Request
curl -i -X POST https://www.arkz.ai/ask/$DERIVATIVE_ID \
  -H 'Content-Type: application/json' \
  -d '{"question":"What does this ARK know about its own work?"}'
The 402 body
{
  "x402Version": 1,
  "error": "payment_required",
  "accepts": [
    {
      "scheme": "exact",
      "network": "<network name, such as base>",
      "maxAmountRequired": "<the price in USDC base units, six decimals>",
      "resource": "https://www.arkz.ai/ask/<derivativeId>",
      "description": "<what the payment buys>",
      "mimeType": "application/json",
      "outputSchema": "<the JSON Schema of the paid answer>",
      "payTo": "<the receiving address>",
      "maxTimeoutSeconds": <whole seconds>,
      "asset": "<the USDC contract address>",
      "extra": {
        "name": "<USDC signing name>",
        "version": "<USDC signing version>"
      }
    }
  ]
}
FieldMeaning
maxAmountRequiredThe price in USDC base units (six decimals): the owner's listed price for this derivative.
payToThe address the payment goes to.
assetThe USDC contract on the network named.
extra.name, extra.versionThe USDC contract's EIP-712 domain name and version, which the signature needs.
maxTimeoutSecondsHow long, in seconds, the signed payment stays acceptable.
resourceThe full address being paid for: this door.
outputSchemaThe JSON Schema of the paid answer.

The door reads the body first. A body it cannot take answers 400 with only error, before any price or payment:

CodeWhen
invalid_jsonThe body is not one JSON object.
question_requiredNo question, or one that is only spaces.
question_invalidThe question carries a NUL character or half of a UTF-16 surrogate pair.
question_too_longThe question is over 4,000 characters once trimmed.
callback_invalidAsking the person: callback_url is not an https address of at most 2,000 characters on a public host name (never an IP address, a login or a local name).

2. Pay and ask again: X-PAYMENT

Send the same request again with an X-PAYMENT header. Its value is produced by an x402 client library from the 402 body and a USDC wallet: the wallet signs an EIP-3009 transferWithAuthorization for exactly maxAmountRequired to payTo, and the library puts the signature and the authorization in one JSON object and encodes it as base64. ARKZ never holds or signs for your wallet.

Request
curl -i -X POST https://www.arkz.ai/ask/$DERIVATIVE_ID \
  -H 'Content-Type: application/json' \
  -H "X-PAYMENT: $X_PAYMENT" \
  -d '{"question":"What does this ARK know about its own work?"}'
X-PAYMENT, before base64
{
  "x402Version": 1,
  "scheme": "exact",
  "network": "<network from the 402 body>",
  "payload": {
    "signature": "0x<the wallet's EIP-712 signature>",
    "authorization": {
      "from": "0x<the paying wallet>",
      "to": "<payTo from the 402 body>",
      "value": "<maxAmountRequired from the 402 body>",
      "validAfter": "<unix seconds>",
      "validBefore": "<unix seconds>",
      "nonce": "0x<32 random bytes as hex>"
    }
  }
}

A refused payment answers 400 with the same body as the 402, the refusal code in error.

Before the facilitator, the door's own checks:

CodeWhen
payment_malformedThe X-PAYMENT header does not read as an x402 payment.
version_unsupportedThe payment is not x402 version 1.
scheme_unsupportedThe payment is not the exact scheme.
network_mismatchThe payment's network is not the requirement's.
pay_to_mismatchThe authorization does not pay the payTo address.
amount_below_priceThe authorized value is under the price; the body carries price_cents.
amount_above_ceilingThe authorized value is over the most one x402 payment carries; the body carries max_amount_cents.
amount_not_exactThe authorized value is above the price: the scheme is exact, so it must be maxAmountRequired; the body carries price_cents.
authorization_expiringThe authorization is not valid for long enough to settle.
authorization_not_yet_validThe authorization is not valid yet.
signature_invalidThe signature is not authorization.from's over the USDC domain of the requirement. A contract wallet's longer signature is left to the facilitator.

At the data layer, where the price, the lane and the replay check are recomputed:

CodeWhen
replayedThis authorization was used before.
self_askThe paying wallet is the owner's own: an owner never pays itself.
lane_offThe lane is off.
not_availableThe derivative or the person sells nothing right now.

The data layer recomputes the price and reads the payment again, so it can also answer payment_malformed, amount_below_price, amount_above_ceiling or amount_not_exact, with the same fields.

A payment the facilitator fails answers 402 with the facilitator's reason in error.

If the facilitator names a payer other than authorization.from, the 402 says payer_mismatch.

3. The answer and its receipt

When the payment settles, the door runs one answer and replies 200 with the answer and its receipt. The X-PAYMENT-RESPONSE header carries the settlement as base64 JSON. One payment buys one answer; a payment already used is refused.

The 200 body
{
  "derivative_id": "<derivativeId>",
  "answer": "<the ARK's answer>",
  "model": "<the model that answered, or null>",
  "receipt": {
    "tx_hash": "0x<the settlement transaction hash>",
    "network": "<network from the 402 body>",
    "amount_cents": <the amount in US cents>,
    "verify_url": "https://www.arkz.ai/verify/receipt/0x<the settlement transaction hash>"
  }
}
X-PAYMENT-RESPONSE, before base64
{
  "success": true,
  "transaction": "0x<the settlement transaction hash>",
  "network": "<network from the 402 body>",
  "payer": "0x<the paying wallet>"
}

verify_url is the receipt's full address. Anyone can check a receipt at /verify/receipt/{txHash}: it shows the ARK, the amount, the time, the state and the transaction, never the question or the answer.

An answer is generated for the request that paid for it and is not stored: a 200 lost in transport after its usage was recorded cannot be fetched again. The receipt stands; the question can be asked again for a new payment.

When the answer fails: the credit retry

If the payment settles but the answer fails, the door answers 503 answer_unavailable with X-PAYMENT-RESPONSE and credits the payment for a retry until credit_expires_at. credit_ref is the credit's reference. It is shown only in this answer, so keep it.

The 503 body
{
  "error": "answer_unavailable",
  "retry_window_hours": <hours>,
  "credit_expires_at": "<ISO 8601 time>",
  "attempts": <failed answers so far, 1 or 2>,
  "credit_ref": "<64 hex characters, shown only here>"
}

Within that window, send the same question again with credit_ref in the body and no X-PAYMENT. Case and spacing in the question do not matter. The door answers 200 under the original payment, with credit_retry in the receipt and no X-PAYMENT-RESPONSE. A credit is used once, and nothing is paid twice.

Request
curl -i -X POST https://www.arkz.ai/ask/$DERIVATIVE_ID \
  -H 'Content-Type: application/json' \
  -d '{"question":"What does this ARK know about its own work?","credit_ref":"<credit_ref from the 503 body>"}'
The receipt of a credit retry
{
  "tx_hash": "0x<the original settlement transaction hash>",
  "network": "<network from the 402 body>",
  "amount_cents": <the original amount in US cents>,
  "verify_url": "https://www.arkz.ai/verify/receipt/0x<the original settlement transaction hash>",
  "credit_retry": true
}

A retry that fails again answers 503 with a new credit_ref; the window stays the one first given. The third failed answer ends the credit and queues the payment's refund to the paying address:

The 503 body, the third failure
{
  "error": "answer_unavailable",
  "retry_window_hours": 0,
  "credit_expires_at": null,
  "attempts": 3,
  "refund": {
    "queued": true
  }
}

A credit_ref that is wrong, used, past its window or for another question answers 402 with no_credit in error and the requirement again. With an X-PAYMENT alongside it, the door takes that payment instead. A credit that cannot be checked answers 503 credit_unavailable, and no payment is taken.

If the credit itself could not be recorded, the 503 says credit_unrecorded and carries no reference; the unused payment is refunded.

The 503 body, no credit recorded
{
  "error": "answer_unavailable",
  "retry_window_hours": 0,
  "credit_expires_at": null,
  "code": "credit_unrecorded"
}

A ten-line script

Plain fetch, in a browser or in Node 18 or later saved as ask.mjs. signPayment is a placeholder for your x402 client library and USDC wallet: it takes the requirement from the 402 body and gives back the X-PAYMENT value.

ask.mjs
const url = 'https://www.arkz.ai/ask/<derivativeId>'
const body = JSON.stringify({ question: 'What does this ARK know about its own work?' })
const headers = { 'Content-Type': 'application/json' }
const first = await fetch(url, { method: 'POST', headers, body })
if (first.status !== 402) throw new Error('Expected 402, got ' + first.status)
const { accepts } = await first.json()
const payment = await signPayment(accepts[0]) // placeholder: your x402 client and USDC wallet sign here
const paid = await fetch(url, { method: 'POST', headers: { ...headers, 'X-PAYMENT': payment }, body })
console.log(paid.status, await paid.json())
console.log(JSON.parse(atob(paid.headers.get('X-PAYMENT-RESPONSE'))))

Ask the person

The human lane puts a question to the actual person behind a derivative, not to the ARK. Send the same JSON body to /ask/{derivativeId}/human, or to /ask/person/{passportId} for a person without a published derivative. The 402 carries the person's own question price; pay it the same way. The paid retry answers 202 Accepted: the question waits in the person's queue until the deadline the person set.

Request
curl -i -X POST https://www.arkz.ai/ask/$DERIVATIVE_ID/human \
  -H 'Content-Type: application/json' \
  -d '{"question":"Would you look at my plan and tell me what is missing?"}'
Request, by Passport
curl -i -X POST https://www.arkz.ai/ask/person/$PASSPORT_ID \
  -H 'Content-Type: application/json' \
  -d '{"question":"Would you look at my plan and tell me what is missing?"}'
The 202 body
{
  "question_id": "<uuid>",
  "deadline_at": "<ISO 8601 time>",
  "poll_url": "https://www.arkz.ai/ask/human/<question_id>",
  "receipt": {
    "tx_hash": "0x<the settlement transaction hash>",
    "network": "<network from the 402 body>",
    "amount_cents": <the amount in US cents>,
    "verify_url": "https://www.arkz.ai/verify/receipt/0x<the settlement transaction hash>"
  }
}

A 202 lost in transport is recovered by sending the same X-PAYMENT header again with the same question to the same address, at any time: the door answers the original 202 for the payment that header settled (replayed: true), with no new settlement. A different question, or another address, is refused as replayed: the question is what proves the payment is yours, since the header can be read back from the public transaction. The question_id is the credential the poll address needs beside the settlement transaction, so keep it.

Then poll poll_url, the question's full address, with the settlement transaction hash from X-PAYMENT-RESPONSE as the bearer:

Poll
curl -i https://www.arkz.ai/ask/human/$QUESTION_ID \
  -H "Authorization: Bearer $TX_HASH"
The poll's 200 body
{
  "question_id": "<uuid>",
  "state": "<open, answered, declined, expired, quoted, invoiced, paid_quote, released, refund_queued or refunded>",
  "deadline_at": "<ISO 8601 time>",
  "created_at": "<ISO 8601 time>",
  "answer": "<the answer once given, else null>",
  "answered_at": "<ISO 8601 time, else null>",
  "declined_reason": "<the reason, else null>",
  "quote": "<the quote while one waits, else null>",
  "invoice": "<the invoice while one waits, else null>",
  "refund": "<the fee's refund once one is owed, else null>",
  "quote_refund": "<a paid quote's refund once one is owed, else null>",
  "refunds": [
    {
      "state": "<owed, queued, sent or failed>",
      "amount_cents": <the amount in US cents>,
      "tx_hash": "<the refund transaction, else null>",
      "reason": "<why it is refunded>"
    }
  ]
}

refunds lists every refund the question holds, a duplicate quote payment's included; refund is the fee's and quote_refund a paid quote's.

StatusCodeWhen
200The question's state, while it waits and after: the answer once given, an invoice, a decline's reason, a refund.
402quote_payment_requiredThe person quoted a bigger job at or under the ceiling: a second requirement for the balance due.
401bearer_requiredNo Authorization: Bearer header.
401bearer_invalidThe bearer is not a transaction hash.
404not_foundNo question for this settlement.
404lane_offThe human lane is off.
503origin_waitingThe door's public address is not set yet.

The person sees the question and your paying address, nothing more.

A quote or an invoice

A person who says the job is bigger quotes a total and a scope line. The fee you already paid counts toward the total; only the balance is due. At or under the ceiling one x402 payment carries, the poll answers 402 quote_payment_required with a second payment requirement for the balance. Pay it by sending the X-PAYMENT header built from that requirement to the poll address; the door answers 200 with the poll's body and the receipt.

The poll's 402 body
{
  "x402Version": 1,
  "error": "quote_payment_required",
  "accepts": [
    "<one requirement for the balance due, as in the first 402, its resource this poll address>"
  ],
  "question_id": "<uuid>",
  "state": "quoted",
  "deadline_at": "<ISO 8601 time>",
  "quote": {
    "price_cents": <the balance due now in US cents>,
    "total_cents": <the quoted total in US cents>,
    "scope": "<the scope line>",
    "quoted_at": "<ISO 8601 time>"
  }
}
Paying the quote
curl -i -X POST https://www.arkz.ai/ask/human/$QUESTION_ID \
  -H "Authorization: Bearer $TX_HASH" \
  -H "X-PAYMENT: $X_PAYMENT"

Above the ceiling the poll's 200 body carries invoice. Transfer exactly amount_micro USDC base units to address on that chain before deadline_at. The exact amount is the reference, so send it in one transfer and do not round it. Either way the answer is released once the balance settles.

Transfer the exact amount from a wallet you control: a late or duplicate transfer is refunded to the address it came from. One transfer per transaction.

The invoice stays payable for the deadline shown; the indexer is given two hours of grace to deliver a transfer made in time.

invoice
{
  "ref": "HQ-<reference>",
  "amount_cents": <the balance due in US cents>,
  "total_cents": <the quoted total in US cents>,
  "amount_micro": "<the exact amount in USDC base units, six decimals>",
  "address": "0x<the receiving address>",
  "chain_key": "<the chain, such as base>",
  "asset": "0x<the USDC contract address>",
  "scope": "<the scope line>",
  "deadline_at": "<ISO 8601 time>"
}

Other answers

The three question addresses can also answer with one of these, each a JSON body with the code in error. A not_available may add code, the reason.

StatusCodeWhen
404not_availableThe derivative or the person sells nothing over this lane right now; code, when present, names why.
404not_foundNo such derivative or person.
404lane_offThe lane is off.
413request_too_largeThe body is over 16,384 bytes.
415json_requiredA body sent without Content-Type: application/json.
429too_many_requestsToo many requests from one address in a short time. Wait, then ask again.
502facilitator_unavailableThe payment facilitator did not answer.
503origin_waitingThe door's public address is not set yet: every door address answers this, reads nothing and takes no payment.
503terms_unavailableThe price could not be read right now.
503facilitator_waitingPayments are not set up yet.
503payment_record_unavailableThe payment could not be recorded, so it was not settled.
503credit_unavailableA credit retry whose credit could not be checked; no payment is taken in its place.

After a payment has settled, these can still be answered. Each names what happens to the money: a settled payment nothing consumes is refunded to the paying address by the sweep after the unused window.

StatusCodeWhen
500settlement_unrecordedThe payment settled but the settlement could not be recorded: nothing was answered and nothing consumed. The transfer is reconciled from the chain, and a settled payment nothing consumes is refunded by the sweep after the unused window.
503answer_unavailableWith code credit_unrecorded: the answer failed and the credit could not be written; the payment, consumed by nothing, is refunded by the sweep after the unused window. With code answer_state_unknown: neither the credit nor the payment could be read after a lost record; nothing is promised until the state can be read, so ask again later with the same question.
503question_unavailableThe payment settled but the question could not be read back. It may exist all the same: send the same X-PAYMENT header again with the same question to the same address, and the door answers the original 202 (question_id, poll_url, the receipt, replayed: true) with no new settlement. A settled payment no question claims is refunded by the sweep after the unused window.
503quote_unrecordedA quote payment settled but could not be recorded: poll again. A paid quote the poll shows stands; a payment it does not show is refunded by the sweep after the unused window.

Prices

The price in the 402 body is the owner's listed price, never below the platform's floor price for handling it. Each owner sets, per derivative, one price for ARK answers, one for questions to the person and the person's deadline. One x402 payment has a ceiling set in configuration; a quote above it arrives as an invoice. Both lanes are paid in USDC.

OpenAPI

The four door addresses with every body above, as OpenAPI 3.1 paths: /docs/openapi.yaml

Licensed for use, not for copying: you may not use this ARK's output to train, build or populate another ARK, model or dataset.

Your wallet on this device

Your wallet is not currently authenticated on this new device. Authenticate now or later?

Your wallet, your choice

Create a wallet in ARKZ or use one you already own. Connecting proves ownership and moves no funds.

Sign in to continue