{"openapi":"3.1.0","info":{"title":"Rate — Booking Outcome Layer","version":"0.3.0","description":"The booking receipt for your agent. Send a public booking URL, get a durable booking record, tx_hash, and parsed confirmation data in return. $0.25 USDC per confirmed booking via x402 on Base. Real-world action, not raw HTML. Works with Square, Fresha, Vagaro, Booksy, Acuity, Calendly, and more — no merchant onboarding required.\n\nWhat you get:\n- Receipts: typed booking record + parsed referenceId + manageUrl, not a screenshot.\n- Idempotent retries: same Idempotency-Key never double-books.\n- Slot reservation: opaque slot_ids prevent multi-agent contention.\n- Lifecycle: cancel a booking via /api/bookings/:id/cancel (free in v0).\n- Push events: webhooks fire on booking.confirmed / booking.failed / booking.cancelled."},"servers":[{"url":"https://rateai.dev"}],"x-service-info":{"categories":["appointments","bookings","agent-action","commerce"],"positioning":"outcome-layer","tagline":"The booking receipt for your agent."},"components":{"securitySchemes":{"x402Auth":{"type":"apiKey","in":"header","name":"X-PAYMENT","description":"Base64-encoded x402 PaymentPayload satisfying the challenge returned by a prior 402 response."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable","next_step"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"retryable":{"type":"boolean"},"next_step":{"type":"string"}}}}},"X402Challenge":{"type":"object","description":"Canonical x402 v2 PaymentRequired body. Returned in the 402 response. Clients sign a v2 PaymentPayload referencing one of `accepts` and resend with X-PAYMENT.","properties":{"x402Version":{"type":"integer","const":1},"error":{"type":"string","example":"Payment required"},"resource":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"description":{"type":"string"},"mimeType":{"type":"string"}}},"accepts":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","const":"exact"},"network":{"type":"string","description":"CAIP-2 chain identifier. eip155:8453 = Base mainnet, eip155:84532 = Base Sepolia (testnet).","examples":["eip155:8453","eip155:84532"]},"asset":{"type":"string","description":"ERC-20 contract address of USDC on the chosen network."},"amount":{"type":"string","description":"atomic units (USDC has 6 decimals; $0.25 = 250000)"},"payTo":{"type":"string","description":"Recipient address"},"maxTimeoutSeconds":{"type":"integer"},"extra":{"type":"object","description":"EIP-712 domain hints (e.g. { name: 'USDC', version: '2' })"}}}}}},"RiskFlags":{"type":"object","properties":{"login_required":{"type":"boolean"},"captcha_detected":{"type":"boolean"},"card_required":{"type":"boolean"},"deposit_required":{"type":"boolean"}}},"Slot":{"type":"object","properties":{"slot_id":{"type":"string","description":"opaque — expires in 10 min; pass unchanged to /api/book"},"start_time":{"type":"string","format":"date-time"},"display":{"type":"string","description":"human-readable label, e.g. 'Saturday, May 3 at 6:30 PM'"},"confidence":{"type":"number","description":"extraction confidence 0..1"},"staff":{"type":"string","nullable":true},"estimated_price":{"type":"string","nullable":true},"duration_minutes":{"type":"integer","nullable":true}}},"ParsedConfirmation":{"type":"object","nullable":true,"description":"Structured fields extracted from the raw confirmation_text. JSON, not a screenshot.","properties":{"rawText":{"type":"string"},"referenceId":{"type":"string","nullable":true,"description":"Booking reference / confirmation number"},"manageUrl":{"type":"string","nullable":true,"description":"Deep link to manage / cancel this booking"},"platform":{"type":"string","description":"Platform the parser used (e.g. 'square')"},"parsedAt":{"type":"string","format":"date-time"}}},"Booking":{"type":"object","properties":{"id":{"type":"string","example":"apt_8m2k4z"},"booking_url":{"type":"string"},"platform":{"type":"string","example":"square"},"business_name":{"type":"string","nullable":true},"service_name":{"type":"string","nullable":true},"start_time":{"type":"string","format":"date-time","nullable":true},"status":{"type":"string","enum":["pending","paid","booked","failed","needs_human","cancelled"]},"confirmation_text":{"type":"string","nullable":true},"confirmation_data":{"$ref":"#/components/schemas/ParsedConfirmation"},"cancelled_at":{"type":"string","format":"date-time","nullable":true},"customer":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string","nullable":true}}}}},"Payment":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"rail":{"type":"string","const":"x402"},"amount":{"type":"string"},"currency":{"type":"string","const":"USDC"},"network":{"type":"string","const":"base"},"tx_hash":{"type":"string","nullable":true},"payer":{"type":"string","nullable":true}}},"TraceEvent":{"type":"object","properties":{"event_type":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"payload":{"type":"object"}}},"BookingResponse":{"type":"object","properties":{"booking":{"$ref":"#/components/schemas/Booking"},"payment":{"$ref":"#/components/schemas/Payment"},"receipt":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"url":{"type":"string"}}},"trace":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"$ref":"#/components/schemas/TraceEvent"}}}},"idempotency_key":{"type":"string"}}},"WebhookSubscription":{"type":"object","properties":{"id":{"type":"string","example":"whk_a1b2c3"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["booking.confirmed","booking.failed","booking.cancelled"]}},"secret":{"type":"string","description":"HMAC-SHA256 signing secret. Returned ONLY on creation; verify deliveries with X-Rate-Signature header."},"active":{"type":"boolean"}}}}},"paths":{"/api/inspect":{"post":{"operationId":"inspectBookingPage","summary":"Detect platform, business name, and risk flags for a public booking URL","description":"Inspect any public booking page (Square, Fresha, Vagaro, Booksy, Acuity, Calendly, etc.) and detect the platform, business name, and risk flags (login required, captcha, card-on-file required, deposit required) before attempting a real booking. Free.","tags":["inspect","platform-detection","risk-flags","free","agent-readable"],"x-payment-info":{"cost":"free","protocols":[]},"x-bazaar":{"categories":["appointments","bookings","agent-action"],"outcomeNote":"Returns a typed verdict (supported / not supported) and a structured risk_flags object — not a screenshot. Agents use this to decide whether to proceed without paying."},"x-guidance":"Call first. If supported=false, do not proceed — inform the user the page requires human action.","x-error-codes":["bad_request","browser_failed"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["booking_url"],"properties":{"booking_url":{"type":"string","format":"uri"}}}}}},"responses":{"200":{"description":"Inspection result","content":{"application/json":{"schema":{"type":"object","properties":{"supported":{"type":"boolean"},"platform":{"type":"string"},"business_name":{"type":"string","nullable":true},"detected_actions":{"type":"array","items":{"type":"string"}},"risk_flags":{"$ref":"#/components/schemas/RiskFlags"},"trace_id":{"type":"string"},"next_step":{"type":"string"}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/availability":{"post":{"operationId":"checkAvailability","summary":"Extract available appointment slots on the live booking page","description":"Extract available appointment slots from any public booking page on the live booking page. Returns opaque slot_ids (10-minute reservation hold) that you pass unchanged to /api/book. Free.","tags":["availability","slots","calendar","free","agent-readable"],"x-payment-info":{"cost":"free","protocols":[]},"x-bazaar":{"categories":["appointments","bookings","agent-action"],"outcomeNote":"Each slot includes a confidence score and a normalized ISO 8601 start_time. Slot reservations (via opaque slot_id) prevent multi-agent contention on the same calendar."},"x-guidance":"slot_id is opaque and expires in 10 minutes — pass it unchanged to /api/book. Re-call if the user takes longer than 10 min to confirm.","x-error-codes":["bad_request","browser_failed"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["booking_url"],"properties":{"booking_url":{"type":"string","format":"uri"},"service_query":{"type":"string","description":"natural-language service name"},"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"YYYY-MM-DD"},"time_window":{"type":"object","properties":{"start":{"type":"string","pattern":"^\\d{2}:\\d{2}$"},"end":{"type":"string","pattern":"^\\d{2}:\\d{2}$"}}}}}}}},"responses":{"200":{"description":"Available slots","content":{"application/json":{"schema":{"type":"object","properties":{"business_name":{"type":"string","nullable":true},"platform":{"type":"string"},"service":{"type":"object","nullable":true,"properties":{"name":{"type":"string"},"estimated_price":{"type":"string","nullable":true},"duration_minutes":{"type":"integer","nullable":true}}},"slots":{"type":"array","items":{"$ref":"#/components/schemas/Slot"}},"trace_id":{"type":"string"},"next_step":{"type":"string"}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/book":{"post":{"operationId":"createBooking","summary":"Book a real appointment — $0.25 USDC, returns 202 + poll URL (durable receipt via GET /api/bookings/:id)","description":"Book a real appointment at any public booking page. Pay $0.25 USDC via x402 on Base. Returns a durable booking record with structured confirmation data (referenceId, manageUrl), tx_hash, and a queryable trace timeline.","tags":["book","appointment","x402","usdc","base","outcome-billing","real-world-action"],"security":[{"x402Auth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string"},"description":"Stable per-attempt key. Use the same value on retry to avoid double-booking."}],"x-payment-info":{"cost":"$0.25 USDC","protocols":["x402"],"network":"base","asset":"USDC"},"x-bazaar":{"categories":["appointments","bookings","commerce","agent-action"],"outcomeNote":"You buy a confirmed booking, not raw HTML. Idempotency-Key support means retries are safe; the response includes parsed confirmation_data + receipt_id you can hand to your user. This is Rate's outcome-layer wedge: receipts, not compute."},"x-guidance":"First call without X-PAYMENT to get the 402 challenge. Confirm with user, pay, retry with same Idempotency-Key + X-PAYMENT (or PAYMENT-SIGNATURE). Server returns 202 in ~3s; the actual booking + on-chain settlement run in the background. Poll GET /api/bookings/:id every 3s until booking.status is no longer 'paid', or subscribe to webhooks for push delivery. The eventual record includes parsed confirmation_data (referenceId, manageUrl) + payment.tx_hash. If status flips to needs_human, tell the user to complete the booking manually.","x-error-codes":["idempotency_required","confirmation_required","bad_request","payment_required","payment_invalid","slot_expired","needs_human","browser_failed","internal"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["booking_url","slot_id","customer","user_confirmation"],"properties":{"booking_url":{"type":"string","format":"uri"},"slot_id":{"type":"string","description":"opaque slot_id from /api/availability — expires in 10 min"},"service_name":{"type":"string"},"start_time":{"type":"string","format":"date-time"},"customer":{"type":"object","required":["name","email"],"properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"}}},"user_confirmation":{"type":"string","const":"yes","description":"Must be 'yes' — confirms user explicitly approved the booking"}}}}}},"responses":{"200":{"description":"Idempotency replay — same Idempotency-Key was used on a previously-completed booking. Returns the durable record with booking.status: \"booked\".","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponse"}}}},"202":{"description":"Booking accepted and queued. Verify + payment row insert completed; the actual booking run + on-chain settlement happen in the background (~60–120s). Poll GET /api/bookings/:id every 3s until booking.status flips from \"paid\" to \"booked\" / \"needs_human\" / \"failed\". Or subscribe to webhooks for push delivery. Response shape extends BookingResponse with `status: \"queued\"` and `poll_url`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required (x402 challenge)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"$ref":"#/components/schemas/X402Challenge"}]}}}},"410":{"description":"Slot expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Idempotency replay — the same Idempotency-Key was used on a booking that ended in needs_human or failed. Returns the failure reason.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Browser session failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/bookings/{id}":{"get":{"operationId":"getBooking","summary":"Fetch a booking record, payment metadata, parsed confirmation, and trace events","description":"Fetch a previously-created booking record + parsed confirmation + payment + trace events. Free.","tags":["receipt","history","free"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-payment-info":{"cost":"free","protocols":[]},"x-bazaar":{"categories":["appointments","bookings","agent-readable"],"outcomeNote":"The durable record. Agents call this to show users their receipt or audit what happened."},"x-guidance":"Use to display the receipt (with parsed referenceId + manageUrl) or trace timeline.","x-error-codes":["not_found"],"responses":{"200":{"description":"Booking detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/bookings/{id}/cancel":{"post":{"operationId":"cancelBooking","summary":"Cancel a booked appointment on the live booking page — free in v0","description":"Cancel a previously-booked appointment on the live booking page. Free in v0. Re-uses the booking's parsed confirmation (referenceId / manageUrl) to navigate platform-specific cancel flows.","tags":["cancel","lifecycle","free","real-world-action"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-payment-info":{"cost":"free","protocols":[]},"x-bazaar":{"categories":["appointments","bookings","agent-action","lifecycle"],"outcomeNote":"Lifecycle ownership: cancel the thing you booked 3 days ago without re-walking the customer-account login. Rate owns the booking record and drives the platform-specific cancel UI. Free in v0."},"x-guidance":"Idempotent — calling on an already-cancelled booking returns its current state without re-running. If the platform requires login or doesn't surface a cancel UI, response is needs_human (422).","x-error-codes":["not_found","bad_request","needs_human","browser_failed"],"responses":{"200":{"description":"Cancelled (or already cancelled)","content":{"application/json":{"schema":{"type":"object","properties":{"booking_id":{"type":"string"},"status":{"type":"string","enum":["cancelled"]},"cancelled_at":{"type":"string","format":"date-time","nullable":true},"cancellation_text":{"type":"string","nullable":true},"trace_id":{"type":"string"}}}}}},"404":{"description":"Booking not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Needs human (login, captcha, or platform requires manual cancel)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/webhooks":{"post":{"operationId":"registerWebhook","summary":"Register a webhook to receive booking lifecycle events","description":"Register a URL to receive push notifications for booking.confirmed / booking.failed / booking.cancelled events. Bodies are signed with HMAC-SHA256 (X-Rate-Signature header). Free.","tags":["webhooks","events","free","push","agent-event-driven"],"x-payment-info":{"cost":"free","protocols":[]},"x-bazaar":{"categories":["bookings","events","infrastructure"],"outcomeNote":"Event-driven, not poll-based. Agents register once and get notified when a booking flips state — useful when humans confirm async or when /api/book hits needs_human and recovers later."},"x-guidance":"Save the returned `secret` — it is only shown once. Verify deliveries with X-Rate-Signature header (HMAC-SHA256 of the body using the secret).","x-error-codes":["bad_request"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["booking.confirmed","booking.failed","booking.cancelled"]},"default":["booking.confirmed"]}}}}}},"responses":{"201":{"description":"Subscription created (secret returned only once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscription"}}}}}},"get":{"operationId":"listWebhooks","summary":"List registered webhook subscriptions (without secrets)","x-payment-info":{"cost":"free","protocols":[]},"responses":{"200":{"description":"Subscriptions","content":{"application/json":{"schema":{"type":"object","properties":{"subscriptions":{"type":"array","items":{"$ref":"#/components/schemas/WebhookSubscription"}}}}}}}}}},"/api/traces/{id}":{"get":{"operationId":"getTrace","summary":"Fetch the full event trace for a booking or inspection","description":"Fetch the full event trace (timeline) for any booking or inspection. Free.","tags":["trace","events","audit","free"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-payment-info":{"cost":"free","protocols":[]},"x-bazaar":{"categories":["bookings","observability"],"outcomeNote":"Audit trail. Agents and humans can replay what Rate did at every step."},"x-guidance":"Use for debugging or displaying a timeline of what Rate did.","x-error-codes":["not_found"],"responses":{"200":{"description":"Trace events","content":{"application/json":{"schema":{"type":"object","properties":{"trace_id":{"type":"string"},"booking_id":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"events":{"type":"array","items":{"$ref":"#/components/schemas/TraceEvent"}}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}