whale-code 6.5.7 → 6.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/dist/cli/services/agent-loop.js +26 -2
- package/dist/cli/services/agent-loop.js.map +1 -1
- package/dist/cli/services/hooks.js +2 -1
- package/dist/cli/services/hooks.js.map +1 -1
- package/dist/cli/services/telemetry-spans.js +1 -0
- package/dist/cli/services/telemetry-spans.js.map +1 -1
- package/dist/cli/services/telemetry.d.ts +23 -0
- package/dist/cli/services/telemetry.js +45 -1
- package/dist/cli/services/telemetry.js.map +1 -1
- package/dist/server/handlers/__test-utils__/test-db.d.ts +17 -3
- package/dist/server/handlers/__test-utils__/test-db.js +113 -14
- package/dist/server/handlers/__test-utils__/test-db.js.map +1 -1
- package/dist/server/handlers/affiliates.d.ts +9 -0
- package/dist/server/handlers/affiliates.js +197 -0
- package/dist/server/handlers/affiliates.js.map +1 -0
- package/dist/server/handlers/api-docs.d.ts +4 -2
- package/dist/server/handlers/api-docs.js +204 -1681
- package/dist/server/handlers/api-docs.js.map +1 -1
- package/dist/server/handlers/campaigns.d.ts +9 -0
- package/dist/server/handlers/campaigns.js +237 -0
- package/dist/server/handlers/campaigns.js.map +1 -0
- package/dist/server/handlers/catalog-schemas.js +9 -9
- package/dist/server/handlers/catalog-schemas.js.map +1 -1
- package/dist/server/handlers/catalog.js +1 -1
- package/dist/server/handlers/catalog.js.map +1 -1
- package/dist/server/handlers/comms-documents.js +28 -2
- package/dist/server/handlers/comms-documents.js.map +1 -1
- package/dist/server/handlers/comms-pdf-generation.js +25 -3
- package/dist/server/handlers/comms-pdf-generation.js.map +1 -1
- package/dist/server/handlers/comms-pdf-helpers.js +4 -4
- package/dist/server/handlers/comms-pdf-helpers.js.map +1 -1
- package/dist/server/handlers/comms.d.ts +100 -0
- package/dist/server/handlers/comms.js +146 -12
- package/dist/server/handlers/comms.js.map +1 -1
- package/dist/server/handlers/coupons.d.ts +9 -0
- package/dist/server/handlers/coupons.js +220 -0
- package/dist/server/handlers/coupons.js.map +1 -0
- package/dist/server/handlers/embeddings.js +1 -1
- package/dist/server/handlers/embeddings.js.map +1 -1
- package/dist/server/handlers/enrichment.js +2 -622
- package/dist/server/handlers/enrichment.js.map +1 -1
- package/dist/server/handlers/fulfillment.d.ts +9 -0
- package/dist/server/handlers/fulfillment.js +209 -0
- package/dist/server/handlers/fulfillment.js.map +1 -0
- package/dist/server/handlers/google-ads.d.ts +24 -0
- package/dist/server/handlers/google-ads.js +2199 -0
- package/dist/server/handlers/google-ads.js.map +1 -0
- package/dist/server/handlers/invoices.d.ts +9 -0
- package/dist/server/handlers/invoices.js +252 -0
- package/dist/server/handlers/invoices.js.map +1 -0
- package/dist/server/handlers/loyalty.d.ts +9 -0
- package/dist/server/handlers/loyalty.js +197 -0
- package/dist/server/handlers/loyalty.js.map +1 -0
- package/dist/server/handlers/meta-ads-graph-api.js +18 -3
- package/dist/server/handlers/meta-ads-graph-api.js.map +1 -1
- package/dist/server/handlers/phone.d.ts +9 -0
- package/dist/server/handlers/phone.js +197 -0
- package/dist/server/handlers/phone.js.map +1 -0
- package/dist/server/handlers/pipeline.d.ts +9 -0
- package/dist/server/handlers/pipeline.js +277 -0
- package/dist/server/handlers/pipeline.js.map +1 -0
- package/dist/server/handlers/qr-codes.d.ts +9 -0
- package/dist/server/handlers/qr-codes.js +198 -0
- package/dist/server/handlers/qr-codes.js.map +1 -0
- package/dist/server/handlers/reviews.d.ts +9 -0
- package/dist/server/handlers/reviews.js +171 -0
- package/dist/server/handlers/reviews.js.map +1 -0
- package/dist/server/handlers/segments.d.ts +9 -0
- package/dist/server/handlers/segments.js +229 -0
- package/dist/server/handlers/segments.js.map +1 -0
- package/dist/server/handlers/social.d.ts +9 -0
- package/dist/server/handlers/social.js +81 -0
- package/dist/server/handlers/social.js.map +1 -0
- package/dist/server/handlers/tax.d.ts +9 -0
- package/dist/server/handlers/tax.js +182 -0
- package/dist/server/handlers/tax.js.map +1 -0
- package/dist/server/handlers/wallet.d.ts +9 -0
- package/dist/server/handlers/wallet.js +203 -0
- package/dist/server/handlers/wallet.js.map +1 -0
- package/dist/server/handlers/webhooks-mgmt.d.ts +9 -0
- package/dist/server/handlers/webhooks-mgmt.js +181 -0
- package/dist/server/handlers/webhooks-mgmt.js.map +1 -0
- package/dist/server/handlers/wholesale.d.ts +9 -0
- package/dist/server/handlers/wholesale.js +219 -0
- package/dist/server/handlers/wholesale.js.map +1 -0
- package/dist/server/index.js +20 -9
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/clickhouse-buffer.js +1 -0
- package/dist/server/lib/clickhouse-buffer.js.map +1 -1
- package/dist/server/lib/coa-renderer.d.ts +1 -1
- package/dist/server/lib/coa-renderer.js +32 -10
- package/dist/server/lib/coa-renderer.js.map +1 -1
- package/dist/server/server-worker.d.ts +1 -0
- package/dist/server/server-worker.js +464 -3
- package/dist/server/server-worker.js.map +1 -1
- package/dist/server/tool-router.js +118 -4
- package/dist/server/tool-router.js.map +1 -1
- package/package.json +28 -4
- package/vendor/ink/package.json +0 -2
- package/whale-logo.png +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-docs.js","names":["GATEWAY_BASE_URL","SECTIONS","products","name","description","base","endpoints","method","path","scope","params","limit","starting_after","ending_before","category_id","status","type","search","include","response","body","sku","short_description","cost_price","pricing_data","field_values","featured_image","image_gallery","manage_stock","stock_quantity","orders","customer_id","location_id","created_after","created_before","fulfillment_status","tracking_number","tracking_url","shipping_service","shipping_method","staff_notes","notes","amount","reason","cart","product_id","quantity","variation_id","tier","unit_price","checkout","cart_id","customer_email","customer_name","payment_method","opaque_data","bill_to","ship_to","loyalty_code","loyalty_points_redeemed","loyalty_discount_amount","cartItems","paymentMethod","locationId","registerId","sessionId","customerId","customerName","userId","tipAmount","cashTendered","changeGiven","idempotencyKey","terminal_id","customers","first_name","last_name","email","phone","date_of_birth","inventory","low_stock","quantity_change","from_location_id","to_location_id","locations","analytics","start_date","end_date","storefront","metadata","session_id","event_type","event_data","code","coa","sample_id","portal","token","webhooks","timestamp","event","agent","message","messages","conversation_id","context","telemetry","session","user","errors","events","vitals","ai_calls","media","url","w","q","f","s","infrastructure","detailed","AUTH_GUIDE","base_url","api_version","api_version_header","authentication","header","key_format","how_to_get","scopes","wildcard","per_resource","internal","internal_note","rate_limits","default_per_minute","headers","exceeded_status","retry_header","pagination","style","error_format","shape","types","authentication_error","permission_error","not_found_error","invalid_request_error","rate_limit_error","api_error","security_headers","cors","idempotency","versioning","url_pattern","note","getExamples","section","BASE","storeVar","keyVar","templates","raw_fetch","nextjs_ssr","react_client","QUICK_START","title","steps","step","tips","handleApiDocs","_sb","args","_storeId","action","sections","Object","entries","map","key","endpoint_count","length","success","data","total_endpoints","reduce","sum","usage","error","keys","join","examples","tip","_cachedSpec","toOpenApiPath","replace","buildParameters","endpoint","pathParams","match","p","slice","push","in","required","schema","desc","toLowerCase","startsWith","buildRequestBody","undefined","properties","content","generateOpenApiSpec","paths","sectionKey","openApiPath","operation","summary","operationId","split","pop","tags","parameters","responses","security","BearerAuth","ApiKeyAuth","requestBody","openapi","info","version","contact","servers","components","securitySchemes","scheme","bearerFormat"],"sources":["../../../src/server/handlers/api-docs.ts"],"sourcesContent":["// server/handlers/api-docs.ts — Static API documentation reference for agents\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\n\nconst GATEWAY_BASE_URL = \"https://whale-gateway.fly.dev\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface Endpoint {\n method: \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\";\n path: string;\n scope: string;\n description: string;\n params?: Record<string, string>;\n body?: Record<string, string>;\n response: string;\n notes?: string;\n}\n\ninterface Section {\n name: string;\n description: string;\n base: string;\n endpoints: Endpoint[];\n}\n\n// ---------------------------------------------------------------------------\n// Endpoint registry — every public REST API route\n// ---------------------------------------------------------------------------\n\nconst SECTIONS: Record<string, Section> = {\n products: {\n name: \"Products\",\n description: \"Product catalog CRUD, variations, and categories\",\n base: \"/v1/stores/:storeId/products\",\n endpoints: [\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/products\",\n scope: \"read:products\",\n description: \"List products with pagination and filters\",\n params: {\n limit: \"Number (1-100, default 25)\",\n starting_after: \"Product ID cursor for forward pagination\",\n ending_before: \"Product ID cursor for backward pagination\",\n category_id: \"Filter by category UUID\",\n status: \"Filter by status: published, draft, archived\",\n type: \"Filter by type: simple, variable, grouped\",\n search: \"Full-text search on name and SKU\",\n include: \"Set to 'none' to skip loading variations (faster for large catalogs)\",\n },\n response: '{ object: \"list\", data: Product[], has_more: boolean, url: string }',\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/products/:id\",\n scope: \"read:products\",\n description: \"Get a single product with variations and inventory\",\n response: \"Product (includes pricing_data, field_values, inventory, variations)\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/products\",\n scope: \"write:products\",\n description: \"Create a new product\",\n body: {\n name: \"Required. Product name\",\n sku: \"SKU code\",\n category_id: \"Category UUID\",\n status: \"published | draft (default: draft)\",\n type: \"simple | variable | grouped (default: simple)\",\n description: \"Full description\",\n short_description: \"Summary text\",\n cost_price: \"Cost per unit\",\n pricing_data: \"Pricing tiers JSON\",\n field_values: \"Custom fields JSON (must match category field schema)\",\n featured_image: \"Image URL\",\n image_gallery: \"Array of image URLs\",\n manage_stock: \"Boolean — track inventory\",\n stock_quantity: \"Initial stock (if manage_stock)\",\n },\n response: \"Product\",\n },\n {\n method: \"PATCH\",\n path: \"/v1/stores/:storeId/products/:id\",\n scope: \"write:products\",\n description: \"Update an existing product (partial update)\",\n body: { \"...\": \"Any product fields to update\" },\n response: \"Product\",\n },\n {\n method: \"DELETE\",\n path: \"/v1/stores/:storeId/products/:id\",\n scope: \"write:products\",\n description: \"Archive a product (soft delete)\",\n response: \"{ deleted: true }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/products/:id/variations\",\n scope: \"read:products\",\n description: \"List variations for a product\",\n response: '{ object: \"list\", data: Variation[] }',\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/products/:id/variations\",\n scope: \"write:products\",\n description: \"Create a product variation\",\n body: {\n name: \"Variation name (e.g. 'Small', '1oz')\",\n sku: \"Variation SKU\",\n pricing_data: \"Variation-specific pricing\",\n field_values: \"Variation-specific field values\",\n stock_quantity: \"Initial stock\",\n },\n response: \"Variation\",\n },\n {\n method: \"PATCH\",\n path: \"/v1/stores/:storeId/products/:id/variations/:variationId\",\n scope: \"write:products\",\n description: \"Update a variation\",\n body: { \"...\": \"Any variation fields\" },\n response: \"Variation\",\n },\n {\n method: \"DELETE\",\n path: \"/v1/stores/:storeId/products/:id/variations/:variationId\",\n scope: \"write:products\",\n description: \"Delete a variation\",\n response: \"{ deleted: true }\",\n },\n ],\n },\n\n orders: {\n name: \"Orders\",\n description: \"Order management — list, view, update status, void, and refund\",\n base: \"/v1/stores/:storeId/orders\",\n endpoints: [\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/orders\",\n scope: \"read:orders\",\n description: \"List orders with pagination\",\n params: {\n limit: \"Number (1-100, default 25)\",\n starting_after: \"Order ID cursor\",\n ending_before: \"Order ID cursor\",\n status: \"Filter: pending, confirmed, processing, shipped, delivered, cancelled\",\n customer_id: \"Filter by customer UUID\",\n location_id: \"Filter by location UUID\",\n created_after: \"ISO datetime — filter orders created on or after this date\",\n created_before: \"ISO datetime — filter orders created on or before this date\",\n },\n response: '{ object: \"list\", data: Order[], has_more: boolean }',\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/orders/:id\",\n scope: \"read:orders\",\n description: \"Get a single order with line items\",\n response: \"Order (includes line_items[], customer, payment_details)\",\n },\n {\n method: \"PATCH\",\n path: \"/v1/stores/:storeId/orders/:id\",\n scope: \"write:orders\",\n description: \"Update order status or fields\",\n body: {\n status: \"Order status: pending, confirmed, processing, preparing, packed, ready, shipped, in_transit, delivered, completed, cancelled, refunded\",\n fulfillment_status: \"unfulfilled, partial, fulfilled, cancelled, shipped, in_transit, delivered, ready_for_pickup, ready\",\n tracking_number: \"Shipping tracking number\",\n tracking_url: \"Tracking URL\",\n shipping_service: \"Carrier name\",\n shipping_method: \"Shipping method\",\n staff_notes: \"Internal notes\",\n },\n response: \"Order\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/orders/:id/void\",\n scope: \"write:orders\",\n description: \"Void an unsettled order\",\n response: \"Order (status: voided)\",\n notes: \"Only works on orders where payment has not yet settled\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/orders/:id/refund\",\n scope: \"write:orders\",\n description: \"Refund a settled order\",\n body: { amount: \"Partial refund amount (optional — omit for full refund)\", reason: \"Refund reason\" },\n response: \"Order (includes refund details)\",\n },\n ],\n },\n\n cart: {\n name: \"Cart\",\n description: \"Shopping cart — create, add/update/remove items, view\",\n base: \"/v1/stores/:storeId/cart\",\n endpoints: [\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/cart\",\n scope: \"write:cart\",\n description: \"Create a new shopping cart\",\n body: {\n customer_id: \"Optional customer UUID\",\n location_id: \"Optional location UUID (auto-resolves default)\",\n },\n response: \"Cart { id, items: [], totals }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/cart/:cartId\",\n scope: \"read:cart\",\n description: \"Get cart with all items and computed totals\",\n response: \"Cart { id, items: CartItem[], totals: { subtotal, tax, total } }\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/cart/:cartId/items\",\n scope: \"write:cart\",\n description: \"Add an item to the cart\",\n body: {\n product_id: \"Required. Product UUID\",\n quantity: \"Required. Number of units\",\n variation_id: \"Variation UUID (for variable products)\",\n tier: \"Pricing tier name (e.g. 'retail', 'wholesale')\",\n unit_price: \"Override price (optional — auto-resolved from pricing_data)\",\n },\n response: 'CartItem { object: \"cart_item\", id, product_id, product_name, quantity, unit_price, line_total }',\n },\n {\n method: \"PATCH\",\n path: \"/v1/stores/:storeId/cart/:cartId/items/:itemId\",\n scope: \"write:cart\",\n description: \"Update cart item quantity\",\n body: { quantity: \"New quantity (must be >= 1)\" },\n response: 'CartItem { object: \"cart_item\", id, quantity, unit_price, line_total }',\n },\n {\n method: \"DELETE\",\n path: \"/v1/stores/:storeId/cart/:cartId/items/:itemId\",\n scope: \"write:cart\",\n description: \"Remove an item from the cart\",\n response: '{ object: \"cart_item\", id, deleted: true }',\n },\n ],\n },\n\n checkout: {\n name: \"Checkout\",\n description: \"E-commerce checkout and POS payment intents\",\n base: \"/v1/stores/:storeId/checkout\",\n endpoints: [\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/checkout\",\n scope: \"write:checkout\",\n description: \"Convert a cart into an order (e-commerce checkout)\",\n body: {\n cart_id: \"Required. Cart UUID\",\n customer_email: \"Customer email (auto-creates customer if not found)\",\n customer_name: \"Customer name\",\n customer_id: \"Existing customer UUID\",\n payment_method: \"Payment method identifier\",\n opaque_data: \"Authorize.Net card token { dataDescriptor, dataValue }\",\n bill_to: \"Billing address { firstName, lastName, address, city, state, zip, country }\",\n ship_to: \"Shipping address (same shape as bill_to)\",\n loyalty_code: \"Loyalty/discount code\",\n loyalty_points_redeemed: \"Points to redeem (integer >= 0)\",\n loyalty_discount_amount: \"Discount amount from loyalty (number >= 0)\",\n },\n response: 'Order { object: \"order\", id, order_number, status: \"completed\" }',\n notes: \"Cart is consumed — cannot be reused after successful checkout. Supports idempotency (returns existing order on replay).\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/checkout/intents\",\n scope: \"write:checkout\",\n description: \"Create a payment intent (POS terminal flow)\",\n body: {\n cartItems: \"Required. Array of cart items\",\n paymentMethod: \"Payment method (card, cash, etc.)\",\n locationId: \"Location UUID\",\n registerId: \"POS register ID\",\n sessionId: \"POS session ID\",\n customerId: \"Customer UUID\",\n customerName: \"Customer name\",\n userId: \"Staff user UUID\",\n tipAmount: \"Tip amount\",\n cashTendered: \"Cash amount given (for cash payments)\",\n changeGiven: \"Change returned\",\n idempotencyKey: \"Idempotency key for safe retries\",\n },\n response: \"PaymentIntent { id, status: 'created', amount }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/checkout/intents/:id\",\n scope: \"read:checkout\",\n description: \"Get payment intent status\",\n response: \"PaymentIntent { id, status, amount, created_at }\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/checkout/intents/:id/capture\",\n scope: \"write:checkout\",\n description: \"Capture an authorized payment\",\n response: \"PaymentIntent { status: 'captured' }\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/checkout/intents/:id/cancel\",\n scope: \"write:checkout\",\n description: \"Cancel a payment intent\",\n response: \"PaymentIntent { status: 'cancelled' }\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/checkout/intents/:id/charge\",\n scope: \"write:checkout\",\n description: \"Charge via Dejavoo POS terminal\",\n body: { terminal_id: \"Dejavoo terminal ID\" },\n response: \"PaymentIntent (with terminal response)\",\n notes: \"POS-specific — sends charge request to physical terminal\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/checkout/intents/:id/abort\",\n scope: \"write:checkout\",\n description: \"Abort an in-progress terminal charge\",\n response: \"PaymentIntent { status: 'aborted' }\",\n },\n ],\n },\n\n customers: {\n name: \"Customers\",\n description: \"Customer CRM — CRUD and search\",\n base: \"/v1/stores/:storeId/customers\",\n endpoints: [\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/customers\",\n scope: \"read:customers\",\n description: \"List customers with pagination and search\",\n params: {\n limit: \"Number (1-100, default 25)\",\n starting_after: \"Customer ID cursor\",\n search: \"Search by name, email, or phone\",\n },\n response: '{ object: \"list\", data: Customer[], has_more: boolean }',\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/customers/:id\",\n scope: \"read:customers\",\n description: \"Get a single customer with full profile\",\n response: \"Customer (includes loyalty_points, total_spent, total_orders)\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/customers\",\n scope: \"write:customers\",\n description: \"Create a new customer\",\n body: {\n first_name: \"Required\",\n last_name: \"Required\",\n email: \"Email address\",\n phone: \"Phone number\",\n date_of_birth: \"YYYY-MM-DD\",\n },\n response: \"Customer\",\n },\n {\n method: \"PATCH\",\n path: \"/v1/stores/:storeId/customers/:id\",\n scope: \"write:customers\",\n description: \"Update a customer\",\n body: { \"...\": \"Any customer fields\" },\n response: \"Customer\",\n },\n ],\n },\n\n inventory: {\n name: \"Inventory\",\n description: \"Inventory levels, summaries, adjustments, and transfers\",\n base: \"/v1/stores/:storeId/inventory\",\n endpoints: [\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/inventory\",\n scope: \"read:inventory\",\n description: \"List inventory levels across locations\",\n params: {\n limit: \"Number (1-100, default 25)\",\n starting_after: \"Cursor\",\n location_id: \"Filter by location UUID\",\n product_id: \"Filter by product UUID\",\n low_stock: \"Set to 'true' to filter to low_stock or out_of_stock items only\",\n },\n response: '{ object: \"list\", data: InventoryLevel[] }',\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/inventory/summary\",\n scope: \"read:inventory\",\n description: \"Inventory summary grouped by location\",\n response: \"{ locations: [{ id, name, total_products, total_units, total_value }] }\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/inventory/adjust\",\n scope: \"write:inventory\",\n description: \"Adjust inventory quantity for a product at a location\",\n body: {\n product_id: \"Required. Product UUID\",\n location_id: \"Required. Location UUID\",\n quantity_change: \"Required. Number (+/-) to adjust by\",\n reason: \"Reason for adjustment (default: 'api_adjustment')\",\n },\n response: '{ object: \"inventory_adjustment\", product_id, location_id, quantity_change, new_quantity, reason }',\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/inventory/transfer\",\n scope: \"write:inventory\",\n description: \"Transfer inventory between locations\",\n body: {\n product_id: \"Required. Product UUID\",\n from_location_id: \"Required. Source location UUID\",\n to_location_id: \"Required. Destination location UUID\",\n quantity: \"Required. Number of units to transfer\",\n },\n response: \"{ from: InventoryLevel, to: InventoryLevel }\",\n },\n ],\n },\n\n locations: {\n name: \"Locations\",\n description: \"Store locations — list and view\",\n base: \"/v1/stores/:storeId/locations\",\n endpoints: [\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/locations\",\n scope: \"read:locations\",\n description: \"List all store locations\",\n response: '{ object: \"list\", data: Location[] }',\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/locations/:id\",\n scope: \"read:locations\",\n description: \"Get a single location\",\n response: \"Location { id, name, address, city, state, zip, type, is_active }\",\n },\n ],\n },\n\n analytics: {\n name: \"Analytics\",\n description: \"Sales, inventory, traffic, customer, and product analytics\",\n base: \"/v1/stores/:storeId/analytics\",\n endpoints: [\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/analytics/sales\",\n scope: \"read:analytics\",\n description: \"Sales analytics summary\",\n params: {\n start_date: \"YYYY-MM-DD (default: 30 days ago)\",\n end_date: \"YYYY-MM-DD (default: today)\",\n location_id: \"Filter by location UUID\",\n },\n response: '{ object: \"analytics.sales\", period: { start_date, end_date }, total_orders, completed_orders, total_revenue, total_tax, total_discount, average_order_value, gross_sales, net_sales, total_profit, profit_margin, unique_customers }',\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/analytics/sales/daily\",\n scope: \"read:analytics\",\n description: \"Daily sales breakdown\",\n params: { start_date: \"YYYY-MM-DD (default: 30 days ago)\", end_date: \"YYYY-MM-DD (default: today)\", location_id: \"Filter by location UUID\" },\n response: \"{ days: [{ date, revenue, orders, average_order_value }] }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/analytics/sales/weekly\",\n scope: \"read:analytics\",\n description: \"Weekly sales breakdown\",\n params: { start_date: \"YYYY-MM-DD (default: 30 days ago)\", end_date: \"YYYY-MM-DD (default: today)\", location_id: \"Filter by location UUID\" },\n response: \"{ weeks: [{ week_start, revenue, orders }] }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/analytics/inventory\",\n scope: \"read:analytics\",\n description: \"Inventory analytics — stock levels and velocity\",\n response: \"{ total_value, low_stock[], out_of_stock[], velocity[] }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/analytics/traffic\",\n scope: \"read:analytics\",\n description: \"Storefront traffic analytics\",\n response: \"{ sessions, page_views, bounce_rate, avg_session_duration }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/analytics/funnel\",\n scope: \"read:analytics\",\n description: \"Conversion funnel analytics\",\n response: \"{ steps: [{ name, count, conversion_rate }] }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/analytics/customers\",\n scope: \"read:analytics\",\n description: \"Customer analytics — segments, retention, lifetime value\",\n response: \"{ total_customers, new_customers, returning, avg_ltv, segments[] }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/analytics/products\",\n scope: \"read:analytics\",\n description: \"Per-product performance analytics\",\n params: { limit: \"Number of products\", category_id: \"Filter by category\" },\n response: \"{ products: [{ id, name, revenue, units_sold, avg_price }] }\",\n },\n ],\n },\n\n storefront: {\n name: \"Storefront\",\n description: \"Storefront sessions, event tracking, and customer OTP authentication\",\n base: \"/v1/stores/:storeId/storefront\",\n endpoints: [\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/storefront/sessions\",\n scope: \"read:storefront\",\n description: \"List storefront sessions\",\n response: '{ object: \"list\", data: Session[] }',\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/storefront/sessions/:id\",\n scope: \"read:storefront\",\n description: \"Get a single session\",\n response: \"Session { id, customer_id, events[], created_at }\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/storefront/sessions\",\n scope: \"write:storefront\",\n description: \"Create a storefront session\",\n body: { customer_id: \"Optional customer UUID\", metadata: \"Optional session metadata\" },\n response: \"Session\",\n },\n {\n method: \"PATCH\",\n path: \"/v1/stores/:storeId/storefront/sessions/:id\",\n scope: \"write:storefront\",\n description: \"Update a session\",\n body: { metadata: \"Updated metadata\" },\n response: \"Session\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/storefront/events\",\n scope: \"write:storefront\",\n description: \"Track a storefront event\",\n body: {\n session_id: \"Session UUID\",\n event_type: \"Event name (e.g. page_view, add_to_cart, purchase)\",\n event_data: \"Event payload JSON\",\n },\n response: \"{ tracked: true }\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/storefront/auth/send-code\",\n scope: \"write:storefront\",\n description: \"Send passwordless OTP to a customer\",\n body: { email: \"Customer email OR\", phone: \"Customer phone\" },\n response: \"{ sent: true }\",\n notes: \"Rate-limited to prevent abuse. Code expires in 10 minutes.\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/storefront/auth/verify-code\",\n scope: \"write:storefront\",\n description: \"Verify OTP code and authenticate customer\",\n body: { email: \"Customer email OR phone\", code: \"6-digit OTP code\" },\n response: \"{ customer_id, session_token, expires_at }\",\n },\n ],\n },\n\n coa: {\n name: \"COA (Certificates of Analysis)\",\n description: \"Lab test results — list, verify, view, and embed\",\n base: \"/v1/stores/:storeId/coa\",\n endpoints: [\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/coa\",\n scope: \"read:coa\",\n description: \"List COAs (scoped to client)\",\n params: { limit: \"Number (1-100)\", starting_after: \"Cursor\" },\n response: '{ object: \"list\", data: COA[] }',\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/coa/verify\",\n scope: \"read:coa\",\n description: \"Verify a COA by sample ID\",\n params: { sample_id: \"Lab sample identifier\" },\n response: \"COA (with verification status)\",\n notes: \"Public-facing verification — can be linked from product pages\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/coa/:id\",\n scope: \"read:coa\",\n description: \"Get a single COA with full lab data\",\n response: \"COA { id, product_name, batch_number, sample_id, cannabinoids, terpenes, ... }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/coa/:id/embed\",\n scope: \"read:coa\",\n description: \"Get embeddable COA data for iframes or widgets\",\n response: \"{ html: string, data: COA }\",\n notes: \"Use for embedding lab results on product detail pages\",\n },\n ],\n },\n\n portal: {\n name: \"Customer Portal\",\n description: \"B2B portal — authentication, profiles, and documents\",\n base: \"/v1/stores/:storeId/portal\",\n endpoints: [\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/portal/auth/send-code\",\n scope: \"write:portal\",\n description: \"Send portal login OTP\",\n body: { email: \"Customer email\" },\n response: \"{ sent: true }\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/portal/auth/verify\",\n scope: \"write:portal\",\n description: \"Verify OTP and get portal session\",\n body: { email: \"Customer email\", code: \"OTP code\" },\n response: \"{ customer_id, token, expires_at }\",\n },\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/portal/auth/refresh\",\n scope: \"write:portal\",\n description: \"Refresh portal session token\",\n body: { token: \"Current session token\" },\n response: \"{ token, expires_at }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/portal/stores\",\n scope: \"read:portal\",\n description: \"Get stores the customer belongs to\",\n response: \"{ stores: Store[] }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/portal/profiles\",\n scope: \"read:portal\",\n description: \"List customer profiles\",\n response: \"{ profiles: Profile[] }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/portal/documents\",\n scope: \"read:portal\",\n description: \"List customer documents\",\n response: '{ object: \"list\", data: Document[] }',\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/portal/documents/:id\",\n scope: \"read:portal\",\n description: \"Get a single document\",\n response: \"Document { id, name, type, url, created_at }\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/portal/api-key\",\n scope: \"read:portal\",\n description: \"Get customer's API key for portal access\",\n response: \"{ api_key, scopes[], expires_at }\",\n },\n ],\n },\n\n webhooks: {\n name: \"Webhooks\",\n description: \"Inbound webhook ingestion — receives events from external services (HMAC-authenticated, no API key required)\",\n base: \"/v1/webhooks\",\n endpoints: [\n {\n method: \"POST\",\n path: \"/v1/webhooks/:slug\",\n scope: \"(none — HMAC signature)\",\n description: \"Receive an inbound webhook event\",\n body: {\n timestamp: \"Unix timestamp (optional — replay protection, rejects if > 5 min old)\",\n event: \"Event type identifier (or 'type' field)\",\n \"...\": \"All other fields pass through to webhook_events table\",\n },\n response: '{ object: \"webhook_event\", received: true, event_type: string }',\n notes: \"Authenticated via HMAC sha256 signature from the webhook endpoint's signing_secret. No API key required.\",\n },\n ],\n },\n\n agent: {\n name: \"Agent\",\n description: \"AI agent chat — SSE streaming proxy to the internal whale-agent service\",\n base: \"/v1/stores/:storeId/agent\",\n endpoints: [\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/agent/chat\",\n scope: \"write:agent\",\n description: \"Stream an AI agent response via Server-Sent Events\",\n body: {\n message: \"User message (string — either this or messages required)\",\n messages: \"Array of { role: 'user'|'assistant'|'system', content: string }\",\n conversation_id: \"Conversation ID for multi-turn context\",\n context: \"Additional context object passed to the agent\",\n },\n response: \"SSE stream (text/event-stream) — streamed tokens from the agent\",\n notes: \"Proxies to internal whale-agent service. 120s timeout (504 on timeout, 502 on connection failure).\",\n },\n ],\n },\n\n telemetry: {\n name: \"Telemetry\",\n description: \"SDK telemetry ingestion — errors, analytics events, Web Vitals, and AI/LLM call tracking\",\n base: \"/v1/stores/:storeId/telemetry\",\n endpoints: [\n {\n method: \"POST\",\n path: \"/v1/stores/:storeId/telemetry/ingest\",\n scope: \"write:telemetry\",\n description: \"Ingest a batch of telemetry data (errors, events, vitals, AI calls)\",\n body: {\n session: \"Required. { session_id, visitor_id, started_at, page_url, referrer, user_agent, screen_width, screen_height, device, language, utm_* }\",\n user: \"Optional. { user_id, email?, name?, traits? }\",\n errors: \"Array (max 50). Each: { error_type, error_message, fingerprint, occurred_at, severity?, stack_trace?, breadcrumbs? }\",\n events: \"Array (max 100). Each: { event_name, properties, timestamp }\",\n vitals: \"Array (max 20). Each: { name: CLS|FID|LCP|INP|TTFB|FCP, value, rating, timestamp }\",\n ai_calls: \"Array (max 50). Each: { model, provider?, prompt_tokens?, completion_tokens?, cost?, duration_ms?, status?, agent_id? }\",\n },\n response: '{ object: \"telemetry_batch\", accepted: { errors, events, vitals, ai_calls }, session_id }',\n notes: \"Used by @neowhale/telemetry SDK. Errors go to ClickHouse, events to Supabase, vitals and AI calls to ClickHouse.\",\n },\n {\n method: \"POST\",\n path: \"/v1/native/telemetry\",\n scope: \"(JWT-authenticated)\",\n description: \"Ingest telemetry from native (macOS/iOS) apps\",\n body: {\n \"...\": \"Same batch format as /telemetry/ingest\",\n },\n response: '{ object: \"telemetry_batch\", accepted: { ... } }',\n notes: \"Authenticated via JWT token (not API key). Bypasses standard API key middleware.\",\n },\n ],\n },\n\n media: {\n name: \"Media\",\n description: \"Image and video proxy with on-the-fly transformations (HMAC-signed, no API key required)\",\n base: \"/v1/stores/:storeId\",\n endpoints: [\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/media\",\n scope: \"(HMAC signature via 's' query param)\",\n description: \"Proxy and transform an image (resize, format conversion, quality)\",\n params: {\n url: \"Required. Base64url-encoded source image URL\",\n w: \"Width: 64, 96, 128, 256, 384, 640, 828, 1080, 1280, or 1920\",\n q: \"Quality 1-100 (default: 80)\",\n f: \"Format: avif, webp, jpeg, png (default: webp)\",\n s: \"Required. HMAC signature\",\n },\n response: \"Binary image data with Cache-Control: public, max-age=86400, s-maxage=604800, immutable\",\n notes: \"Max 8 concurrent transforms, queues for 5s then returns 503 with Retry-After: 2. Use WhaleClient.signMedia() to generate signatures.\",\n },\n {\n method: \"GET\",\n path: \"/v1/stores/:storeId/video\",\n scope: \"(HMAC signature via 's' query param)\",\n description: \"Stream a video with HTTP range request support\",\n params: {\n url: \"Required. Base64url-encoded source video URL\",\n s: \"Required. HMAC signature\",\n },\n response: \"Streamed video (supports Accept-Ranges: bytes for seeking). Auto-resolves optimized -web.mp4 variants.\",\n notes: \"Cache-Control: public, max-age=86400, s-maxage=604800, immutable. X-Video-Source header indicates original vs optimized.\",\n },\n ],\n },\n\n infrastructure: {\n name: \"Infrastructure\",\n description: \"Health checks, OpenAPI spec, and interactive documentation\",\n base: \"\",\n endpoints: [\n {\n method: \"GET\",\n path: \"/health\",\n scope: \"(no auth)\",\n description: \"Health check — simple or detailed with dependency status\",\n params: {\n detailed: \"Set to 'true' to check Supabase and ClickHouse dependencies\",\n },\n response: '{ status: \"ok\"|\"degraded\"|\"down\", version: \"2026-02-20\", uptime_s?, dependencies?: { supabase, clickhouse } }',\n notes: \"Simple response: 200 always. Detailed: 503 if both dependencies are down. Cached for 10s.\",\n },\n {\n method: \"GET\",\n path: \"/openapi.json\",\n scope: \"(no auth)\",\n description: \"OpenAPI 3.0 specification\",\n response: \"OpenAPI JSON spec\",\n },\n {\n method: \"GET\",\n path: \"/docs\",\n scope: \"(no auth)\",\n description: \"Interactive API documentation page\",\n response: \"HTML page\",\n },\n ],\n },\n};\n\n// ---------------------------------------------------------------------------\n// Auth reference\n// ---------------------------------------------------------------------------\n\nconst AUTH_GUIDE = {\n base_url: GATEWAY_BASE_URL,\n api_version: \"2026-02-20\",\n api_version_header: \"X-API-Version\",\n authentication: {\n method: \"API Key via header\",\n header: \"x-api-key\",\n key_format: \"wk_live_<32chars> (production) or wk_test_<32chars> (sandbox)\",\n how_to_get: \"Use the api_keys tool (action: generate) or the WhaleTools dashboard under Settings → API Keys\",\n },\n scopes: {\n wildcard: \"* (full access), read:* (all reads), write:* (all writes)\",\n per_resource: [\n \"read:products\", \"write:products\",\n \"read:orders\", \"write:orders\",\n \"read:customers\", \"write:customers\",\n \"read:inventory\", \"write:inventory\",\n \"read:locations\",\n \"read:analytics\",\n \"read:cart\", \"write:cart\",\n \"read:checkout\", \"write:checkout\",\n \"read:storefront\", \"write:storefront\",\n \"read:portal\", \"write:portal\",\n \"read:coa\",\n ],\n internal: [\"write:agent\", \"write:telemetry\"],\n internal_note: \"Agent chat and telemetry scopes are used by WhaleTools SDKs and internal services. Media/video endpoints use HMAC signatures instead of API keys.\",\n },\n rate_limits: {\n default_per_minute: 60,\n headers: \"X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset\",\n exceeded_status: 429,\n retry_header: \"Retry-After (seconds)\",\n },\n pagination: {\n style: \"Cursor-based\",\n params: \"limit (1-100, default 25), starting_after (ID), ending_before (ID)\",\n response: '{ object: \"list\", data: [...], has_more: true|false, url: \"...\" }',\n },\n error_format: {\n shape: '{ error: { type, message, code, param?, request_id? } }',\n types: {\n authentication_error: \"401 — invalid_api_key (also returned for missing key)\",\n permission_error: \"403 — insufficient_scope, store_mismatch\",\n not_found_error: \"404 — resource_not_found\",\n invalid_request_error: \"400/405/409 — validation_error, method_not_allowed, conflict\",\n rate_limit_error: \"429 — rate_limit_exceeded, auth_rate_limited (too many auth attempts)\",\n api_error: \"500 — internal_error\",\n },\n },\n security_headers: {\n cors: \"Configurable per-store. Default: allow all origins\",\n idempotency: \"Idempotency-Key header supported on all POST/PATCH requests\",\n versioning: \"X-API-Version header (optional — latest version used if omitted)\",\n },\n url_pattern: \"All endpoints: /v1/stores/:storeId/<resource>\",\n note: \"Replace :storeId with your store UUID. Get it from the WhaleTools dashboard or the store tool.\",\n};\n\n// ---------------------------------------------------------------------------\n// Code examples by section\n// ---------------------------------------------------------------------------\n\nfunction getExamples(section: string): { raw_fetch: string; nextjs_ssr: string; react_client: string } | null {\n const BASE = GATEWAY_BASE_URL;\n const storeVar = \"${STORE_ID}\";\n const keyVar = \"${API_KEY}\";\n\n const templates: Record<string, { raw_fetch: string; nextjs_ssr: string; react_client: string }> = {\n products: {\n raw_fetch: `// List products\nconst res = await fetch(\"${BASE}/v1/stores/${storeVar}/products?limit=25&status=published\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n});\nconst { data: products, has_more } = await res.json();\n\n// Get single product\nconst product = await fetch(\"${BASE}/v1/stores/${storeVar}/products/\\${productId}\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());`,\n\n nextjs_ssr: `// app/products/page.tsx (Next.js App Router — Server Component)\nconst API_KEY = process.env.WHALE_API_KEY!;\nconst STORE_ID = process.env.WHALE_STORE_ID!;\nconst BASE = \"${BASE}\";\n\nasync function getProducts(page?: string) {\n const params = new URLSearchParams({ limit: \"25\", status: \"published\" });\n if (page) params.set(\"starting_after\", page);\n\n const res = await fetch(\\`\\${BASE}/v1/stores/\\${STORE_ID}/products?\\${params}\\`, {\n headers: { \"x-api-key\": API_KEY },\n next: { revalidate: 60 }, // ISR: revalidate every 60s\n });\n\n if (!res.ok) throw new Error(\"Failed to fetch products\");\n return res.json();\n}\n\nexport default async function ProductsPage({ searchParams }: { searchParams: { after?: string } }) {\n const { data: products, has_more } = await getProducts(searchParams.after);\n\n return (\n <div className=\"grid grid-cols-3 gap-4\">\n {products.map((p: any) => (\n <div key={p.id} className=\"border rounded p-4\">\n {p.featured_image && <img src={p.featured_image} alt={p.name} />}\n <h2>{p.name}</h2>\n <p>{p.pricing_data?.tiers?.[0]?.price ? \\`$\\${p.pricing_data.tiers[0].price}\\` : \"Contact for pricing\"}</p>\n </div>\n ))}\n {has_more && <a href={\\`?after=\\${products.at(-1)?.id}\\`}>Next page →</a>}\n </div>\n );\n}`,\n\n react_client: `// hooks/useProducts.ts (React client-side)\nimport { useState, useEffect } from \"react\";\n\nconst BASE = \"${BASE}\";\n\nexport function useProducts(storeId: string, apiKey: string) {\n const [products, setProducts] = useState([]);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n fetch(\\`\\${BASE}/v1/stores/\\${storeId}/products?status=published&limit=50\\`, {\n headers: { \"x-api-key\": apiKey },\n })\n .then(r => r.json())\n .then(({ data }) => setProducts(data))\n .finally(() => setLoading(false));\n }, [storeId, apiKey]);\n\n return { products, loading };\n}`,\n },\n\n cart: {\n raw_fetch: `// Create cart\nconst cart = await fetch(\"${BASE}/v1/stores/${storeVar}/cart\", {\n method: \"POST\",\n headers: { \"x-api-key\": \"${keyVar}\", \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ customer_id: customerId })\n}).then(r => r.json());\n\n// Add item\nawait fetch(\"${BASE}/v1/stores/${storeVar}/cart/\\${cart.id}/items\", {\n method: \"POST\",\n headers: { \"x-api-key\": \"${keyVar}\", \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ product_id: \"...\", quantity: 2 })\n}).then(r => r.json());\n\n// View cart\nconst fullCart = await fetch(\"${BASE}/v1/stores/${storeVar}/cart/\\${cart.id}\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());`,\n\n nextjs_ssr: `// app/api/cart/route.ts (Next.js Route Handler — server-side proxy)\nimport { NextRequest, NextResponse } from \"next/server\";\n\nconst API_KEY = process.env.WHALE_API_KEY!;\nconst STORE_ID = process.env.WHALE_STORE_ID!;\nconst BASE = \"${BASE}\";\n\nexport async function POST(req: NextRequest) {\n const body = await req.json();\n const res = await fetch(\\`\\${BASE}/v1/stores/\\${STORE_ID}/cart\\`, {\n method: \"POST\",\n headers: { \"x-api-key\": API_KEY, \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n return NextResponse.json(await res.json());\n}`,\n\n react_client: `// hooks/useCart.ts\nimport { useState, useCallback } from \"react\";\n\nexport function useCart(storeId: string, apiKey: string) {\n const [cart, setCart] = useState<any>(null);\n const BASE = \"${BASE}\";\n const headers = { \"x-api-key\": apiKey, \"Content-Type\": \"application/json\" };\n\n const createCart = useCallback(async () => {\n const res = await fetch(\\`\\${BASE}/v1/stores/\\${storeId}/cart\\`, {\n method: \"POST\", headers,\n });\n const data = await res.json();\n setCart(data);\n return data;\n }, [storeId]);\n\n const addItem = useCallback(async (productId: string, quantity: number) => {\n if (!cart) return;\n const res = await fetch(\\`\\${BASE}/v1/stores/\\${storeId}/cart/\\${cart.id}/items\\`, {\n method: \"POST\", headers,\n body: JSON.stringify({ product_id: productId, quantity }),\n });\n const data = await res.json();\n setCart(data);\n return data;\n }, [storeId, cart]);\n\n const removeItem = useCallback(async (itemId: string) => {\n if (!cart) return;\n const res = await fetch(\\`\\${BASE}/v1/stores/\\${storeId}/cart/\\${cart.id}/items/\\${itemId}\\`, {\n method: \"DELETE\", headers,\n });\n const data = await res.json();\n setCart(data);\n return data;\n }, [storeId, cart]);\n\n return { cart, createCart, addItem, removeItem };\n}`,\n },\n\n checkout: {\n raw_fetch: `// E-commerce checkout (convert cart to order)\nconst order = await fetch(\"${BASE}/v1/stores/${storeVar}/checkout\", {\n method: \"POST\",\n headers: { \"x-api-key\": \"${keyVar}\", \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n cart_id: cartId,\n payment_method: \"card\",\n shipping_address: { street: \"123 Main St\", city: \"Charlotte\", state: \"NC\", zip: \"28202\" }\n })\n}).then(r => r.json());\n\n// POS payment intent flow\nconst intent = await fetch(\"${BASE}/v1/stores/${storeVar}/checkout/intents\", {\n method: \"POST\",\n headers: { \"x-api-key\": \"${keyVar}\", \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ cart_id: cartId, amount: 49.99 })\n}).then(r => r.json());\n\n// Capture after authorization\nawait fetch(\"${BASE}/v1/stores/${storeVar}/checkout/intents/\\${intent.id}/capture\", {\n method: \"POST\",\n headers: { \"x-api-key\": \"${keyVar}\" }\n});`,\n\n nextjs_ssr: `// app/api/checkout/route.ts (Server-side checkout — keep API key secret)\nimport { NextRequest, NextResponse } from \"next/server\";\n\nconst API_KEY = process.env.WHALE_API_KEY!;\nconst STORE_ID = process.env.WHALE_STORE_ID!;\nconst BASE = \"${BASE}\";\n\nexport async function POST(req: NextRequest) {\n const { cart_id, shipping_address } = await req.json();\n\n const res = await fetch(\\`\\${BASE}/v1/stores/\\${STORE_ID}/checkout\\`, {\n method: \"POST\",\n headers: { \"x-api-key\": API_KEY, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ cart_id, shipping_address }),\n });\n\n if (!res.ok) {\n const err = await res.json();\n return NextResponse.json(err, { status: res.status });\n }\n\n return NextResponse.json(await res.json());\n}`,\n\n react_client: `// components/CheckoutButton.tsx\n\"use client\";\nimport { useState } from \"react\";\n\nexport function CheckoutButton({ cartId }: { cartId: string }) {\n const [loading, setLoading] = useState(false);\n\n const handleCheckout = async () => {\n setLoading(true);\n try {\n // Call YOUR server-side route (never expose API key to client)\n const res = await fetch(\"/api/checkout\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ cart_id: cartId }),\n });\n const order = await res.json();\n if (order.id) window.location.href = \\`/orders/\\${order.id}/confirmation\\`;\n } finally {\n setLoading(false);\n }\n };\n\n return <button onClick={handleCheckout} disabled={loading}>{loading ? \"Processing...\" : \"Checkout\"}</button>;\n}`,\n },\n\n orders: {\n raw_fetch: `// List orders\nconst { data: orders } = await fetch(\"${BASE}/v1/stores/${storeVar}/orders?limit=25\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());\n\n// Get order details\nconst order = await fetch(\"${BASE}/v1/stores/${storeVar}/orders/\\${orderId}\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());\n\n// Update order status\nawait fetch(\"${BASE}/v1/stores/${storeVar}/orders/\\${orderId}\", {\n method: \"PATCH\",\n headers: { \"x-api-key\": \"${keyVar}\", \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ status: \"shipped\" })\n});`,\n\n nextjs_ssr: `// app/orders/page.tsx (Server Component)\nasync function getOrders() {\n const res = await fetch(\n \\`${BASE}/v1/stores/\\${process.env.WHALE_STORE_ID}/orders?limit=50\\`,\n { headers: { \"x-api-key\": process.env.WHALE_API_KEY! }, next: { revalidate: 30 } }\n );\n return res.json();\n}\n\nexport default async function OrdersPage() {\n const { data: orders } = await getOrders();\n return (\n <table>\n <thead><tr><th>Order</th><th>Status</th><th>Total</th><th>Date</th></tr></thead>\n <tbody>\n {orders.map((o: any) => (\n <tr key={o.id}>\n <td><a href={\\`/orders/\\${o.id}\\`}>{o.order_number}</a></td>\n <td>{o.status}</td>\n <td>\\${o.total}</td>\n <td>{new Date(o.created_at).toLocaleDateString()}</td>\n </tr>\n ))}\n </tbody>\n </table>\n );\n}`,\n\n react_client: `// hooks/useOrders.ts\nimport useSWR from \"swr\";\n\nconst fetcher = (url: string) => fetch(url).then(r => r.json());\n\nexport function useOrders() {\n // Fetch via your own API route (keeps API key server-side)\n const { data, error, isLoading } = useSWR(\"/api/orders\", fetcher);\n return { orders: data?.data ?? [], error, isLoading };\n}`,\n },\n\n customers: {\n raw_fetch: `// Search customers\nconst { data: customers } = await fetch(\"${BASE}/v1/stores/${storeVar}/customers?search=john\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());\n\n// Create customer\nconst customer = await fetch(\"${BASE}/v1/stores/${storeVar}/customers\", {\n method: \"POST\",\n headers: { \"x-api-key\": \"${keyVar}\", \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ first_name: \"John\", last_name: \"Doe\", email: \"john@example.com\" })\n}).then(r => r.json());`,\n\n nextjs_ssr: `// app/api/customers/route.ts\nimport { NextRequest, NextResponse } from \"next/server\";\n\nconst headers = { \"x-api-key\": process.env.WHALE_API_KEY! };\nconst base = \\`${BASE}/v1/stores/\\${process.env.WHALE_STORE_ID}\\`;\n\nexport async function GET(req: NextRequest) {\n const search = req.nextUrl.searchParams.get(\"q\") || \"\";\n const res = await fetch(\\`\\${base}/customers?search=\\${encodeURIComponent(search)}\\`, { headers });\n return NextResponse.json(await res.json());\n}`,\n\n react_client: `// hooks/useCustomers.ts\nimport useSWR from \"swr\";\n\nexport function useCustomers(search: string) {\n const { data, isLoading } = useSWR(\n search ? \\`/api/customers?q=\\${encodeURIComponent(search)}\\` : null,\n (url) => fetch(url).then(r => r.json())\n );\n return { customers: data?.data ?? [], isLoading };\n}`,\n },\n\n inventory: {\n raw_fetch: `// Get inventory summary\nconst summary = await fetch(\"${BASE}/v1/stores/${storeVar}/inventory/summary\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());\n\n// Adjust stock\nawait fetch(\"${BASE}/v1/stores/${storeVar}/inventory/adjust\", {\n method: \"POST\",\n headers: { \"x-api-key\": \"${keyVar}\", \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ product_id: \"...\", location_id: \"...\", adjustment: -5, reason: \"Sold offline\" })\n});`,\n\n nextjs_ssr: `// app/inventory/page.tsx\nasync function getInventory() {\n const res = await fetch(\n \\`${BASE}/v1/stores/\\${process.env.WHALE_STORE_ID}/inventory/summary\\`,\n { headers: { \"x-api-key\": process.env.WHALE_API_KEY! }, next: { revalidate: 60 } }\n );\n return res.json();\n}\n\nexport default async function InventoryPage() {\n const summary = await getInventory();\n return (\n <div>\n {summary.locations.map((loc: any) => (\n <div key={loc.id}>\n <h3>{loc.name}</h3>\n <p>{loc.total_products} products, {loc.total_units} units</p>\n </div>\n ))}\n </div>\n );\n}`,\n\n react_client: `// hooks/useInventory.ts\nimport useSWR from \"swr\";\n\nexport function useInventorySummary() {\n const { data, isLoading } = useSWR(\"/api/inventory/summary\", (url) => fetch(url).then(r => r.json()));\n return { summary: data, isLoading };\n}`,\n },\n\n analytics: {\n raw_fetch: `// Sales analytics\nconst sales = await fetch(\"${BASE}/v1/stores/${storeVar}/analytics/sales?period=last_30\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());\n\n// Daily breakdown\nconst daily = await fetch(\"${BASE}/v1/stores/${storeVar}/analytics/sales/daily?period=last_7\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());\n\n// Product performance\nconst topProducts = await fetch(\"${BASE}/v1/stores/${storeVar}/analytics/products?limit=10\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());`,\n\n nextjs_ssr: `// app/dashboard/page.tsx\nasync function getDashboard() {\n const base = \\`${BASE}/v1/stores/\\${process.env.WHALE_STORE_ID}/analytics\\`;\n const headers = { \"x-api-key\": process.env.WHALE_API_KEY! };\n const opts = { headers, next: { revalidate: 300 } as any };\n\n const [sales, daily, products] = await Promise.all([\n fetch(\\`\\${base}/sales?period=last_30\\`, opts).then(r => r.json()),\n fetch(\\`\\${base}/sales/daily?period=last_7\\`, opts).then(r => r.json()),\n fetch(\\`\\${base}/products?limit=5\\`, opts).then(r => r.json()),\n ]);\n\n return { sales, daily, products };\n}\n\nexport default async function DashboardPage() {\n const { sales, daily, products } = await getDashboard();\n return (\n <div>\n <h1>Dashboard</h1>\n <div className=\"grid grid-cols-3 gap-4\">\n <div>Revenue: \\${sales.total_revenue}</div>\n <div>Orders: {sales.total_orders}</div>\n <div>AOV: \\${sales.average_order_value}</div>\n </div>\n </div>\n );\n}`,\n\n react_client: `// hooks/useAnalytics.ts\nimport useSWR from \"swr\";\n\nexport function useSalesAnalytics(period = \"last_30\") {\n const { data, isLoading } = useSWR(\n \\`/api/analytics/sales?period=\\${period}\\`,\n (url) => fetch(url).then(r => r.json())\n );\n return { sales: data, isLoading };\n}`,\n },\n\n locations: {\n raw_fetch: `// List all locations\nconst { data: locations } = await fetch(\"${BASE}/v1/stores/${storeVar}/locations\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());`,\n\n nextjs_ssr: `// lib/api.ts — shared helper\nexport async function getLocations() {\n const res = await fetch(\n \\`${BASE}/v1/stores/\\${process.env.WHALE_STORE_ID}/locations\\`,\n { headers: { \"x-api-key\": process.env.WHALE_API_KEY! }, next: { revalidate: 3600 } }\n );\n return res.json().then(r => r.data);\n}`,\n\n react_client: `// hooks/useLocations.ts\nimport useSWR from \"swr\";\n\nexport function useLocations() {\n const { data } = useSWR(\"/api/locations\", (url) => fetch(url).then(r => r.json()));\n return data?.data ?? [];\n}`,\n },\n\n storefront: {\n raw_fetch: `// Track page view event\nawait fetch(\"${BASE}/v1/stores/${storeVar}/storefront/events\", {\n method: \"POST\",\n headers: { \"x-api-key\": \"${keyVar}\", \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n session_id: sessionId,\n event_type: \"page_view\",\n event_data: { url: \"/products/blue-dream\", product_id: \"...\" }\n })\n});\n\n// Send OTP for customer login\nawait fetch(\"${BASE}/v1/stores/${storeVar}/storefront/auth/send-code\", {\n method: \"POST\",\n headers: { \"x-api-key\": \"${keyVar}\", \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email: \"customer@example.com\" })\n});`,\n\n nextjs_ssr: `// app/api/auth/send-code/route.ts\nimport { NextRequest, NextResponse } from \"next/server\";\n\nexport async function POST(req: NextRequest) {\n const { email } = await req.json();\n const res = await fetch(\n \\`${BASE}/v1/stores/\\${process.env.WHALE_STORE_ID}/storefront/auth/send-code\\`,\n {\n method: \"POST\",\n headers: { \"x-api-key\": process.env.WHALE_API_KEY!, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email }),\n }\n );\n return NextResponse.json(await res.json(), { status: res.status });\n}`,\n\n react_client: `// hooks/useAuth.ts\nimport { useState } from \"react\";\n\nexport function useStorefrontAuth() {\n const [loading, setLoading] = useState(false);\n\n const sendCode = async (email: string) => {\n setLoading(true);\n await fetch(\"/api/auth/send-code\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email }),\n });\n setLoading(false);\n };\n\n const verifyCode = async (email: string, code: string) => {\n const res = await fetch(\"/api/auth/verify-code\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, code }),\n });\n return res.json(); // { customer_id, session_token }\n };\n\n return { sendCode, verifyCode, loading };\n}`,\n },\n\n coa: {\n raw_fetch: `// List COAs\nconst { data: coas } = await fetch(\"${BASE}/v1/stores/${storeVar}/coa?limit=50\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());\n\n// Verify by sample ID\nconst verified = await fetch(\"${BASE}/v1/stores/${storeVar}/coa/verify?sample_id=LAB-2026-001\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());\n\n// Get embeddable COA data\nconst embed = await fetch(\"${BASE}/v1/stores/${storeVar}/coa/\\${coaId}/embed\", {\n headers: { \"x-api-key\": \"${keyVar}\" }\n}).then(r => r.json());`,\n\n nextjs_ssr: `// app/products/[id]/coa/page.tsx\nasync function getCOAEmbed(coaId: string) {\n const res = await fetch(\n \\`${BASE}/v1/stores/\\${process.env.WHALE_STORE_ID}/coa/\\${coaId}/embed\\`,\n { headers: { \"x-api-key\": process.env.WHALE_API_KEY! }, next: { revalidate: 3600 } }\n );\n return res.json();\n}\n\nexport default async function COAPage({ params }: { params: { id: string } }) {\n const { html, data } = await getCOAEmbed(params.id);\n return (\n <div>\n <h1>Certificate of Analysis — {data.product_name}</h1>\n <div dangerouslySetInnerHTML={{ __html: html }} />\n </div>\n );\n}`,\n\n react_client: `// components/COAViewer.tsx\n\"use client\";\nimport { useEffect, useState } from \"react\";\n\nexport function COAViewer({ coaId }: { coaId: string }) {\n const [html, setHtml] = useState(\"\");\n\n useEffect(() => {\n fetch(\\`/api/coa/\\${coaId}/embed\\`).then(r => r.json()).then(d => setHtml(d.html));\n }, [coaId]);\n\n return <div dangerouslySetInnerHTML={{ __html: html }} />;\n}`,\n },\n\n portal: {\n raw_fetch: `// B2B portal login\nawait fetch(\"${BASE}/v1/stores/${storeVar}/portal/auth/send-code\", {\n method: \"POST\",\n headers: { \"x-api-key\": \"${keyVar}\", \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email: \"buyer@dispensary.com\" })\n});\n\n// Verify and get session\nconst session = await fetch(\"${BASE}/v1/stores/${storeVar}/portal/auth/verify\", {\n method: \"POST\",\n headers: { \"x-api-key\": \"${keyVar}\", \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email: \"buyer@dispensary.com\", code: \"123456\" })\n}).then(r => r.json());\n\n// List documents with portal token\nconst { data: docs } = await fetch(\"${BASE}/v1/stores/${storeVar}/portal/documents\", {\n headers: { \"x-api-key\": \"${keyVar}\", \"Authorization\": \"Bearer \" + session.token }\n}).then(r => r.json());`,\n\n nextjs_ssr: `// app/portal/layout.tsx — server-side session check\nimport { cookies } from \"next/headers\";\nimport { redirect } from \"next/navigation\";\n\nexport default async function PortalLayout({ children }: { children: React.ReactNode }) {\n const token = cookies().get(\"portal_token\")?.value;\n if (!token) redirect(\"/portal/login\");\n return <div className=\"portal-layout\">{children}</div>;\n}`,\n\n react_client: `// hooks/usePortal.ts\nimport { useState } from \"react\";\n\nexport function usePortal() {\n const [token, setToken] = useState<string | null>(null);\n\n const login = async (email: string, code: string) => {\n const res = await fetch(\"/api/portal/verify\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email, code }),\n });\n const data = await res.json();\n setToken(data.token);\n return data;\n };\n\n const getDocuments = async () => {\n const res = await fetch(\"/api/portal/documents\", {\n headers: { Authorization: \\`Bearer \\${token}\\` },\n });\n return res.json();\n };\n\n return { token, login, getDocuments };\n}`,\n },\n };\n\n return templates[section] ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Quick start guide\n// ---------------------------------------------------------------------------\n\nconst QUICK_START = {\n title: \"WhaleTools API — Quick Start Guide\",\n steps: [\n {\n step: 1,\n title: \"Get your API key\",\n description: \"Generate an API key from the WhaleTools dashboard (Settings → API Keys) or use the api_keys MCP tool.\",\n code: `// Via dashboard: Settings → API Keys → Generate\n// Or programmatically:\n// api_keys tool: { action: \"generate\", name: \"My Website\", scopes: [\"read:products\", \"read:cart\", \"write:cart\", \"write:checkout\"] }`,\n },\n {\n step: 2,\n title: \"Set up environment variables\",\n description: \"Store your API key and store ID securely. Never expose the API key in client-side code.\",\n code: `# .env.local (Next.js) or .env\nWHALE_API_KEY=wk_live_abc123...\nWHALE_STORE_ID=your-store-uuid`,\n },\n {\n step: 3,\n title: \"Fetch products\",\n description: \"Make your first API call to list published products.\",\n code: `const res = await fetch(\n \\`https://whale-gateway.fly.dev/v1/stores/\\${process.env.WHALE_STORE_ID}/products?status=published&limit=25\\`,\n { headers: { \"x-api-key\": process.env.WHALE_API_KEY } }\n);\nconst { data: products, has_more } = await res.json();\nconsole.log(\\`Loaded \\${products.length} products\\`);`,\n },\n {\n step: 4,\n title: \"Create a cart\",\n description: \"Start a shopping session by creating a cart.\",\n code: `const cart = await fetch(\n \\`https://whale-gateway.fly.dev/v1/stores/\\${STORE_ID}/cart\\`,\n {\n method: \"POST\",\n headers: { \"x-api-key\": API_KEY, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({})\n }\n).then(r => r.json());\nconsole.log(\"Cart ID:\", cart.id);`,\n },\n {\n step: 5,\n title: \"Add items to cart\",\n description: \"Add products to the cart by product ID.\",\n code: `await fetch(\n \\`https://whale-gateway.fly.dev/v1/stores/\\${STORE_ID}/cart/\\${cart.id}/items\\`,\n {\n method: \"POST\",\n headers: { \"x-api-key\": API_KEY, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ product_id: products[0].id, quantity: 1 })\n }\n);`,\n },\n {\n step: 6,\n title: \"Checkout\",\n description: \"Convert the cart into an order.\",\n code: `const order = await fetch(\n \\`https://whale-gateway.fly.dev/v1/stores/\\${STORE_ID}/checkout\\`,\n {\n method: \"POST\",\n headers: { \"x-api-key\": API_KEY, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n cart_id: cart.id,\n shipping_address: {\n street: \"123 Main St\",\n city: \"Charlotte\",\n state: \"NC\",\n zip: \"28202\"\n }\n })\n }\n).then(r => r.json());\nconsole.log(\"Order created:\", order.id, \"Status:\", order.status);`,\n },\n ],\n tips: [\n \"Keep your API key server-side. Use Next.js Route Handlers or API routes as a proxy for client-side calls.\",\n \"Use cursor pagination (starting_after) instead of offset for consistent results.\",\n \"Set next: { revalidate: 60 } in Next.js fetch for automatic ISR (Incremental Static Regeneration).\",\n \"Idempotency-Key header prevents duplicate orders — always set it on checkout requests.\",\n \"All list endpoints return { object: 'list', data: [...], has_more: boolean }.\",\n ],\n};\n\n// ---------------------------------------------------------------------------\n// Main handler\n// ---------------------------------------------------------------------------\n\nexport async function handleApiDocs(\n _sb: SupabaseClient,\n args: Record<string, unknown>,\n _storeId?: string,\n): Promise<{ success: boolean; data?: unknown; error?: string }> {\n const action = args.action as string;\n\n switch (action) {\n case \"sections\": {\n const sections = Object.entries(SECTIONS).map(([key, s]) => ({\n key,\n name: s.name,\n description: s.description,\n endpoint_count: s.endpoints.length,\n }));\n return {\n success: true,\n data: {\n total_endpoints: sections.reduce((sum, s) => sum + s.endpoint_count, 0),\n sections,\n usage: 'Use api_docs with action \"endpoints\" and section \"<key>\" to see full endpoint details.',\n },\n };\n }\n\n case \"endpoints\": {\n const section = args.section as string | undefined;\n if (!section) {\n return { success: false, error: 'Missing required param \"section\". Use action \"sections\" to see available sections.' };\n }\n const s = SECTIONS[section];\n if (!s) {\n return {\n success: false,\n error: `Unknown section \"${section}\". Available: ${Object.keys(SECTIONS).join(\", \")}`,\n };\n }\n return {\n success: true,\n data: {\n section: s.name,\n description: s.description,\n base: s.base,\n endpoints: s.endpoints,\n },\n };\n }\n\n case \"auth\": {\n return { success: true, data: AUTH_GUIDE };\n }\n\n case \"examples\": {\n const section = args.section as string | undefined;\n if (!section) {\n return { success: false, error: 'Missing required param \"section\". Use action \"sections\" to see available sections.' };\n }\n const examples = getExamples(section);\n if (!examples) {\n return {\n success: false,\n error: `No examples for section \"${section}\". Available: ${Object.keys(SECTIONS).join(\", \")}`,\n };\n }\n return {\n success: true,\n data: {\n section,\n examples,\n tip: \"Always keep API keys server-side. Use Next.js Route Handlers or API routes as a proxy for client-side calls.\",\n },\n };\n }\n\n case \"quick_start\": {\n return { success: true, data: QUICK_START };\n }\n\n default:\n return {\n success: false,\n error: `Unknown api_docs action: \"${action}\". Available: sections, endpoints, auth, examples, quick_start`,\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// OpenAPI 3.0 spec generation — serves /openapi.json for Scalar docs viewer\n// ---------------------------------------------------------------------------\n\nlet _cachedSpec: object | null = null;\n\n/** Convert :param to {param} for OpenAPI path syntax */\nfunction toOpenApiPath(path: string): string {\n return path.replace(/:([a-zA-Z_]+)/g, \"{$1}\");\n}\n\n/** Build OpenAPI parameter objects from the endpoint definition */\nfunction buildParameters(endpoint: Endpoint, path: string): object[] {\n const params: object[] = [];\n\n // Extract path parameters from the path itself\n const pathParams = path.match(/:([a-zA-Z_]+)/g) || [];\n for (const p of pathParams) {\n const name = p.slice(1);\n params.push({\n name,\n in: \"path\",\n required: true,\n schema: { type: \"string\" },\n description: name === \"storeId\" ? \"Store UUID\" : name === \"id\" ? \"Resource UUID\" : `${name} identifier`,\n });\n }\n\n // Add query parameters for GET endpoints\n if (endpoint.method === \"GET\" && endpoint.params) {\n for (const [name, desc] of Object.entries(endpoint.params)) {\n params.push({\n name,\n in: \"query\",\n required: desc.toLowerCase().startsWith(\"required\"),\n schema: { type: \"string\" },\n description: desc,\n });\n }\n }\n\n return params;\n}\n\n/** Build request body schema from endpoint body definition */\nfunction buildRequestBody(endpoint: Endpoint): object | undefined {\n if (!endpoint.body || endpoint.method === \"GET\") return undefined;\n\n const properties: Record<string, object> = {};\n const required: string[] = [];\n\n for (const [name, desc] of Object.entries(endpoint.body)) {\n if (name === \"...\") {\n properties[\"additionalProperties\"] = { type: \"object\", description: desc };\n continue;\n }\n properties[name] = { type: \"string\", description: desc };\n if (desc.toLowerCase().startsWith(\"required\")) {\n required.push(name);\n }\n }\n\n return {\n required: true,\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n properties,\n ...(required.length > 0 ? { required } : {}),\n },\n },\n },\n };\n}\n\n/** Generate the full OpenAPI 3.0 spec from SECTIONS */\nexport function generateOpenApiSpec(): object {\n if (_cachedSpec) return _cachedSpec;\n\n const paths: Record<string, Record<string, object>> = {};\n\n for (const [sectionKey, section] of Object.entries(SECTIONS)) {\n for (const endpoint of section.endpoints) {\n const openApiPath = toOpenApiPath(endpoint.path);\n\n if (!paths[openApiPath]) paths[openApiPath] = {};\n\n const method = endpoint.method.toLowerCase();\n const operation: Record<string, unknown> = {\n summary: endpoint.description,\n operationId: `${method}_${sectionKey}_${endpoint.path.split(\"/\").pop()?.replace(/:/g, \"\")}`,\n tags: [section.name],\n parameters: buildParameters(endpoint, endpoint.path),\n responses: {\n \"200\": {\n description: endpoint.response || \"Success\",\n content: {\n \"application/json\": {\n schema: { type: \"object\" },\n },\n },\n },\n \"401\": { description: \"Unauthorized — missing or invalid API key\" },\n \"404\": { description: \"Resource not found\" },\n },\n security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }],\n };\n\n if (endpoint.notes) {\n operation.description = endpoint.notes;\n }\n\n const requestBody = buildRequestBody(endpoint);\n if (requestBody) {\n operation.requestBody = requestBody;\n }\n\n paths[openApiPath][method] = operation;\n }\n }\n\n _cachedSpec = {\n openapi: \"3.0.3\",\n info: {\n title: \"WhaleTools Platform API\",\n description: \"Complete commerce, catalog, CRM, analytics, and agent API for the WhaleTools platform.\",\n version: \"2026-03-07\",\n contact: { name: \"WhaleTools\", url: \"https://whaletools.dev\" },\n },\n servers: [\n { url: GATEWAY_BASE_URL, description: \"Production\" },\n ],\n security: [{ BearerAuth: [] }, { ApiKeyAuth: [] }],\n paths,\n components: {\n securitySchemes: {\n BearerAuth: {\n type: \"http\",\n scheme: \"bearer\",\n bearerFormat: \"JWT\",\n description: \"Supabase JWT from auth login\",\n },\n ApiKeyAuth: {\n type: \"apiKey\",\n in: \"header\",\n name: \"x-api-key\",\n description: \"Store API key generated via the api_keys tool\",\n },\n },\n },\n tags: Object.entries(SECTIONS).map(([, s]) => ({\n name: s.name,\n description: s.description,\n })),\n };\n\n return _cachedSpec;\n}\n"],"mappings":"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AC9B,MAAMC,MAAM,GAAG,YAAY;EAE3B,MAAMC,SAA0F,GAAG;IACjGhK,QAAQ,EAAE;MACRiK,SAAS,EAAE;AACjB,2BAA2BJ,IAAI,cAAcC,QAAQ;AACrD,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA;AACA,+BAA+BF,IAAI,cAAcC,QAAQ;AACzD,6BAA6BC,MAAM;AACnC,wBAAwB;MAElBG,UAAU,EAAE;AAClB;AACA;AACA,gBAAgBL,IAAI;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE;MAEIM,YAAY,EAAE;AACpB;AACA;AACA,gBAAgBN,IAAI;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACI,CAAC;IAEDnH,IAAI,EAAE;MACJuH,SAAS,EAAE;AACjB,4BAA4BJ,IAAI,cAAcC,QAAQ;AACtD;AACA,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA;AACA,eAAeF,IAAI,cAAcC,QAAQ;AACzC;AACA,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA;AACA,gCAAgCF,IAAI,cAAcC,QAAQ;AAC1D,6BAA6BC,MAAM;AACnC,wBAAwB;MAElBG,UAAU,EAAE;AAClB;AACA;AACA;AACA;AACA,gBAAgBL,IAAI;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE;MAEIM,YAAY,EAAE;AACpB;AACA;AACA;AACA;AACA,kBAAkBN,IAAI;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACI,CAAC;IAED7G,QAAQ,EAAE;MACRiH,SAAS,EAAE;AACjB,6BAA6BJ,IAAI,cAAcC,QAAQ;AACvD;AACA,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,8BAA8BF,IAAI,cAAcC,QAAQ;AACxD;AACA,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA;AACA,eAAeF,IAAI,cAAcC,QAAQ;AACzC;AACA,6BAA6BC,MAAM;AACnC,IAAI;MAEEG,UAAU,EAAE;AAClB;AACA;AACA;AACA;AACA,gBAAgBL,IAAI;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE;MAEIM,YAAY,EAAE;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACI,CAAC;IAEDvI,MAAM,EAAE;MACNqI,SAAS,EAAE;AACjB,wCAAwCJ,IAAI,cAAcC,QAAQ;AAClE,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA,6BAA6BF,IAAI,cAAcC,QAAQ;AACvD,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA,eAAeF,IAAI,cAAcC,QAAQ;AACzC;AACA,6BAA6BC,MAAM;AACnC;AACA,IAAI;MAEEG,UAAU,EAAE;AAClB;AACA;AACA,QAAQL,IAAI;AACZ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE;MAEIM,YAAY,EAAE;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACI,CAAC;IAED3F,SAAS,EAAE;MACTyF,SAAS,EAAE;AACjB,2CAA2CJ,IAAI,cAAcC,QAAQ;AACrE,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA,gCAAgCF,IAAI,cAAcC,QAAQ;AAC1D;AACA,6BAA6BC,MAAM;AACnC;AACA,wBAAwB;MAElBG,UAAU,EAAE;AAClB;AACA;AACA;AACA,iBAAiBL,IAAI;AACrB;AACA;AACA;AACA;AACA;AACA,EAAE;MAEIM,YAAY,EAAE;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACI,CAAC;IAEDrF,SAAS,EAAE;MACTmF,SAAS,EAAE;AACjB,+BAA+BJ,IAAI,cAAcC,QAAQ;AACzD,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA,eAAeF,IAAI,cAAcC,QAAQ;AACzC;AACA,6BAA6BC,MAAM;AACnC;AACA,IAAI;MAEEG,UAAU,EAAE;AAClB;AACA;AACA,QAAQL,IAAI;AACZ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE;MAEIM,YAAY,EAAE;AACpB;AACA;AACA;AACA;AACA;AACA;IACI,CAAC;IAED/E,SAAS,EAAE;MACT6E,SAAS,EAAE;AACjB,6BAA6BJ,IAAI,cAAcC,QAAQ;AACvD,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA,6BAA6BF,IAAI,cAAcC,QAAQ;AACvD,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA,mCAAmCF,IAAI,cAAcC,QAAQ;AAC7D,6BAA6BC,MAAM;AACnC,wBAAwB;MAElBG,UAAU,EAAE;AAClB;AACA,mBAAmBL,IAAI;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE;MAEIM,YAAY,EAAE;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACI,CAAC;IAEDhF,SAAS,EAAE;MACT8E,SAAS,EAAE;AACjB,2CAA2CJ,IAAI,cAAcC,QAAQ;AACrE,6BAA6BC,MAAM;AACnC,wBAAwB;MAElBG,UAAU,EAAE;AAClB;AACA;AACA,QAAQL,IAAI;AACZ;AACA;AACA;AACA,EAAE;MAEIM,YAAY,EAAE;AACpB;AACA;AACA;AACA;AACA;AACA;IACI,CAAC;IAED5E,UAAU,EAAE;MACV0E,SAAS,EAAE;AACjB,eAAeJ,IAAI,cAAcC,QAAQ;AACzC;AACA,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAeF,IAAI,cAAcC,QAAQ;AACzC;AACA,6BAA6BC,MAAM;AACnC;AACA,IAAI;MAEEG,UAAU,EAAE;AAClB;AACA;AACA;AACA;AACA;AACA,QAAQL,IAAI;AACZ;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE;MAEIM,YAAY,EAAE;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACI,CAAC;IAEDtE,GAAG,EAAE;MACHoE,SAAS,EAAE;AACjB,sCAAsCJ,IAAI,cAAcC,QAAQ;AAChE,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA,gCAAgCF,IAAI,cAAcC,QAAQ;AAC1D,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA,6BAA6BF,IAAI,cAAcC,QAAQ;AACvD,6BAA6BC,MAAM;AACnC,wBAAwB;MAElBG,UAAU,EAAE;AAClB;AACA;AACA,QAAQL,IAAI;AACZ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE;MAEIM,YAAY,EAAE;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACI,CAAC;IAEDpE,MAAM,EAAE;MACNkE,SAAS,EAAE;AACjB,eAAeJ,IAAI,cAAcC,QAAQ;AACzC;AACA,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA;AACA,+BAA+BF,IAAI,cAAcC,QAAQ;AACzD;AACA,6BAA6BC,MAAM;AACnC;AACA;AACA;AACA;AACA,sCAAsCF,IAAI,cAAcC,QAAQ;AAChE,6BAA6BC,MAAM;AACnC,wBAAwB;MAElBG,UAAU,EAAE;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAE;MAEIC,YAAY,EAAE;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAC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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"api-docs.js","names":["GATEWAY_BASE_URL","OPENAPI_URL","CACHE_TTL_MS","_cache","fetchOpenApiSpec","Date","now","fetchedAt","spec","res","fetch","headers","Accept","signal","AbortSignal","timeout","ok","Error","status","json","getCachedSpecSync","deriveSections","tags","tagEndpointCounts","methods","Object","entries","paths","op","operation","tag","map","t","key","name","toLowerCase","replace","description","endpoint_count","deriveEndpointsForSection","sectionName","endpoints","path","method","some","push","toUpperCase","summary","parameters","requestBody","responses","findSectionTag","sectionKey","find","AUTH_GUIDE","authentication","key_format","live","test","example","scopes","wildcard","categories","rate_limiting","plans","free","starter","growth","enterprise","pagination","type","params","errors","format","types","QUICK_START","title","steps","step","code","api_reference","openapi_spec","handleApiDocs","_sb","args","_storeId","action","sections","success","data","api_version","info","version","total_endpoints","reduce","sum","s","total_sections","length","usage","err","error","message","section","available","join","query","results","text","includes","result_count","slice","note","generateOpenApiSpec","generateOpenApiSpecSync","cached","openapi","servers","url"],"sources":["../../../src/server/handlers/api-docs.ts"],"sourcesContent":["// server/handlers/api-docs.ts — Dynamic API documentation from gateway OpenAPI spec\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\n\nconst GATEWAY_BASE_URL = \"https://whale-gateway.fly.dev\";\nconst OPENAPI_URL = `${GATEWAY_BASE_URL}/openapi.json`;\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\n// ---------------------------------------------------------------------------\n// Cached OpenAPI spec — fetched from gateway on demand\n// ---------------------------------------------------------------------------\n\ninterface CachedSpec {\n spec: OpenApiSpec;\n fetchedAt: number;\n}\n\ninterface OpenApiSpec {\n openapi: string;\n info: { title: string; version: string; description?: string; contact?: { name?: string; url?: string } };\n servers: { url: string; description?: string }[];\n security?: object[];\n components: {\n securitySchemes?: Record<string, object>;\n schemas?: Record<string, object>;\n parameters?: Record<string, object>;\n headers?: Record<string, object>;\n };\n paths: Record<string, Record<string, OpenApiOperation>>;\n tags?: { name: string; description?: string }[];\n}\n\ninterface OpenApiOperation {\n summary?: string;\n description?: string;\n operationId?: string;\n tags?: string[];\n parameters?: object[];\n requestBody?: object;\n responses?: Record<string, object>;\n security?: object[];\n}\n\nlet _cache: CachedSpec | null = null;\n\nasync function fetchOpenApiSpec(): Promise<OpenApiSpec> {\n if (_cache && Date.now() - _cache.fetchedAt < CACHE_TTL_MS) {\n return _cache.spec;\n }\n\n const res = await fetch(OPENAPI_URL, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(8000),\n });\n\n if (!res.ok) {\n throw new Error(`Gateway returned ${res.status} fetching OpenAPI spec`);\n }\n\n const spec = (await res.json()) as OpenApiSpec;\n _cache = { spec, fetchedAt: Date.now() };\n return spec;\n}\n\n/** Get cached spec if available (for sync callers like /openapi.json endpoint) */\nfunction getCachedSpecSync(): OpenApiSpec | null {\n if (_cache && Date.now() - _cache.fetchedAt < CACHE_TTL_MS) {\n return _cache.spec;\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers — derive section/endpoint data from the live OpenAPI spec\n// ---------------------------------------------------------------------------\n\ninterface DerivedSection {\n key: string;\n name: string;\n description: string;\n endpoint_count: number;\n}\n\ninterface DerivedEndpoint {\n method: string;\n path: string;\n summary: string;\n description?: string;\n parameters?: object[];\n requestBody?: object;\n responses?: Record<string, object>;\n}\n\nfunction deriveSections(spec: OpenApiSpec): DerivedSection[] {\n const tags = spec.tags || [];\n const tagEndpointCounts: Record<string, number> = {};\n\n for (const [, methods] of Object.entries(spec.paths)) {\n for (const [, op] of Object.entries(methods)) {\n const operation = op as OpenApiOperation;\n if (operation.tags) {\n for (const tag of operation.tags) {\n tagEndpointCounts[tag] = (tagEndpointCounts[tag] || 0) + 1;\n }\n }\n }\n }\n\n return tags.map((t) => ({\n key: t.name.toLowerCase().replace(/\\s+/g, \"_\"),\n name: t.name,\n description: t.description || \"\",\n endpoint_count: tagEndpointCounts[t.name] || 0,\n }));\n}\n\nfunction deriveEndpointsForSection(spec: OpenApiSpec, sectionName: string): DerivedEndpoint[] {\n const endpoints: DerivedEndpoint[] = [];\n\n for (const [path, methods] of Object.entries(spec.paths)) {\n for (const [method, op] of Object.entries(methods)) {\n const operation = op as OpenApiOperation;\n if (operation.tags?.some((t) => t.toLowerCase().replace(/\\s+/g, \"_\") === sectionName || t === sectionName)) {\n endpoints.push({\n method: method.toUpperCase(),\n path,\n summary: operation.summary || \"\",\n description: operation.description,\n parameters: operation.parameters,\n requestBody: operation.requestBody,\n responses: operation.responses,\n });\n }\n }\n }\n\n return endpoints;\n}\n\nfunction findSectionTag(spec: OpenApiSpec, sectionKey: string): { name: string; description?: string } | undefined {\n return spec.tags?.find(\n (t) => t.name.toLowerCase().replace(/\\s+/g, \"_\") === sectionKey || t.name === sectionKey,\n );\n}\n\n// ---------------------------------------------------------------------------\n// Auth guide (static — these are usage instructions, not endpoint data)\n// ---------------------------------------------------------------------------\n\nconst AUTH_GUIDE = {\n authentication: {\n method: \"API Key via x-api-key header\",\n key_format: {\n live: \"wk_live_<32 chars> — production data\",\n test: \"wk_test_<32 chars> — sandbox data\",\n },\n example: `fetch(\"${GATEWAY_BASE_URL}/v1/stores/{store_id}/products\", {\\n headers: { \"x-api-key\": \"wk_live_...\" }\\n})`,\n },\n scopes: {\n description: \"Each API key has scopes controlling access\",\n wildcard: \"* — full access (all read + write)\",\n categories: [\n \"read:products, write:products\",\n \"read:orders, write:orders\",\n \"read:customers, write:customers\",\n \"read:inventory, write:inventory\",\n \"read:analytics\",\n \"read:storefront, write:storefront\",\n \"read:agents, write:agents\",\n \"read:documents, write:documents\",\n \"read:telemetry, write:telemetry\",\n ],\n },\n rate_limiting: {\n headers: [\"X-RateLimit-Limit\", \"X-RateLimit-Remaining\", \"X-RateLimit-Reset\", \"Retry-After (on 429)\"],\n plans: {\n free: \"60/min\",\n starter: \"300/min\",\n growth: \"600/min\",\n enterprise: \"3000/min\",\n },\n },\n pagination: {\n type: \"Cursor-based\",\n params: [\"limit (1-500, default 25)\", \"starting_after (resource ID)\", \"ending_before (resource ID)\"],\n },\n errors: {\n format: '{ \"error\": { \"type\": \"...\", \"message\": \"...\", \"code\": \"...\", \"request_id\": \"req_...\" } }',\n types: [\n \"authentication_error (401)\",\n \"permission_error (403)\",\n \"not_found_error (404)\",\n \"invalid_request_error (400/422)\",\n \"rate_limit_error (429)\",\n \"conflict_error (409)\",\n \"api_error (500)\",\n ],\n },\n};\n\n// ---------------------------------------------------------------------------\n// Quick start guide (static usage instructions)\n// ---------------------------------------------------------------------------\n\nconst QUICK_START = {\n title: \"WhaleTools API Quick Start\",\n steps: [\n {\n step: 1,\n title: \"Get your API key\",\n description: \"Generate a key via the dashboard at whaletools.dev/dashboard/api-keys or use the api_keys MCP tool.\",\n },\n {\n step: 2,\n title: \"Make your first request\",\n code: `curl -H \"x-api-key: wk_live_...\" ${GATEWAY_BASE_URL}/v1/stores/{store_id}/products`,\n },\n {\n step: 3,\n title: \"Use the TypeScript SDK\",\n code: `import { WhaleClient } from \"@neowhale/api-client\";\\nconst whale = new WhaleClient({ apiKey: \"wk_live_...\" });\\nconst products = await whale.products.list();`,\n },\n {\n step: 4,\n title: \"Use the Python SDK\",\n code: `from whaletools import WhaleClient\\nwhale = WhaleClient(api_key=\"wk_live_...\")\\nproducts = whale.products.list()`,\n },\n ],\n api_reference: `${GATEWAY_BASE_URL}/docs`,\n openapi_spec: `${GATEWAY_BASE_URL}/openapi.json`,\n};\n\n// ---------------------------------------------------------------------------\n// Main handler\n// ---------------------------------------------------------------------------\n\nexport async function handleApiDocs(\n _sb: SupabaseClient,\n args: Record<string, unknown>,\n _storeId?: string,\n): Promise<{ success: boolean; data?: unknown; error?: string }> {\n const action = args.action as string;\n\n switch (action) {\n case \"sections\": {\n try {\n const spec = await fetchOpenApiSpec();\n const sections = deriveSections(spec);\n return {\n success: true,\n data: {\n api_version: spec.info.version,\n total_endpoints: sections.reduce((sum, s) => sum + s.endpoint_count, 0),\n total_sections: sections.length,\n sections,\n usage: 'Use api_docs with action \"endpoints\" and section \"<key>\" to see full endpoint details.',\n },\n };\n } catch (err) {\n return { success: false, error: `Failed to fetch API spec: ${(err as Error).message}` };\n }\n }\n\n case \"endpoints\": {\n const section = args.section as string | undefined;\n if (!section) {\n return { success: false, error: 'Missing required param \"section\". Use action \"sections\" to see available sections.' };\n }\n try {\n const spec = await fetchOpenApiSpec();\n const tag = findSectionTag(spec, section);\n if (!tag) {\n const available = (spec.tags || []).map((t) => t.name.toLowerCase().replace(/\\s+/g, \"_\"));\n return {\n success: false,\n error: `Unknown section \"${section}\". Available: ${available.join(\", \")}`,\n };\n }\n const endpoints = deriveEndpointsForSection(spec, section);\n return {\n success: true,\n data: {\n section: tag.name,\n description: tag.description || \"\",\n endpoint_count: endpoints.length,\n endpoints,\n },\n };\n } catch (err) {\n return { success: false, error: `Failed to fetch API spec: ${(err as Error).message}` };\n }\n }\n\n case \"search\": {\n const query = (args.query as string || \"\").toLowerCase();\n if (!query) {\n return { success: false, error: 'Missing required param \"query\". Provide a search term (e.g., \"products\", \"checkout\", \"webhook\").' };\n }\n try {\n const spec = await fetchOpenApiSpec();\n const results: { method: string; path: string; summary: string; tags: string[] }[] = [];\n\n for (const [path, methods] of Object.entries(spec.paths)) {\n for (const [method, op] of Object.entries(methods)) {\n const operation = op as OpenApiOperation;\n const text = `${path} ${operation.summary || \"\"} ${operation.description || \"\"} ${(operation.tags || []).join(\" \")}`.toLowerCase();\n if (text.includes(query)) {\n results.push({\n method: method.toUpperCase(),\n path,\n summary: operation.summary || \"\",\n tags: operation.tags || [],\n });\n }\n }\n }\n\n return {\n success: true,\n data: {\n query,\n result_count: results.length,\n results: results.slice(0, 50),\n ...(results.length > 50 ? { note: `Showing first 50 of ${results.length} results. Narrow your search.` } : {}),\n },\n };\n } catch (err) {\n return { success: false, error: `Failed to fetch API spec: ${(err as Error).message}` };\n }\n }\n\n case \"auth\": {\n return { success: true, data: AUTH_GUIDE };\n }\n\n case \"quick_start\": {\n return { success: true, data: QUICK_START };\n }\n\n default:\n return {\n success: false,\n error: `Unknown api_docs action: \"${action}\". Available: sections, endpoints, search, auth, quick_start`,\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// OpenAPI spec proxy — serves /openapi.json on the agent server\n// ---------------------------------------------------------------------------\n\n/** Generate (fetch) the OpenAPI spec — async version for the handler */\nexport async function generateOpenApiSpec(): Promise<object> {\n return fetchOpenApiSpec();\n}\n\n/** Sync fallback: return cached spec or a redirect hint */\nexport function generateOpenApiSpecSync(): object {\n const cached = getCachedSpecSync();\n if (cached) return cached;\n\n // Return a minimal spec pointing to the canonical source\n return {\n openapi: \"3.1.0\",\n info: {\n title: \"WhaleTools Platform API\",\n version: \"latest\",\n description: `Full spec available at ${OPENAPI_URL}`,\n },\n servers: [{ url: GATEWAY_BASE_URL, description: \"Production\" }],\n paths: {},\n };\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
type Result = {
|
|
3
|
+
success: boolean;
|
|
4
|
+
data?: unknown;
|
|
5
|
+
error?: string;
|
|
6
|
+
[key: string]: unknown;
|
|
7
|
+
};
|
|
8
|
+
export declare function handleCampaigns(sb: SupabaseClient, args: Record<string, unknown>, storeId?: string): Promise<Result>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// server/handlers/campaigns.ts — Marketing campaign management (email, SMS, multi-channel)
|
|
2
|
+
|
|
3
|
+
import { sanitizeFilterValue } from "../lib/utils.js";
|
|
4
|
+
export async function handleCampaigns(sb, args, storeId) {
|
|
5
|
+
const sid = storeId;
|
|
6
|
+
switch (args.action) {
|
|
7
|
+
// ---- LIST: list campaigns ----
|
|
8
|
+
case "list":
|
|
9
|
+
{
|
|
10
|
+
let q = sb.from("campaigns").select("id, name, description, campaign_type, channels, status, segment_id, total_recipients, total_sent, total_delivered, total_opened, total_clicked, open_rate, click_rate, revenue, scheduled_at, sent_at, created_at").eq("store_id", sid).order("created_at", {
|
|
11
|
+
ascending: false
|
|
12
|
+
});
|
|
13
|
+
if (args.status) q = q.eq("status", args.status);
|
|
14
|
+
if (args.campaign_type) q = q.eq("campaign_type", args.campaign_type);
|
|
15
|
+
if (args.query) {
|
|
16
|
+
const sq = sanitizeFilterValue(String(args.query));
|
|
17
|
+
q = q.or(`name.ilike.%${sq}%,description.ilike.%${sq}%`);
|
|
18
|
+
}
|
|
19
|
+
q = q.limit(args.limit || 50);
|
|
20
|
+
const {
|
|
21
|
+
data,
|
|
22
|
+
error
|
|
23
|
+
} = await q;
|
|
24
|
+
return error ? {
|
|
25
|
+
success: false,
|
|
26
|
+
error: error.message
|
|
27
|
+
} : {
|
|
28
|
+
success: true,
|
|
29
|
+
count: data?.length,
|
|
30
|
+
data
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---- GET: get campaign detail with performance metrics ----
|
|
35
|
+
case "get":
|
|
36
|
+
{
|
|
37
|
+
const campaignId = args.campaign_id;
|
|
38
|
+
if (!campaignId) return {
|
|
39
|
+
success: false,
|
|
40
|
+
error: "campaign_id is required"
|
|
41
|
+
};
|
|
42
|
+
const {
|
|
43
|
+
data,
|
|
44
|
+
error
|
|
45
|
+
} = await sb.from("campaigns").select("*").eq("id", campaignId).eq("store_id", sid).single();
|
|
46
|
+
if (error) return {
|
|
47
|
+
success: false,
|
|
48
|
+
error: error.message
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Get event breakdown
|
|
52
|
+
const {
|
|
53
|
+
data: events
|
|
54
|
+
} = await sb.from("marketing_campaign_events").select("event_type, channel").eq("campaign_id", campaignId).eq("store_id", sid);
|
|
55
|
+
const eventBreakdown = {};
|
|
56
|
+
for (const e of events || []) {
|
|
57
|
+
const key = `${e.channel}_${e.event_type}`;
|
|
58
|
+
eventBreakdown[key] = (eventBreakdown[key] || 0) + 1;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
success: true,
|
|
62
|
+
data: {
|
|
63
|
+
...data,
|
|
64
|
+
event_breakdown: eventBreakdown
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ---- CREATE: create a new campaign ----
|
|
70
|
+
case "create":
|
|
71
|
+
{
|
|
72
|
+
const name = args.name;
|
|
73
|
+
if (!name) return {
|
|
74
|
+
success: false,
|
|
75
|
+
error: "name is required"
|
|
76
|
+
};
|
|
77
|
+
const record = {
|
|
78
|
+
store_id: sid,
|
|
79
|
+
name,
|
|
80
|
+
status: "draft"
|
|
81
|
+
};
|
|
82
|
+
if (args.description !== undefined) record.description = args.description;
|
|
83
|
+
if (args.campaign_type !== undefined) record.campaign_type = args.campaign_type;
|
|
84
|
+
if (args.subject !== undefined) record.subject = args.subject;
|
|
85
|
+
if (args.preview_text !== undefined) record.preview_text = args.preview_text;
|
|
86
|
+
if (args.html_content !== undefined) record.html_content = args.html_content;
|
|
87
|
+
if (args.text_content !== undefined) record.text_content = args.text_content;
|
|
88
|
+
if (args.template_id !== undefined) record.template_id = args.template_id;
|
|
89
|
+
if (args.template_slug !== undefined) record.template_slug = args.template_slug;
|
|
90
|
+
if (args.from_name !== undefined) record.from_name = args.from_name;
|
|
91
|
+
if (args.from_email !== undefined) record.from_email = args.from_email;
|
|
92
|
+
if (args.reply_to !== undefined) record.reply_to = args.reply_to;
|
|
93
|
+
if (args.segment_id !== undefined) record.segment_id = args.segment_id;
|
|
94
|
+
if (args.channels !== undefined) record.channels = args.channels;
|
|
95
|
+
if (args.channel_config !== undefined) record.channel_config = args.channel_config;
|
|
96
|
+
if (args.deal_id !== undefined) record.deal_id = args.deal_id;
|
|
97
|
+
if (args.scheduled_at !== undefined) record.scheduled_at = args.scheduled_at;
|
|
98
|
+
if (args.utm_source !== undefined) record.utm_source = args.utm_source;
|
|
99
|
+
if (args.utm_medium !== undefined) record.utm_medium = args.utm_medium;
|
|
100
|
+
if (args.utm_campaign !== undefined) record.utm_campaign = args.utm_campaign;
|
|
101
|
+
if (args.budget !== undefined) record.budget = args.budget;
|
|
102
|
+
const {
|
|
103
|
+
data,
|
|
104
|
+
error
|
|
105
|
+
} = await sb.from("campaigns").insert(record).select().single();
|
|
106
|
+
return error ? {
|
|
107
|
+
success: false,
|
|
108
|
+
error: error.message
|
|
109
|
+
} : {
|
|
110
|
+
success: true,
|
|
111
|
+
data
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ---- UPDATE: modify a campaign ----
|
|
116
|
+
case "update":
|
|
117
|
+
{
|
|
118
|
+
const campaignId = args.campaign_id;
|
|
119
|
+
if (!campaignId) return {
|
|
120
|
+
success: false,
|
|
121
|
+
error: "campaign_id is required"
|
|
122
|
+
};
|
|
123
|
+
const updates = {
|
|
124
|
+
updated_at: new Date().toISOString()
|
|
125
|
+
};
|
|
126
|
+
if (args.name !== undefined) updates.name = args.name;
|
|
127
|
+
if (args.description !== undefined) updates.description = args.description;
|
|
128
|
+
if (args.subject !== undefined) updates.subject = args.subject;
|
|
129
|
+
if (args.html_content !== undefined) updates.html_content = args.html_content;
|
|
130
|
+
if (args.text_content !== undefined) updates.text_content = args.text_content;
|
|
131
|
+
if (args.status !== undefined) updates.status = args.status;
|
|
132
|
+
if (args.segment_id !== undefined) updates.segment_id = args.segment_id;
|
|
133
|
+
if (args.scheduled_at !== undefined) updates.scheduled_at = args.scheduled_at;
|
|
134
|
+
if (args.channels !== undefined) updates.channels = args.channels;
|
|
135
|
+
if (args.channel_config !== undefined) updates.channel_config = args.channel_config;
|
|
136
|
+
const {
|
|
137
|
+
data,
|
|
138
|
+
error
|
|
139
|
+
} = await sb.from("campaigns").update(updates).eq("id", campaignId).eq("store_id", sid).select().single();
|
|
140
|
+
return error ? {
|
|
141
|
+
success: false,
|
|
142
|
+
error: error.message
|
|
143
|
+
} : {
|
|
144
|
+
success: true,
|
|
145
|
+
data
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---- LIST_SMS: list SMS campaigns ----
|
|
150
|
+
case "list_sms":
|
|
151
|
+
{
|
|
152
|
+
let q = sb.from("sms_campaigns").select("*").eq("store_id", sid).order("created_at", {
|
|
153
|
+
ascending: false
|
|
154
|
+
});
|
|
155
|
+
if (args.status) q = q.eq("status", args.status);
|
|
156
|
+
q = q.limit(args.limit || 50);
|
|
157
|
+
const {
|
|
158
|
+
data,
|
|
159
|
+
error
|
|
160
|
+
} = await q;
|
|
161
|
+
return error ? {
|
|
162
|
+
success: false,
|
|
163
|
+
error: error.message
|
|
164
|
+
} : {
|
|
165
|
+
success: true,
|
|
166
|
+
count: data?.length,
|
|
167
|
+
data
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ---- CREATE_SMS: create an SMS campaign ----
|
|
172
|
+
case "create_sms":
|
|
173
|
+
{
|
|
174
|
+
const name = args.name;
|
|
175
|
+
const messageBody = args.message_body;
|
|
176
|
+
if (!name || !messageBody) return {
|
|
177
|
+
success: false,
|
|
178
|
+
error: "name and message_body are required"
|
|
179
|
+
};
|
|
180
|
+
const record = {
|
|
181
|
+
store_id: sid,
|
|
182
|
+
name,
|
|
183
|
+
message_body: messageBody,
|
|
184
|
+
status: "draft"
|
|
185
|
+
};
|
|
186
|
+
if (args.description !== undefined) record.description = args.description;
|
|
187
|
+
if (args.segment_id !== undefined) record.segment_id = args.segment_id;
|
|
188
|
+
if (args.is_mms !== undefined) record.is_mms = args.is_mms;
|
|
189
|
+
if (args.media_url !== undefined) record.media_url = args.media_url;
|
|
190
|
+
if (args.scheduled_for !== undefined) record.scheduled_for = args.scheduled_for;
|
|
191
|
+
const {
|
|
192
|
+
data,
|
|
193
|
+
error
|
|
194
|
+
} = await sb.from("sms_campaigns").insert(record).select().single();
|
|
195
|
+
return error ? {
|
|
196
|
+
success: false,
|
|
197
|
+
error: error.message
|
|
198
|
+
} : {
|
|
199
|
+
success: true,
|
|
200
|
+
data
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ---- EVENTS: list campaign events/analytics ----
|
|
205
|
+
case "events":
|
|
206
|
+
{
|
|
207
|
+
const campaignId = args.campaign_id;
|
|
208
|
+
if (!campaignId) return {
|
|
209
|
+
success: false,
|
|
210
|
+
error: "campaign_id is required"
|
|
211
|
+
};
|
|
212
|
+
let q = sb.from("marketing_campaign_events").select("*").eq("campaign_id", campaignId).eq("store_id", sid).order("created_at", {
|
|
213
|
+
ascending: false
|
|
214
|
+
});
|
|
215
|
+
if (args.event_type) q = q.eq("event_type", args.event_type);
|
|
216
|
+
q = q.limit(args.limit || 100);
|
|
217
|
+
const {
|
|
218
|
+
data,
|
|
219
|
+
error
|
|
220
|
+
} = await q;
|
|
221
|
+
return error ? {
|
|
222
|
+
success: false,
|
|
223
|
+
error: error.message
|
|
224
|
+
} : {
|
|
225
|
+
success: true,
|
|
226
|
+
count: data?.length,
|
|
227
|
+
data
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
default:
|
|
231
|
+
return {
|
|
232
|
+
success: false,
|
|
233
|
+
error: `Unknown campaigns action: ${args.action}. Valid: list, get, create, update, list_sms, create_sms, events`
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=campaigns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaigns.js","names":["sanitizeFilterValue","handleCampaigns","sb","args","storeId","sid","action","q","from","select","eq","order","ascending","status","campaign_type","query","sq","String","or","limit","data","error","success","message","count","length","campaignId","campaign_id","single","events","eventBreakdown","e","key","channel","event_type","event_breakdown","name","record","store_id","description","undefined","subject","preview_text","html_content","text_content","template_id","template_slug","from_name","from_email","reply_to","segment_id","channels","channel_config","deal_id","scheduled_at","utm_source","utm_medium","utm_campaign","budget","insert","updates","updated_at","Date","toISOString","update","messageBody","message_body","is_mms","media_url","scheduled_for"],"sources":["../../../src/server/handlers/campaigns.ts"],"sourcesContent":["// server/handlers/campaigns.ts — Marketing campaign management (email, SMS, multi-channel)\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport { sanitizeFilterValue } from \"../lib/utils.js\";\n\ntype Result = { success: boolean; data?: unknown; error?: string; [key: string]: unknown };\n\nexport async function handleCampaigns(\n sb: SupabaseClient,\n args: Record<string, unknown>,\n storeId?: string,\n): Promise<Result> {\n const sid = storeId as string;\n\n switch (args.action) {\n // ---- LIST: list campaigns ----\n case \"list\": {\n let q = sb.from(\"campaigns\")\n .select(\"id, name, description, campaign_type, channels, status, segment_id, total_recipients, total_sent, total_delivered, total_opened, total_clicked, open_rate, click_rate, revenue, scheduled_at, sent_at, created_at\")\n .eq(\"store_id\", sid)\n .order(\"created_at\", { ascending: false });\n if (args.status) q = q.eq(\"status\", args.status as string);\n if (args.campaign_type) q = q.eq(\"campaign_type\", args.campaign_type as string);\n if (args.query) {\n const sq = sanitizeFilterValue(String(args.query));\n q = q.or(`name.ilike.%${sq}%,description.ilike.%${sq}%`);\n }\n q = q.limit(args.limit as number || 50);\n const { data, error } = await q;\n return error ? { success: false, error: error.message } : { success: true, count: data?.length, data };\n }\n\n // ---- GET: get campaign detail with performance metrics ----\n case \"get\": {\n const campaignId = args.campaign_id as string;\n if (!campaignId) return { success: false, error: \"campaign_id is required\" };\n const { data, error } = await sb.from(\"campaigns\")\n .select(\"*\")\n .eq(\"id\", campaignId)\n .eq(\"store_id\", sid)\n .single();\n if (error) return { success: false, error: error.message };\n\n // Get event breakdown\n const { data: events } = await sb.from(\"marketing_campaign_events\")\n .select(\"event_type, channel\")\n .eq(\"campaign_id\", campaignId)\n .eq(\"store_id\", sid);\n const eventBreakdown: Record<string, number> = {};\n for (const e of events || []) {\n const key = `${e.channel}_${e.event_type}`;\n eventBreakdown[key] = (eventBreakdown[key] || 0) + 1;\n }\n\n return { success: true, data: { ...data, event_breakdown: eventBreakdown } };\n }\n\n // ---- CREATE: create a new campaign ----\n case \"create\": {\n const name = args.name as string;\n if (!name) return { success: false, error: \"name is required\" };\n const record: Record<string, unknown> = {\n store_id: sid,\n name,\n status: \"draft\",\n };\n if (args.description !== undefined) record.description = args.description;\n if (args.campaign_type !== undefined) record.campaign_type = args.campaign_type;\n if (args.subject !== undefined) record.subject = args.subject;\n if (args.preview_text !== undefined) record.preview_text = args.preview_text;\n if (args.html_content !== undefined) record.html_content = args.html_content;\n if (args.text_content !== undefined) record.text_content = args.text_content;\n if (args.template_id !== undefined) record.template_id = args.template_id;\n if (args.template_slug !== undefined) record.template_slug = args.template_slug;\n if (args.from_name !== undefined) record.from_name = args.from_name;\n if (args.from_email !== undefined) record.from_email = args.from_email;\n if (args.reply_to !== undefined) record.reply_to = args.reply_to;\n if (args.segment_id !== undefined) record.segment_id = args.segment_id;\n if (args.channels !== undefined) record.channels = args.channels;\n if (args.channel_config !== undefined) record.channel_config = args.channel_config;\n if (args.deal_id !== undefined) record.deal_id = args.deal_id;\n if (args.scheduled_at !== undefined) record.scheduled_at = args.scheduled_at;\n if (args.utm_source !== undefined) record.utm_source = args.utm_source;\n if (args.utm_medium !== undefined) record.utm_medium = args.utm_medium;\n if (args.utm_campaign !== undefined) record.utm_campaign = args.utm_campaign;\n if (args.budget !== undefined) record.budget = args.budget;\n\n const { data, error } = await sb.from(\"campaigns\")\n .insert(record)\n .select()\n .single();\n return error ? { success: false, error: error.message } : { success: true, data };\n }\n\n // ---- UPDATE: modify a campaign ----\n case \"update\": {\n const campaignId = args.campaign_id as string;\n if (!campaignId) return { success: false, error: \"campaign_id is required\" };\n const updates: Record<string, unknown> = { updated_at: new Date().toISOString() };\n if (args.name !== undefined) updates.name = args.name;\n if (args.description !== undefined) updates.description = args.description;\n if (args.subject !== undefined) updates.subject = args.subject;\n if (args.html_content !== undefined) updates.html_content = args.html_content;\n if (args.text_content !== undefined) updates.text_content = args.text_content;\n if (args.status !== undefined) updates.status = args.status;\n if (args.segment_id !== undefined) updates.segment_id = args.segment_id;\n if (args.scheduled_at !== undefined) updates.scheduled_at = args.scheduled_at;\n if (args.channels !== undefined) updates.channels = args.channels;\n if (args.channel_config !== undefined) updates.channel_config = args.channel_config;\n\n const { data, error } = await sb.from(\"campaigns\")\n .update(updates)\n .eq(\"id\", campaignId)\n .eq(\"store_id\", sid)\n .select()\n .single();\n return error ? { success: false, error: error.message } : { success: true, data };\n }\n\n // ---- LIST_SMS: list SMS campaigns ----\n case \"list_sms\": {\n let q = sb.from(\"sms_campaigns\")\n .select(\"*\")\n .eq(\"store_id\", sid)\n .order(\"created_at\", { ascending: false });\n if (args.status) q = q.eq(\"status\", args.status as string);\n q = q.limit(args.limit as number || 50);\n const { data, error } = await q;\n return error ? { success: false, error: error.message } : { success: true, count: data?.length, data };\n }\n\n // ---- CREATE_SMS: create an SMS campaign ----\n case \"create_sms\": {\n const name = args.name as string;\n const messageBody = args.message_body as string;\n if (!name || !messageBody) return { success: false, error: \"name and message_body are required\" };\n const record: Record<string, unknown> = {\n store_id: sid,\n name,\n message_body: messageBody,\n status: \"draft\",\n };\n if (args.description !== undefined) record.description = args.description;\n if (args.segment_id !== undefined) record.segment_id = args.segment_id;\n if (args.is_mms !== undefined) record.is_mms = args.is_mms;\n if (args.media_url !== undefined) record.media_url = args.media_url;\n if (args.scheduled_for !== undefined) record.scheduled_for = args.scheduled_for;\n\n const { data, error } = await sb.from(\"sms_campaigns\")\n .insert(record)\n .select()\n .single();\n return error ? { success: false, error: error.message } : { success: true, data };\n }\n\n // ---- EVENTS: list campaign events/analytics ----\n case \"events\": {\n const campaignId = args.campaign_id as string;\n if (!campaignId) return { success: false, error: \"campaign_id is required\" };\n let q = sb.from(\"marketing_campaign_events\")\n .select(\"*\")\n .eq(\"campaign_id\", campaignId)\n .eq(\"store_id\", sid)\n .order(\"created_at\", { ascending: false });\n if (args.event_type) q = q.eq(\"event_type\", args.event_type as string);\n q = q.limit(args.limit as number || 100);\n const { data, error } = await q;\n return error ? { success: false, error: error.message } : { success: true, count: data?.length, data };\n }\n\n default:\n return { success: false, error: `Unknown campaigns action: ${args.action}. Valid: list, get, create, update, list_sms, create_sms, events` };\n }\n}\n"],"mappings":"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","ignoreList":[]}
|