{"openapi":"3.1.0","info":{"title":"ReturnCheck","version":"1.0.0","description":"ReturnCheck verifies which return policy actually applies to this purchase. Never invents: returns UNKNOWN instead of guessing.","contact":{"name":"ReturnCheck","email":"returncheckteam@gmail.com","url":"https://returncheck.m-angelmartinez-fer.workers.dev"}},"servers":[{"url":"https://returncheck.m-angelmartinez-fer.workers.dev"}],"paths":{"/v1/check":{"post":{"operationId":"check_return","summary":"Verify which return policy actually applies to this purchase.","description":"Returns a verified verdict with the exact policy clause. No API key = limited free trial; with an API key it costs 0.02 USD per useful verdict (UNKNOWN is free).","security":[{},{"bearerAuth":[]},{"paymentSignature":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.02"}},"x-x402":{"x402Version":2,"accepts":[{"scheme":"exact","network":"eip155:8453","amount":"20000","asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","payTo":"0xbF428071027402E9b0cE85e22146EDdc028cEB3b","maxTimeoutSeconds":60,"extra":{"name":"USD Coin","version":"2"}}],"extensions":{"payment-identifier":{"info":{"required":true},"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"required":{"type":"boolean"},"id":{"type":"string","minLength":16,"maxLength":128,"pattern":"^[a-zA-Z0-9_-]+$"}},"required":["required"]}},"bazaar":{"info":{"input":{"type":"http","method":"POST","bodyType":"json","body":{"product_url":"https://shop.example/product/1","buyer_country":"US","item_condition":"unopened","page_text":"New and unopened products may be returned within 30 days of delivery with proof of purchase."}}},"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"input":{"type":"object","properties":{"type":{"type":"string","const":"http"},"method":{"type":"string","enum":["POST"]},"bodyType":{"type":"string","const":"json"},"body":{"type":"object","required":["product_url","buyer_country"],"properties":{"product_url":{"type":"string","format":"uri"},"buyer_country":{"type":"string"},"item_condition":{"type":"string","enum":["unopened","opened","used","defective"]},"page_text":{"type":"string","description":"Caller-supplied policy text. Additional request fields are documented in OpenAPI."}}}},"required":["type","method","bodyType","body"]}},"required":["input"]}}},"unknown_is_free":true,"envelope":{"encoding":"base64-json","required_top_level":["x402Version","accepted","payload","extensions"],"accepted_from":"Copy the complete accepts[0] object from the live 402 challenge.","payload_shape":"payload directly contains signature and authorization; do not nest another PaymentPayload.","client_extensions":["payment-identifier"],"do_not_copy_extensions":["bazaar"],"resource":"Clients do not need to send resource; ReturnCheck sets the served resource before facilitator verification."},"identifier_path":"extensions[\"payment-identifier\"].info.id","legacy_identifier_path":"payload.extensions[\"payment-identifier\"]","identifier_required":true,"identifier_pattern":"^[a-zA-Z0-9_-]{16,128}$","legacy_supported":true,"discovery_probe":"POST /v1/check with no body and no PAYMENT-SIGNATURE returns the live 402 challenge before free-trial accounting. Do not send {} or a real check body for discovery.","retry":"After a paid attempt that may have reached settlement, reuse the same endpoint, body, identifier, signature and nonce; never create a second authorization to recover an uncertain outcome. If an authorization expires before any paid attempt is sent, sign a fresh one and use that exact fresh authorization for the later replay.","authorization_expiry":"If the authorization expires before the paid call, sign a fresh authorization. If that fresh authorization settles, replay that exact new authorization, signature and nonce.","free_trial":"Unsigned requests may use the free allowance. PAYMENT-SIGNATURE enters the payment path directly; no need to exhaust the allowance.","unknown":"UNKNOWN is not settled."},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckRequest"},"example":{"product_url":"https://shop.example/product/1","buyer_country":"US","item_condition":"unopened","page_text":"New and unopened products may be returned within 30 days of delivery with proof of purchase."}}}},"responses":{"200":{"description":"Verified answer (contract v1.0). Consult the payment headers for settlement status. X-ReturnCheck-Settlement is confirmed, pending, unconfirmed, not_charged or replay. pending and unconfirmed do NOT mean the call was free: they mean neither side knows yet. Do not assume you were not charged and do not create a second authorization; reuse the same identifier. A replay returns the stored answer with X-ReturnCheck-Replay: true and X-ReturnCheck-Cost: 0.0000, which means this call did not settle again — not that the original one did not settle.","headers":{"X-ReturnCheck-Cost":{"description":"Reported USD cost, 4 decimals. A nonzero value does not prove settlement when status is pending or unconfirmed. 0.0000 on replays and on UNKNOWN.","schema":{"type":"string"}},"X-ReturnCheck-Settlement":{"description":"confirmed | pending | unconfirmed | not_charged | replay.","schema":{"type":"string","enum":["confirmed","pending","unconfirmed","not_charged","replay"]}},"X-ReturnCheck-Replay":{"description":"Present and true when the stored answer for this identifier is served again.","schema":{"type":"string"}},"PAYMENT-RESPONSE":{"description":"Base64 x402 SettlementResponse. Inspect success and errorReason; its presence alone does not prove payment. An uncertain replay omits this header; confirmed or historical replays require a stored transaction hash.","schema":{"type":"string"}}}},"400":{"description":"Invalid input, or a signed call without a usable payment identity. error.code INVALID_INPUT: the body is not JSON or fails the contract. error.code PAYMENT_IDENTIFIER_REQUIRED: no valid extensions[\"payment-identifier\"].info.id, or the standard and legacy locations carry different identifiers, which is never resolved by picking one. Rejected before the idempotency gate, the engine and the facilitator: nothing was verified and nothing was settled. Fix the envelope and retry with the same identifier.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required. The body and the PAYMENT-REQUIRED header carry the challenge, including extensions. Cases: no PAYMENT-SIGNATURE, a signature that does not match the published terms, failed verification, an exhausted free allowance, and a settlement that the facilitator rejected. In the rejected-settlement case /settle was already invoked, so that identifier is not retried automatically: reconcile before authorizing again, and do not assume the rejected attempt cost nothing."},"409":{"description":"Payment identifier conflict. Nothing is settled again in either case. error.code CONFLICT: this identifier is already bound to a different request fingerprint (same id, different payload), which the payment-identifier extension requires to fail instead of replaying or charging twice. Use a new identifier for a genuinely different request. error.code PAYMENT_IN_FLIGHT: another request owns this identifier and is still processing. Retry the identical request with the same identifier.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"error.code INTERNAL, or an engine error with its own code. HTTP 500 alone does not establish whether settlement was attempted. Stop and reconcile the outcome before retrying; preserve the identical request, identifier and signed authorization. Never create a second authorization to recover from this error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"error.code PAYMENT_GATE_UNAVAILABLE. The gate is unavailable or did not grant an authoritative owner, the result could not be persisted before settlement, or the result could not be committed for a verdict that is never settled. This attempt did not invoke /settle; another attempt may still own the identifier. Retry the identical request with the same identifier, never with a second authorization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"},"paymentSignature":{"type":"apiKey","in":"header","name":"PAYMENT-SIGNATURE","description":"Base64 JSON x402 v2 client envelope: x402Version + accepted (complete live accepts[0]) + payload {signature, authorization} + extensions[\"payment-identifier\"].info.id. Do not copy the bazaar discovery extension into this header. Legacy payload.extensions[\"payment-identifier\"] remains server-compatible but is not the canonical form. Signed calls bypass the free allowance; UNKNOWN is not settled."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable machine code, e.g. INVALID_INPUT, PAYMENT_IDENTIFIER_REQUIRED, CONFLICT, PAYMENT_IN_FLIGHT, PAYMENT_GATE_UNAVAILABLE, INTERNAL."},"message":{"type":"string"},"details":{"type":"object","description":"Optional, present only when the handler supplies it."}}}},"required":["error"]},"CheckRequest":{"type":"object","required":["product_url","buyer_country"],"properties":{"product_url":{"type":"string","description":"Product page URL (http/https)."},"buyer_country":{"type":"string","description":"ISO 3166-1 alpha-2, e.g. US."},"item_condition":{"type":"string","enum":["unopened","opened","used","defective"]},"reason":{"type":"string","enum":["changed_mind","defective","wrong_size_or_model","arrived_late","other"]},"purchase_date":{"type":"string"},"delivery_date":{"type":"string"},"merchant":{"type":"string"},"seller_name":{"type":"string"},"buyer_state":{"type":"string","description":"2-letter uppercase subdivision code, e.g. CA. Needed to resolve state-conditional clauses (e.g. 'where prohibited by law') instead of falling back to UNKNOWN."},"as_of":{"type":"string","description":"YYYY-MM-DD. The date to evaluate the return window against, if not today."},"membership":{"type":"string","description":"Optional: the buyer's membership/loyalty tier with this merchant (e.g. 'Plus'), for policies with membership-conditional terms."},"purchase_channel":{"type":"string","enum":["online","store","phone","marketplace"],"description":"Optional: where the purchase was made, for policies with channel-conditional terms."},"page_text":{"type":"string","description":"RECOMMENDED. Plain text of the product or returns page you already have open. We verify against this instead of fetching: works on any store, no blocking, no JavaScript problem, and faster. Measured 2026-08-28: without it we reach the policy for 17 of 50 US retailers. Max 4,000,000 chars."},"page_html":{"type":"string","description":"RECOMMENDED (alternative to page_text). Raw HTML of the same page; lets us also read structured data (JSON-LD). Max 4,000,000 chars."},"page_source_url":{"type":"string","description":"URL of supplied page_text/page_html if different from product_url. Caller-declared provenance, not independently fetched."}}}}}}