API reference
Last updated v1
Watchpost is one HTTP endpoint. Send an intended purchase to POST /v1/verify with a connection token, and act on the approve / review / block verdict it returns. This is the same contract the MCP tools and the skill wrap — paste it into your agent's system prompt.
Base URL & auth
Base URL: https://api.watchpost.systems. Every request authenticates with a per-user connection token (create one in the app → Connections) sent as a bearer header:
Authorization: Bearer wp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxVerify a purchase
curl https://api.watchpost.systems/v1/verify \
-H "Authorization: Bearer $WATCHPOST_TOKEN" \
-H "content-type: application/json" \
-d '{
"agent": { "name": "my-agent" },
"merchant": { "domain": "amazon.com" },
"product": { "title": "USB-C cable, 1m" },
"payment": { "amountMinor": 1599, "currency": "USD" }
}'Request body (JSON):
{
"agent": { "name": string, "runtime"?: string },
"merchant": { "domain": string, "displayName"?: string },
"product": { "title": string, "url"?: string,
"description"?: string, "rawListingHtml"?: string },
"payment": { "amountMinor": integer, // MINOR units/cents: $15.99 -> 1599
"currency": string, // ISO 4217, e.g. "USD"
"isRecurring"?: boolean }
}amountMinor is in the currency's minor unit (cents), never dollars — $15.99 is 1599, ¥6000 is 6000.
Verdict response (200):
{
"transactionId": "tx_…",
"decision": "approve" | "review" | "block",
"trustScore": 0-100,
"listingRiskScore": 0-100,
"category": string | null,
"ruleMatched": string | null,
"reasoning": [ { "pillar": "merchant"|"listing"|"rules",
"verdict": "pass"|"warn"|"fail",
"summary": string, "details"?: string[] } ],
// present only for "review":
"reviewExpectedWithinSeconds": 300,
// present only when verdict signing is configured (a compact JWS you can
// verify against /.well-known/jwks.json before acting):
"credential"?: string
}On approve, proceed. On block, do not pay and read the user the reasoning. On review, don't pay yet — poll the status endpoint below until the user answers.
Wait for a review decision
When the verdict is review, the user gets a push and taps Allow or Block. Poll until resolved is true:
GET /v1/transactions/{transactionId}/status
->
{
"transactionId": "tx_…",
"decision": "review",
"outcome": "PENDING" | "USER_APPROVED" | "USER_BLOCKED" | "EXPIRED" | …,
"status": "pending" | "approved" | "blocked" | "expired",
"resolved": boolean,
"respondedAt": string | null
}Only complete the purchase when status is approved. A review that nobody answers expires after 24 hours and resolves as expired — treat that (and blocked) as a decline; never pay on it.
After the payment attempt, report how it went so the record isn't left open:
POST /v1/transactions/{transactionId}/outcome
{ "outcome": "completed" | "failed" }Error shapes
Errors are JSON with an error code. The agent should relay a 402 to the user verbatim — it's actionable.
401 { "error": "invalid_token" } // bad / missing / revoked bearer token
402 { "error": "free_limit_reached", // free monthly cap hit
"limit": 10,
"upgradeUrl": "https://app.watchpost.systems/billing",
"resetsAt": "2026-08-01T00:00:00.000Z" }
429 { "error": "rate_limited", // + a Retry-After header (seconds)
"retryAfterSeconds": 30 }
409 { "error": "already_resolved" } // outcome/respond on a terminal tx
409 { "error": "not_reviewable" } // respond on a non-pending reviewPaid tiers never see the 402. A 409 on the outcome/respond endpoints means the transaction was already resolved (idempotent — a replay is safe, it just won't double-count).
Protocol adapters
If your purchase is authorized through a structured protocol, post the native payload instead and Watchpost normalizes it: POST /v1/verify/acp (Agentic Commerce Protocol checkout session) and POST /v1/verify/ap2 (Google AP2 cart mandate). Both return the same verdict shape.