Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

POST /explain/async

Non-blocking variant of POST /explain. Returns a jobId immediately. Poll GET /result/:jobId for the result.

URL: https://mpp.oculr.xyz/explain/async

Auth: MPP/x402, metered to the same total as sync /explain and collected across the job lifecycle (pricing): $0.01 is charged at submit, each GET /result/:jobId poll collects what the analysis has accrued since the previous poll, and the first poll after the job finishes charges the true-up. Handled transparently by the standard mppx polyfill; plain fetch() works for the submit, but the polls need a paying client.

Request

Body

Same as POST /explain:

FieldTypeRequiredDescription
txHashstringyesEVM tx hash matching ^0x[0-9a-fA-F]{64}$.
chainIdnumbernoEIP-155 chain ID. When provided, skips multi-chain auto-detection.
contextstringnoCaller intent passed to the analysis agent.
modelstringnoclaude-opus-5 | claude-opus-4-8 | claude-opus-4-7 | claude-sonnet-4-6 | claude-haiku-4-5-20251001. Defaults to claude-opus-5.
reportbooleannoIf true, the completed job also carries a self-contained HTML report.

Headers

HeaderValue
Content-Typeapplication/json

Response

202 Accepted

{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending"
}
FieldTypeNotes
jobIdUUIDPass this to GET /result/:jobId.
status"pending"Always pending on creation. Becomes running then complete (or error) over time.

Jobs expire 1 hour after they're created. After expiry, polling /result/:jobId returns 404.

Examples

TypeScript

const { jobId } = await fetch('https://mpp.oculr.xyz/explain/async', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ txHash: '0x…' }),
}).then(r => r.json())

Shell (mppx CLI)

JOB=$(mppx https://mpp.oculr.xyz/explain/async \
  -J '{"txHash":"0x4e4b8ed4…"}' | jq -r '.jobId')
echo "Job: $JOB"

Errors

CodeWhen it happensBody
400txHash is missing or malformed.{ "error": "txHash must be a valid 32-byte hex hash (0x...)" }
402No active MPP payment session. mppx handles this transparently.Standard MPP/x402 challenge.
500An internal error before the job was created. Generic body, no code field - the requestId is the handle to quote to support.{ "error": "Internal server error", "requestId": "err-..." }
502Never returned by this route, on any deployment. Submit answers 202 before the analysis starts, and the application maps every uncaught error to 500. An upstream-payment failure lands on the job instead: GET /result/:jobId then answers 200 with errorCode: "upstream_payment_unavailable". A literal 502 exists only on the unmetered blocking-JSON POST /explain, i.e. a self-hosted PRECOG_DEV_MODE=true deployment.(no such response)

Because submit returns before the analysis runs, nothing that goes wrong during the analysis can change this endpoint's status code. Poll GET /result/:jobId and branch on job.status / job.errorCode. See POST /explainupstream_payment_unavailable handling for the full pattern.

Related