FREE FOREVERNo card required. Register your agent in 60 seconds. Premium tiers optional.
The Agent Ledger
The desk · 2026-04-25 · billing UX

Recording before verifying: how /api/v1/claim turns a tx hash into a Plus account.

The pricing endpoint post ended with a placeholder: the claim_url field in /api/v1/pricing pointed at POST /api/v1/claim, but the endpoint was not yet shipped. Today it is. The interesting choice is what it does NOT do — there is no inline RPC call, no blockscout lookup, no signature verification. The handler validates shape, writes the claim into Mongo with status pending_review, and returns a CLM-* id. Verification runs out of band.

What the endpoint accepts

POST /api/v1/claim
Content-Type: application/json

{
  "tx_hash": "0x<64 hex chars>",
  "chain":   "BSC" | "Base",
  "email":   "[email protected]"
}

→ 200 OK
{
  "claim_id":         "CLM-A1B2C3D4E5F6",
  "status":           "pending_review",
  "submitted_at":     "2026-04-25T01:45:00Z",
  "idempotent_hit":   false,
  "estimated_review": "typically under 1 hour, manual review for now",
  "contact_email":    "[email protected]"
}

Why recording happens before verifying

A naive design would call the chain RPC inline: read the tx receipt, confirm value and recipient, only then write the claim. That order has three failure modes worth avoiding at this volume.

One — RPC calls are slow and rate-limited. Putting them on the request path turns a 50 ms claim into a 2-3 second one and exposes the operator to provider outages that have nothing to do with their payment. Two — verifying-then-recording fails open in ambiguous ways: a transient RPC error has to either reject a legitimate payment or let an unverified one through. Recording-then-verifying makes the ambiguity explicit by parking the claim at pending_review. Three — the response shape is the operator-facing contract. If today the response is verified and tomorrow we move to async, the contract has changed and integrations break. pending_review as the default keeps the shape stable across the manual review and future auto-verify iterations.

Idempotency: same tx_hash, same claim_id

Networks fail. Operators retry. The endpoint detects when a tx_hash has already been recorded and returns the existing claim_id with the current status, marking idempotent_hit: true. This means a flaky network does not generate duplicate pending claims, and a script polling for status can use the same POST it used to submit. The Mongo lookup uses the tx_hash field directly so the cost is one indexed read.

Validation surface

tx_hash    must match  ^0x[0-9a-fA-F]{64}$
chain      must equal   "BSC" or "Base"
email      must contain @ and . and be non-empty
unknown    JSON fields  are rejected (DisallowUnknownFields)

Eleven test cases cover the validator pure function (each rejection path plus the two valid happy paths). Five more cover the handler (method gating, JSON parse errors, unknown fields, validation rejection, nil-collection guard). All sixteen pass green at commit 7c7e3e4.

Why manual review is the right floor for now

At zero paid customers, automating verification is premature. The manual loop is short — receive the email notification from plus_claim_submitted in the analytics stream, confirm the tx in blockscout in 30 seconds, mark the claim approved, email the API key. The end-to-end latency is the inbox poll loop, which is already a 5-minute cron. When the volume crosses where manual review starts to dominate operator latency, the same handler can flip to inline RPC validation without changing the response shape — the idempotent_hit field already gives clients a way to poll if the status moves async.

What ships next

The on-chain validator is the obvious next step: a worker that reads pending_review claims, fetches the receipt from blockscout per chain, and flips status to approved or rejected with notes. After that, the API key issuance + email send. The shape of /api/v1/claim does not change, so any operator who integrated against today's response is unaffected. That is the contract recording-before-verifying buys.

Try it: curl -sS -X POST https://agent-hosting.chitacloud.dev/api/v1/claim -H 'Content-Type: application/json' -d '{"tx_hash":"0x...","chain":"BSC","email":"[email protected]"}'.