{"openapi":"3.1.0","info":{"title":"ClixRx API","version":"1.0.0","description":"AI-native prescription discount lookup. One set of ClixRx BIN/PCN codes only — no competitor pricing.","contact":{"url":"https://clixrx.com"},"x-openai-compatible":true},"servers":[{"url":"https://api.clixrx.com","description":"Production"},{"url":"http://localhost:3001","description":"Local"}],"paths":{"/health":{"get":{"operationId":"getHealth","summary":"Health check","description":"Returns service status and dependency health.","tags":["System"],"responses":{"200":{"description":"Service healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded"]},"version":{"type":"string"},"uptime":{"type":"number"},"openapi":{"type":"string","example":"/api/openapi.json"},"deps":{"type":"object","properties":{"redis":{"type":"string","enum":["ok","error"]},"supabase":{"type":"string","enum":["ok","error","unconfigured"]}}},"disclaimer":{"type":"string"}}}}}}}}},"/api/v1/drugs/search":{"get":{"operationId":"searchDrugs","summary":"Search drugs","description":"Full-text drug search. Returns matching drugs with NDC codes.","tags":["Drugs"],"parameters":[{"name":"q","in":"query","required":true,"description":"Drug name, brand name, or NDC","schema":{"type":"string","example":"lisinopril 10mg"}},{"name":"zip","in":"query","required":true,"description":"ZIP code (5 digits) — required by DrugSearchRequestSchema","schema":{"type":"string","pattern":"^\\d{5}$","example":"90210"}}],"responses":{"200":{"description":"Drug search results","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/DrugSearchResult"}},"disclaimer":{"type":"string"}}}}}}}}},"/api/v1/prices":{"get":{"operationId":"getPrices","summary":"Get pharmacy prices","description":"Returns ClixRx discount prices for a given drug at nearby pharmacies. Always returns our BIN/PCN codes — never competitor codes.","tags":["Pricing"],"parameters":[{"name":"ndc","in":"query","required":true,"description":"11-digit NDC from a prior drug search","schema":{"type":"string","example":"00378-1805-10"}},{"name":"zip","in":"query","required":true,"description":"5-digit US ZIP code","schema":{"type":"string","example":"90210"}},{"name":"radius","in":"query","required":false,"description":"Search radius in miles (default 10, max 100)","schema":{"type":"number","minimum":1,"maximum":100,"default":10}},{"name":"quantity","in":"query","required":false,"description":"Quantity (default 30)","schema":{"type":"integer","minimum":1,"default":30,"example":30}}],"responses":{"200":{"description":"Pharmacy prices","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/PharmacyPriceResult"}},"disclaimer":{"type":"string"}}}}}}}}},"/api/v1/coupon":{"post":{"operationId":"createCoupon","summary":"Create digital coupon","description":"Generates a shareable digital coupon. BIN/PCN/Group/Member codes are minted server-side — callers supply only the drug, location, and chosen pharmacy (pharmacyId from a prior /prices call).","tags":["Coupon"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ndc","zip","pharmacyId","idempotencyKey"],"properties":{"ndc":{"type":"string","description":"11-digit NDC"},"zip":{"type":"string","description":"5-digit US ZIP code"},"pharmacyId":{"type":"string","description":"pharmacyId from a prior GET /api/v1/prices result"},"quantity":{"type":"integer","minimum":1,"maximum":360,"default":30},"drugName":{"type":"string","description":"Optional display name; resolved from NDC if omitted"},"genericName":{"type":"string","description":"Optional generic display name"},"deliveryMethod":{"type":"string","enum":["view","sms","email","print"],"default":"view","description":"RxSense text/email delivery is available only for RxSense price results."},"phone":{"type":"string","pattern":"^\\d{10}$","description":"Required when deliveryMethod is sms."},"email":{"type":"string","format":"email","description":"Required when deliveryMethod is email."},"deliveryConsent":{"type":"boolean","enum":[true],"description":"Required for sms or email one-time delivery."},"idempotencyKey":{"type":"string","format":"uuid","description":"Reuse the same value when retrying this exact request."}}}}}},"responses":{"201":{"description":"Created coupon","content":{"application/json":{"schema":{"type":"object","properties":{"coupon":{"$ref":"#/components/schemas/RxCoupon"},"disclaimer":{"type":"string"}}}}}},"409":{"description":"Idempotency conflict, delivery in progress, or delivery status unknown"},"422":{"description":"Requested delivery method is not supported by the selected pricing partner"},"429":{"description":"Per-IP or per-recipient delivery limit exceeded"},"503":{"description":"Durable delivery tracking unavailable or required migrations not applied"}}}},"/api/v1/coupon/{shareToken}":{"get":{"operationId":"getCouponByToken","summary":"Get coupon by share token","description":"Returns a public coupon by its share token. No authentication required.","tags":["Coupon"],"parameters":[{"name":"shareToken","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Coupon found","content":{"application/json":{"schema":{"type":"object","properties":{"coupon":{"$ref":"#/components/schemas/RxCoupon"},"disclaimer":{"type":"string"}}}}}},"404":{"description":"Coupon not found"}}}},"/api/v1/coupon/{shareToken}/wallet/google":{"post":{"operationId":"addCouponToGoogleWallet","summary":"Add coupon to Google Wallet","description":"Generates a Google Wallet pass save URL for the given coupon.","tags":["Coupon","Wallet"],"parameters":[{"name":"shareToken","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Google Wallet save URL","content":{"application/json":{"schema":{"type":"object","properties":{"saveUrl":{"type":"string","format":"uri"}}}}}}}}},"/api/v1/advisor":{"post":{"operationId":"askAdvisor","summary":"Ask Rx Advisor","description":"AI-powered Rx Advisor (Claude). Provides general drug information only — not medical advice. Always returns the mandatory legal disclaimer.","tags":["Advisor"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"message":{"type":"string","maxLength":2000},"drugContext":{"type":"object","properties":{"name":{"type":"string"},"dosage":{"type":"string"},"form":{"type":"string"}}}}}}}},"responses":{"200":{"description":"Advisor reply","content":{"application/json":{"schema":{"type":"object","properties":{"reply":{"type":"string"},"model":{"type":"string"}}}}}}}}},"/api/v1/scan":{"post":{"operationId":"scanPillBottle","summary":"Scan pill bottle","description":"Upload a photo of a pill bottle label. Claude Vision extracts drug name, dosage, and quantity.","tags":["Scan"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["image"],"properties":{"image":{"type":"string","format":"binary"}}}}}},"responses":{"200":{"description":"Scanned drug info","content":{"application/json":{"schema":{"type":"object","properties":{"drugName":{"type":"string","nullable":true},"dosage":{"type":"string","nullable":true},"quantity":{"type":"integer","nullable":true},"confidence":{"type":"number","minimum":0,"maximum":1}}}}}}}}},"/api/v1/pharmacies/nearby":{"get":{"operationId":"getPharmaciesNearby","summary":"Get nearby pharmacies","description":"Returns pharmacies within the specified radius of a ZIP code or lat/lng.","tags":["Pharmacies"],"parameters":[{"name":"zip","in":"query","schema":{"type":"string"}},{"name":"lat","in":"query","schema":{"type":"number"}},{"name":"lng","in":"query","schema":{"type":"number"}},{"name":"radiusMiles","in":"query","schema":{"type":"number","default":10}}],"responses":{"200":{"description":"Nearby pharmacies","content":{"application/json":{"schema":{"type":"object","properties":{"pharmacies":{"type":"array","items":{"$ref":"#/components/schemas/PharmacyPriceResult"}}}}}}}}}},"/api/v1/alerts":{"post":{"operationId":"createPriceAlert","summary":"Create price alert","description":"Subscribe to a QStash-backed price drop alert for a drug at a given ZIP.","tags":["Alerts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ndc","zip","targetPrice"],"properties":{"ndc":{"type":"string"},"zip":{"type":"string"},"targetPrice":{"type":"number","minimum":0},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"quantity":{"type":"integer","minimum":1,"default":30}}}}}},"responses":{"201":{"description":"Alert created","content":{"application/json":{"schema":{"type":"object","properties":{"alertId":{"type":"string","format":"uuid"},"message":{"type":"string"}}}}}}}},"get":{"operationId":"listPriceAlerts","summary":"List price alerts","description":"Returns the authenticated user's active price alerts. Requires a Clerk-issued Bearer token. Anonymous lookup by email or phone query parameters is NOT supported.","tags":["Alerts"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"List of alerts","content":{"application/json":{"schema":{"type":"object","properties":{"alerts":{"type":"array","items":{"$ref":"#/components/schemas/PriceAlert"}}}}}}},"401":{"description":"Auth required — Clerk Bearer token missing or invalid"}}}},"/api/v1/alerts/{id}":{"delete":{"operationId":"deletePriceAlert","summary":"Delete price alert","description":"Deletes one of the authenticated user's price alerts. Requires a Clerk-issued Bearer token; the alert must belong to the caller.","tags":["Alerts"],"security":[{"BearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"401":{"description":"Auth required — Clerk Bearer token missing or invalid"},"404":{"description":"Alert not found or not owned by the caller"}}}},"/api/v1/alerts/unsubscribe":{"get":{"operationId":"unsubscribeFromAlert","summary":"One-click unsubscribe from alert","description":"Public unsubscribe link included in alert notification emails.","tags":["Alerts"],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"token","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Unsubscribed successfully"},"400":{"description":"Invalid or expired token"}}}},"/api/v1/me":{"get":{"operationId":"getMe","summary":"Get authenticated user info","description":"Returns premium status. Anonymous-safe — never blocks.","tags":["User"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"User info","content":{"application/json":{"schema":{"type":"object","properties":{"isPremium":{"type":"boolean"},"premiumSince":{"type":"string","format":"date-time","nullable":true}}}}}}}}},"/api/v1/stripe/create-checkout":{"post":{"operationId":"createStripeCheckout","summary":"Create Stripe checkout session","description":"Creates a Stripe checkout URL for Premium upgrade.","tags":["Stripe"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Checkout URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}}}}}}}}},"/api/v1/stripe/webhook":{"post":{"operationId":"stripeWebhook","summary":"Stripe webhook receiver","description":"Receives Stripe events (checkout.session.completed, etc.). HMAC-SHA256 verified.","tags":["Stripe"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Webhook processed"},"400":{"description":"Invalid signature"}}}},"/api/v1/vault":{"get":{"operationId":"listVaultMedications","summary":"List Family Vault medications","tags":["Vault"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Vault entries","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/VaultMedication"}}}}}}}}},"post":{"operationId":"addVaultMedication","summary":"Add medication to Family Vault","tags":["Vault"],"security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["memberName","ndc","drugName","quantity","zip"],"properties":{"memberName":{"type":"string"},"ndc":{"type":"string"},"drugName":{"type":"string"},"genericName":{"type":"string"},"dosage":{"type":"string"},"quantity":{"type":"integer","minimum":1},"zip":{"type":"string"}}}}}},"responses":{"201":{"description":"Created vault entry","content":{"application/json":{"schema":{"type":"object","properties":{"entry":{"$ref":"#/components/schemas/VaultMedication"}}}}}}}}},"/api/v1/vault/{id}":{"delete":{"operationId":"deleteVaultMedication","summary":"Remove medication from Family Vault","tags":["Vault"],"security":[{"BearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}}}}},"/api/v1/openai/tools":{"get":{"operationId":"getOpenAITools","summary":"OpenAI-Compatible Tools Manifest","description":"Returns the complete tools[] array in OpenAI function-calling format. Use this to configure any OpenAI-compatible agent (GPT-4o, Cursor, Claude with tool use) to interact with ClixRx.","tags":["OpenAI Integration"],"responses":{"200":{"description":"Tools array","content":{"application/json":{"schema":{"type":"object","properties":{"tools":{"type":"array"},"meta":{"type":"object"}}}}}}}}},"/api/v1/openai/invoke":{"post":{"operationId":"invokeOpenAITool","summary":"Invoke a Single Tool (OpenAI Function Call)","description":"Execute a single tool call using the OpenAI function-calling payload format. Rate-limited (20 req/60 min).","tags":["OpenAI Integration"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"arguments":{"type":"object"}},"required":["name","arguments"]}}}},"responses":{"200":{"description":"Tool result + disclaimer"},"400":{"description":"Unknown tool or invalid arguments"},"429":{"description":"Rate limit exceeded"}}}},"/api/v1/openai/session":{"post":{"operationId":"openAISession","summary":"Multi-Turn Tool-Calling Session","description":"Orchestrate a full multi-turn conversation with automatic tool calling. Accepts OpenAI messages array, runs up to 5 tool call rounds, returns final answer. Rate-limited.","tags":["OpenAI Integration"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array"},"model":{"type":"string"}},"required":["messages"]}}}},"responses":{"200":{"description":"Final assistant message + tool trace"},"429":{"description":"Rate limit exceeded"}}}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Clerk-issued JWT (Authorization: Bearer <token>)"}},"schemas":{"RxCoupon":{"type":"object","description":"A ClixRx digital coupon. Always uses our BIN/PCN/Group codes.","required":["id","shareToken","drugName","genericName","dosage","quantity","pharmacyName","pharmacyChain","pharmacyAddress","price","bin","pcn","groupId","memberId","rxGroup","qrData","issuedAt","expiresAt"],"properties":{"id":{"type":"string","format":"uuid"},"shareToken":{"type":"string"},"drugName":{"type":"string"},"genericName":{"type":"string"},"dosage":{"type":"string"},"quantity":{"type":"integer","minimum":1},"pharmacyName":{"type":"string"},"pharmacyChain":{"type":"string"},"pharmacyAddress":{"type":"string"},"pharmacyPhone":{"type":"string"},"price":{"type":"number","minimum":0},"retailPrice":{"type":"number","minimum":0,"description":"Estimate, may be absent (CAD-352: not derived from UsualAndCustomary)."},"savingsAmt":{"type":"number","minimum":0,"description":"Estimate, may be absent (CAD-352)."},"savingsPct":{"type":"number","minimum":0,"maximum":100,"description":"Estimate, may be absent (CAD-352)."},"bin":{"type":"string","description":"ClixRx BIN code"},"pcn":{"type":"string","description":"ClixRx PCN code"},"groupId":{"type":"string","description":"ClixRx Group ID"},"memberId":{"type":"string"},"rxGroup":{"type":"string"},"qrData":{"type":"string"},"shareLink":{"type":"string","format":"uri"},"printUrl":{"type":"string","format":"uri","description":"Provider-hosted partner-branded printable coupon PDF, when available."},"issuedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"pharmacyLat":{"type":"number"},"pharmacyLng":{"type":"number"}}},"PharmacyPriceResult":{"type":"object","description":"Pharmacy price result with ClixRx discount codes.","required":["pharmacyId","pharmacyName","pharmacyChain","address","city","state","zip","price","bin","pcn","groupId","ndc","quantity","partnerSlug","fetchedAt"],"properties":{"pharmacyId":{"type":"string"},"pharmacyName":{"type":"string"},"pharmacyChain":{"type":"string"},"address":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"zip":{"type":"string"},"phone":{"type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"},"distanceMiles":{"type":"number"},"isOpen":{"type":"boolean"},"hasDriveThru":{"type":"boolean"},"is24Hours":{"type":"boolean"},"price":{"type":"number","minimum":0},"retailPrice":{"type":"number","minimum":0,"description":"Estimate, may be absent (CAD-352: not derived from UsualAndCustomary)."},"savingsAmt":{"type":"number","minimum":0,"description":"Estimate, may be absent (CAD-352)."},"savingsPct":{"type":"number","minimum":0,"maximum":100,"description":"Estimate, may be absent (CAD-352)."},"bin":{"type":"string","description":"ClixRx BIN"},"pcn":{"type":"string","description":"ClixRx PCN"},"groupId":{"type":"string"},"ndc":{"type":"string"},"quantity":{"type":"integer","minimum":1},"partnerSlug":{"type":"string","description":"Internal pricing partner identifier"},"fetchedAt":{"type":"string","format":"date-time"}}},"DrugSearchResult":{"type":"object","description":"A drug result from the ClixRx drug resolver.","required":["ndc","drugName","genericName","dosageForm","strength"],"properties":{"ndc":{"type":"string","description":"11-digit NDC code"},"drugName":{"type":"string"},"genericName":{"type":"string"},"brandName":{"type":"string","nullable":true},"dosageForm":{"type":"string","example":"TABLET"},"strength":{"type":"string","example":"10 MG"},"routeOfAdministration":{"type":"string","example":"ORAL"},"isGenericAvailable":{"type":"boolean"},"packageDescription":{"type":"string"}}},"PriceAlert":{"type":"object","description":"A QStash-backed price drop alert subscription.","required":["id","ndc","zip","target_price","quantity","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"ndc":{"type":"string"},"zip":{"type":"string"},"target_price":{"type":"number","minimum":0},"email":{"type":"string","format":"email","nullable":true},"phone":{"type":"string","nullable":true},"quantity":{"type":"integer","minimum":1},"created_at":{"type":"string","format":"date-time"}}},"VaultMedication":{"type":"object","description":"A medication entry in the Family Vault.","required":["id","memberName","ndc","drugName","quantity","zip","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"memberName":{"type":"string"},"ndc":{"type":"string"},"drugName":{"type":"string"},"genericName":{"type":"string"},"dosage":{"type":"string"},"quantity":{"type":"integer","minimum":1},"zip":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}}},"/api/v1/user/savings":{"post":{"operationId":"recordSavings","summary":"Record a savings event for a signed-in user","description":"PHI-safe: records only savings amount and share token — no drug or pharmacy names. Idempotent. Non-blocking.","tags":["User"],"security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["shareToken"],"properties":{"shareToken":{"type":"string","minLength":8,"maxLength":64}}}}}},"responses":{"200":{"description":"Savings recorded or no-op","content":{"application/json":{"schema":{"type":"object","properties":{"recorded":{"type":"boolean"},"savingsAmtCents":{"type":"integer"},"reason":{"type":"string"}}}}}},"401":{"description":"Auth required"}}}},"/api/v1/coupon/sms":{"post":{"operationId":"sendCouponSms","summary":"Send coupon via SMS (Twilio)","description":"Delivers coupon BIN/PCN/Group/Member codes to a US phone number via Twilio. Returns 501 if TWILIO_* env vars not configured.","tags":["Coupon"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["shareToken","phone"],"properties":{"shareToken":{"type":"string"},"phone":{"type":"string","example":"5551234567","description":"US phone number — leading +1 optional"}}}}}},"responses":{"200":{"description":"SMS sent","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"boolean"}}}}}},"404":{"description":"Coupon not found or expired"},"501":{"description":"SMS not configured — TWILIO_* env vars missing"},"502":{"description":"Twilio delivery failed"}}}},"/api/v1/coupon/email":{"post":{"operationId":"sendCouponEmail","summary":"Send coupon via email (Resend)","description":"Delivers a branded HTML coupon email via Resend. Returns 501 if RESEND_API_KEY not configured.","tags":["Coupon"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["shareToken","email"],"properties":{"shareToken":{"type":"string"},"email":{"type":"string","format":"email"}}}}}},"responses":{"200":{"description":"Email sent","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"boolean"}}}}}},"404":{"description":"Coupon not found or expired"},"501":{"description":"Email not configured — RESEND_API_KEY missing"},"502":{"description":"Resend delivery failed"}}}},"/api/v1/coupon/{shareToken}/wallet/apple":{"post":{"operationId":"addToAppleWallet","summary":"Generate Apple Wallet .pkpass for a coupon","description":"Returns a .pkpass binary that opens in Wallet.app on iOS. Requires APPLE_WALLET_* env vars. Returns 503 if unconfigured.","tags":["Coupon","Wallet"],"parameters":[{"name":"shareToken","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":".pkpass binary file","content":{"application/vnd.apple.pkpass":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Coupon not found or expired"},"503":{"description":"Apple Wallet not configured — APPLE_WALLET_* env vars missing"}}}},"/api/v1/gemini/tools":{"get":{"operationId":"geminiTools","summary":"Gemini-format tools manifest","description":"Returns all ClixRx tools as Gemini FunctionDeclaration objects. Used by Gemini models to invoke drug search, pricing, and coupon generation. Cached 1h.","tags":["AI"],"responses":{"200":{"description":"Gemini tools list","content":{"application/json":{"schema":{"type":"object","properties":{"tools":{"type":"array","items":{"type":"object"}},"meta":{"type":"object"}}}}}}}}},"/api/v1/gemini/invoke":{"post":{"operationId":"geminiInvoke","summary":"Invoke a single Gemini tool call","description":"Executes one tool by name with args and returns a Gemini FunctionResponse. Rate-limited per §16 rule 5.","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","args"],"properties":{"name":{"type":"string","example":"getRxPrices"},"args":{"type":"object","additionalProperties":true}}}}}},"responses":{"200":{"description":"Gemini FunctionResponse","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"response":{"type":"object","properties":{"content":{"type":"string","description":"JSON-stringified tool result"}}}}}}}},"400":{"description":"Invalid request or unknown tool"},"429":{"description":"Rate limit exceeded"}}}},"/api/v1/gemini/session":{"post":{"operationId":"geminiSession","summary":"Multi-turn Gemini tool-calling session","description":"Accepts a Gemini-format contents array, executes pending functionCall parts from the last model turn (max 3), returns combined FunctionResponse turn. Rate-limited.","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contents"],"properties":{"contents":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"object","properties":{"role":{"type":"string","enum":["user","model"]},"parts":{"type":"array","items":{"type":"object"}}}}},"model":{"type":"string","example":"gemini-2.0-flash-001"}}}}}},"responses":{"200":{"description":"Model turn with tool results","content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string"},"parts":{"type":"array","items":{"type":"object"}},"model":{"type":"string"}}}}}},"400":{"description":"Invalid request format"},"429":{"description":"Rate limit exceeded"}}}},"/api/v1/perplexity/tools":{"get":{"operationId":"perplexityTools","summary":"Perplexity-format tools manifest","description":"Returns all ClixRx tools in OpenAI function-calling format (compatible with Perplexity Sonar models). Cached 1h.","tags":["AI"],"responses":{"200":{"description":"Perplexity tools list","content":{"application/json":{"schema":{"type":"object","properties":{"tools":{"type":"array","items":{"type":"object"}},"meta":{"type":"object"}}}}}}}}},"/api/v1/vault/dashboard":{"get":{"operationId":"vaultDashboard","summary":"Vault entries with cached price lookups","description":"Returns all vault entries enriched with cached best prices and pharmacy names (read-only cache — never triggers live partner API calls). Requires auth.","tags":["Vault"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Enriched vault entries","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"type":"object"}},"disclaimer":{"type":"string"}}}}}},"401":{"description":"Auth required"},"503":{"description":"Database unavailable"}}}},"/api/v1/vault/refills":{"get":{"operationId":"vaultRefills","summary":"Upcoming refill timeline for vault medications","description":"Calculates days until next refill for each vault entry based on last_filled_date + days_supply. Returns null for entries without fill history. Requires auth.","tags":["Vault"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Refill timeline","content":{"application/json":{"schema":{"type":"object","properties":{"refills":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"drugName":{"type":"string"},"daysUntilRefill":{"type":"integer","nullable":true},"refillDate":{"type":"string","format":"date","nullable":true}}}},"disclaimer":{"type":"string"}}}}}},"401":{"description":"Auth required"},"503":{"description":"Database unavailable"}}}},"/api/v1/user/savings/summary":{"get":{"operationId":"getSavingsSummary","summary":"Get lifetime savings summary for authenticated user","description":"PHI-safe: returns only numeric totals — no drug names, pharmacy names, or PII.","tags":["User"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Lifetime savings summary","content":{"application/json":{"schema":{"type":"object","properties":{"lifetimeSavingsAmt":{"type":"number","example":184.23},"couponCount":{"type":"integer","example":7}}}}}},"401":{"description":"Auth required"}}}},"/api/v1/push/register-token":{"post":{"operationId":"registerPushToken","summary":"Register or update device push token","description":"Upserts an Expo push token for the authenticated user. PHI-safe: stores only token and platform. One token per user per platform (upsert on conflict).","tags":["Push"],"security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["expoPushToken","platform"],"properties":{"expoPushToken":{"type":"string","example":"ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]"},"platform":{"type":"string","enum":["ios","android","web"]}}}}}},"responses":{"200":{"description":"Token registered","content":{"application/json":{"schema":{"type":"object","properties":{"registered":{"type":"boolean"}}}}}},"400":{"description":"Invalid request"},"401":{"description":"Auth required"}}}},"/api/v1/antidote/drug/{name}":{"get":{"operationId":"getAntidoteDrug","summary":"Get drug information from NeedAntidote","description":"Returns structured drug information (uses, side effects, interactions) via the NeedAntidote partner API. Cached 1h.","tags":["Drugs"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","example":"lisinopril"},"description":"Drug name (brand or generic)"}],"responses":{"200":{"description":"Drug information","content":{"application/json":{"schema":{"type":"object","properties":{"drug":{"type":"object"},"disclaimer":{"type":"string"}}}}}},"404":{"description":"Drug not found"}}}},"/api/v1/location/zip":{"get":{"operationId":"latLngToZip","summary":"Convert lat/lng coordinates to US ZIP code","description":"Uses the US Census Bureau Geocoder to convert GPS coordinates to a 5-digit ZIP code. Used by the mobile app to auto-populate ZIP for price searches.","tags":["Location"],"parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number","example":40.7128}},{"name":"lng","in":"query","required":true,"schema":{"type":"number","example":-74.006}}],"responses":{"200":{"description":"ZIP code","content":{"application/json":{"schema":{"type":"object","properties":{"zip":{"type":"string","example":"10007"}}}}}},"400":{"description":"Invalid coordinates"},"404":{"description":"No ZIP found for coordinates"}}}},"/api/v1/me/context":{"get":{"operationId":"getUserContext","summary":"Get Single Brain user context (§14b)","description":"Returns the full user_context record for the authenticated user — query counts, usage tier, last search, refill predictions, and AI-pushed suggestions. Used by the Real-Time State Layer.","tags":["User"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"User context record","content":{"application/json":{"schema":{"type":"object","properties":{"context":{"type":"object"}}}}}},"401":{"description":"Auth required"}}}},"/api/v1/me/context/event":{"post":{"operationId":"pushUserContextEvent","summary":"Push an event into the Single Brain user context","description":"Increments query counts, updates last_search, or pushes AI-generated suggestions into the user_context record. Used by MCP tools and the advisor.","tags":["User"],"security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["type"],"properties":{"type":{"type":"string","example":"search"},"payload":{"type":"object","additionalProperties":true}}}}}},"responses":{"200":{"description":"Event recorded","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"401":{"description":"Auth required"}}}},"/api/v1/me/insurance-cards":{"get":{"operationId":"listInsuranceCards","summary":"List saved insurance cards for authenticated user","description":"Returns all saved insurance cards from the hybrid OCR + manual-entry vault. PHI: cards are stored server-side, never in browser storage.","tags":["User"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Insurance cards list","content":{"application/json":{"schema":{"type":"object","properties":{"cards":{"type":"array","items":{"type":"object"}}}}}}},"401":{"description":"Auth required"}}},"post":{"operationId":"saveInsuranceCard","summary":"Save a new insurance card","description":"Stores an insurance card (from OCR scan or manual entry). Returns the saved card with assigned ID.","tags":["User"],"security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["bin","pcn","groupId","consentGivenAt"],"properties":{"bin":{"type":"string","pattern":"^\\d{6}$"},"pcn":{"type":"string"},"groupId":{"type":"string"},"consentGivenAt":{"type":"string","format":"date-time","description":"ISO timestamp when the user checked the consent box"},"planName":{"type":"string"},"label":{"type":"string"}}}}}},"responses":{"201":{"description":"Card saved","content":{"application/json":{"schema":{"type":"object","properties":{"card":{"type":"object"}}}}}},"401":{"description":"Auth required"}}}},"/api/v1/me/insurance-cards/{id}":{"delete":{"operationId":"deleteInsuranceCard","summary":"Delete a saved insurance card","tags":["User"],"security":[{"BearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Card deleted"},"401":{"description":"Auth required"},"404":{"description":"Card not found"}}}},"/api/v1/recommendations":{"post":{"operationId":"getRecommendations","summary":"AI recommendation engine — deterministic mode selection","description":"Takes a drug search context and returns structured recommendations including decision mode, transfer difficulty rating, insurance comparison, and top pharmacy options. Deterministic — no LLM call.","tags":["Advisor"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["drugName","zip"],"properties":{"drugName":{"type":"string","example":"lisinopril"},"zip":{"type":"string","example":"10001"},"quantity":{"type":"integer","default":30},"hasInsurance":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Recommendations","content":{"application/json":{"schema":{"type":"object","properties":{"recommendation":{"type":"object"},"disclaimer":{"type":"string"}}}}}},"400":{"description":"Invalid request"}}}},"/api/v1/search/nl":{"post":{"operationId":"naturalLanguageSearch","summary":"Parse natural language drug search query","description":"Accepts a free-text query (e.g. \"cheapest Eliquis near me for 90 days\") and returns structured search intent via Claude. PHI rule §16: query length logged only — never the query text.","tags":["Drugs"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["query"],"properties":{"query":{"type":"string","minLength":2,"maxLength":300,"example":"cheapest metformin near 10001"},"zip":{"type":"string","example":"10001"}}}}}},"responses":{"200":{"description":"Parsed search intent","content":{"application/json":{"schema":{"type":"object","properties":{"drugName":{"type":"string"},"dosage":{"type":"string"},"quantity":{"type":"integer"},"inferredMode":{"type":"string"},"disclaimer":{"type":"string"}}}}}},"400":{"description":"Invalid request"}}}}}}