{"openapi":"3.0.3","info":{"title":"Lynxbe Public API","version":"4c4682ddbc57","description":"The Lynxbe platform public API. Authenticate with a business API key (lxb_...) in the X-API-Key header, or as a Bearer token. Create keys in the Lynxbe app: API Docs page (/automation/api-docs), API Keys tab. Full reference: https://lynxbe.co.il/api-reference"},"servers":[{"url":"https://api.lynxbe.co.il/api/v1"}],"tags":[{"name":"WhatsApp","description":"WhatsApp Business API endpoints for conversations, messages, templates, and contacts."},{"name":"Voice & Softphone","description":"Voice calls, recordings, queues, and softphone management."},{"name":"SMS","description":"SMS messaging via SMPP gateway."},{"name":"Email Marketing","description":"Email campaigns, templates, and SES integration. Campaigns (list and send) require Email Marketing on the plan (403 MODULE_LOCKED otherwise)."},{"name":"Booking","description":"Appointment scheduling, availability checks, and booking management."},{"name":"Customers (CRM)","description":"Customer management, contacts, and CRM operations."},{"name":"People (Identity Hub)","description":"One record per person or organisation the business deals with, with every channel they are reachable on (phone, email, WhatsApp, social). Look a caller up by phone or email before creating anything."},{"name":"CRM Boards","description":"CRM boards, items, pipelines, and automation."},{"name":"Services","description":"Service catalog management."},{"name":"Analytics","description":"Reports, dashboards, and analytics."},{"name":"Automation Flows","description":"Workflow automation creation and execution."},{"name":"Invoicing","description":"Invoice creation, sending, and payment tracking."},{"name":"Payments","description":"Payment processing, transactions, and refunds."},{"name":"Commerce & Orders","description":"Catalog items and order management for external storefronts and integrations."},{"name":"Contracts","description":"Contract creation and e-signature management."},{"name":"Workforce Management","description":"Time tracking, attendance, shifts, and HR management. Employees, attendance, shifts, clock-in/out and the monthly report require Workforce on the plan; the payroll export requires Payroll (403 otherwise)."},{"name":"Field Sales","description":"Field sales agents, customer visits, and on-site orders."},{"name":"Coupons","description":"Discount and coupon management."},{"name":"Notifications","description":"Notification templates, delivery, and preferences."},{"name":"Social Media","description":"Social media posting and account management. Requires Social on the plan (403 MODULE_LOCKED otherwise)."},{"name":"Webhooks","description":"Webhook subscription management."},{"name":"Audit Log","description":"Audit trail and security logs."},{"name":"Users & Teams","description":"User management, team assignment, and permissions."},{"name":"Compliance","description":"GDPR/HIPAA compliance, data retention, and consent management."},{"name":"Account","description":"Account and API key information endpoints."},{"name":"Branding Pages","description":"Smart Domain Branding — Multi-page canvas editor with block-based content, templates, and AI generation."},{"name":"Dialer","description":"Outbound dialer campaigns, leads, and campaign analytics."},{"name":"Checklists","description":"Checklist templates, campaigns, and client progress tracking."},{"name":"Meeting Polls","description":"Group meeting availability polls — create polls and collect participant votes. Requires Meeting Polls on the plan (403 MODULE_LOCKED otherwise)."},{"name":"Broadcast Campaigns","description":"Bulk WhatsApp/SMS broadcast campaigns and delivery status."},{"name":"AI Agents","description":"Installed AI agents, their approvals queue and run triggers. Approval-gated actions stay gated: a run proposes, a human in the app decides."},{"name":"Documents","description":"Workforce documents (IDs, contracts, certificates) as metadata: who, what type, when it expires. File contents and download links stay in the app. Requires the Workforce module on the plan."},{"name":"Expenses","description":"Business expenses (accounting), read only: list, one expense, and totals by category and payment method. Approving, paying and VAT reporting stay in the app. Requires Expense Management on the plan."},{"name":"B2B Orders & Pricing","description":"B2B commerce orders placed by your business customers (draft, approval, shipping) and the prices each customer gets, read only. Separate from the POS orders under Commerce. Your cost and margin are never returned. Orders require Commerce Orders on the plan; prices and price lists require Commerce Pricing."},{"name":"Retail Register","description":"Shop counter sales, read only: receipts, their items and tenders, and totals by day. Selling, refunds and shifts stay in the app. Card references are never returned. Requires Retail Register on the plan."},{"name":"Customer Portal","description":"Give one of your customers a private, time-limited portal session: a hosted page to update the card on file, and portal endpoints for their invoices and subscriptions. Requires Customer Portal on the plan."},{"name":"Team Chat","description":"Post alerts and updates from your systems into your team's chat channels, as the Lynxbe Bot. Only channels - direct messages between employees are never readable or writable by API. Requires Team Chat on the plan."},{"name":"App Marketplace","description":"The marketplace apps your business installed and their state. Requires the App Marketplace on the plan."},{"name":"Google Business Profile","description":"Your Google reviews, as synced from your connected Google Business Profile - for a review widget on your site or your own reporting. Replying stays in the app. Requires Google Business Profile on the plan."},{"name":"Ads","description":"Your ad campaigns on Facebook/Instagram, TikTok and Google Ads with their latest synced stats, read only. Each platform appears only if it is on your plan."},{"name":"Custom Domains","description":"The status of the custom domains your business bought through Lynxbe: registration, expiry, auto-renew and the HTTPS certificate. Buying and renewing stay in the app."},{"name":"App Clips","description":"Your App Clip QR/NFC links and how they perform: uses, sessions and completed conversions per link. Requires the App Clips module. Creating and editing links stays in the app."},{"name":"Legal","description":"Your legal matters and upcoming statutory deadlines, read only. Requires the Legal Practice module. Opening matters, confirming deadlines and drafting stay in the app."},{"name":"Vending","description":"Your vending machines, read live from the Nayax account you connected in the app. Requires the Vending module. Connecting the Nayax token stays in the app."},{"name":"Carpentry","description":"Carpentry and cabinet-making projects and their workshop work orders, read only. Requires the Carpentry module. Creating projects and moving work-order stages stay in the app."},{"name":"Sensing","description":"Wi-Fi presence sensing: your sensors and the anomalies they raised (after-hours motion, unexpected inactivity, a sensor gone offline), read only. Requires the Sensing module. Acknowledging and resolving anomalies stays in the app; for real-time alerts subscribe to the sensing.* webhooks."},{"name":"AI Chat","description":"Call the Lynxbe AI from your own app. Every call is checked against your AI plan and credits first and charged to your AI credits after. Answers never include internal system details."},{"name":"Lynxbe Apps","description":"The dedicated address of an app built on Lynxbe (first: OpenLex): /api/v1/apps/{app}/*. The app reads its public config, signs a user of one of its businesses in and works through an app session: a 15-minute access token plus a rotating 30-day refresh token. The business's API key with the app:session scope turns the app on and never leaves the server. Send the access token as Authorization: Bearer to the app's features (Lynxbe AI, legal); a token of another app is refused."}],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"paths":{"/conversations":{"get":{"operationId":"wa_conversations_list","summary":"List Conversations","description":"All conversations across your channels (WhatsApp, SMS, email and the rest of the unified inbox), newest message first. Filter by channel, by the inbox handling status, or by text.\n\nRequired API key scope: `read:conversations`.","tags":["WhatsApp"],"parameters":[{"name":"page","in":"query","description":"Page number for pagination","schema":{"type":"integer","default":1,"example":1}},{"name":"limit","in":"query","description":"Items per page (1-100)","schema":{"type":"integer","default":20,"example":20}},{"name":"status","in":"query","description":"Archive state (active and pending both mean not archived)","schema":{"type":"string","enum":["active","archived","pending"]}},{"name":"channel","in":"query","description":"Only this channel. Instagram and Telegram direct messages arrive here too; read their messages with Get Conversation","schema":{"type":"string","enum":["whatsapp","sms","email","instagram_dm","telegram"],"example":"instagram_dm"}},{"name":"conversation_status","in":"query","description":"Inbox handling status","schema":{"type":"string","enum":["open","pending","resolved","closed"]}},{"name":"search","in":"query","description":"Text in the customer name, phone or last message (max 100)","schema":{"type":"string"}}],"responses":{"200":{"description":"List of conversations","content":{"application/json":{"example":{"success":true,"data":[{"id":"0f1e2d3c-4b5a-4968-8778-695a4b3c2d1e","customer_phone":"+972501234567","customer_name":"Dana Levi","status":"active","conversation_status":"open","last_message":"Hello!","last_message_at":"2026-09-29T10:30:00.000Z","unread_count":2,"channel":"whatsapp","created_at":"2026-09-01T08:00:00.000Z","updated_at":"2026-09-29T10:30:00.000Z"}],"pagination":{"page":1,"limit":20}}}}},"400":{"description":"Bad request - CHANNEL_INVALID, CONVERSATION_STATUS_INVALID or SEARCH_TOO_LONG"},"401":{"description":"Unauthorized - Invalid or missing API key"}},"x-required-scope":"read:conversations"}},"/conversations/{id}":{"get":{"operationId":"wa_conversation_get","summary":"Get Conversation","description":"Get a specific conversation by ID, including its most recent messages (reactions excluded).\n\nRequired API key scope: `read:conversations`.","tags":["WhatsApp"],"parameters":[{"name":"id","in":"path","required":true,"description":"Conversation ID (UUID)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max messages to return (default 50)","schema":{"type":"integer","default":50}},{"name":"before","in":"query","description":"Only return messages created before this timestamp (ISO 8601)","schema":{"type":"string"}}],"responses":{"200":{"description":"Conversation details with messages","content":{"application/json":{"example":{"success":true,"data":{"conversation":{"id":"...","status":"active"},"messages":[{"id":"...","direction":"inbound","type":"text","content":"Hello","status":"delivered","created_at":"2026-01-15T10:30:00Z"}]}}}}},"404":{"description":"Conversation not found"}},"x-required-scope":"read:conversations"}},"/messages/whatsapp":{"post":{"operationId":"wa_messages_send","summary":"Send Message","description":"Send a WhatsApp message to a contact.\n\nRequired API key scope: `send:whatsapp`.","tags":["WhatsApp"],"requestBody":{"description":"Message content and recipient","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","description":"Recipient phone number (E.164 format)","example":"+972501234567"},"message":{"type":"string","description":"Text message content","example":"Hello! This is a test message from Lynxbe."},"account_id":{"type":"integer","description":"WhatsApp account ID (optional, uses default if omitted)"}},"required":["to","message"]}}}},"responses":{"200":{"description":"Message sent successfully","content":{"application/json":{"example":{"success":true,"data":{"messageId":"wamid.abc123...","status":"sent"}}}}},"400":{"description":"Required fields: to, message"},"429":{"description":"Rate limit exceeded"}},"x-required-scope":"send:whatsapp"}},"/templates/whatsapp":{"get":{"operationId":"wa_templates_list","summary":"List Templates","description":"Get WhatsApp message templates for your business (includes shared system templates). Returns name, category, language, and approval status.\n\nRequired API key scope: `read:templates`.","tags":["WhatsApp"],"parameters":[{"name":"status","in":"query","description":"Filter by template approval status","schema":{"type":"string","enum":["APPROVED","PENDING","REJECTED"]}}],"responses":{"200":{"description":"List of message templates"}},"x-required-scope":"read:templates"}},"/messages/whatsapp/template":{"post":{"operationId":"wa_template_send","summary":"Send Template Message","description":"Send a pre-approved WhatsApp template message to a contact. Templates must be approved by Meta before use.\n\nRequired API key scope: `send:whatsapp`.","tags":["WhatsApp"],"requestBody":{"description":"Template message details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","description":"Recipient phone number (E.164 format)","example":"+972501234567"},"template_name":{"type":"string","description":"Name of the approved template","example":"hello_world"},"language_code":{"type":"string","description":"Template language code (defaults to \"en\")","example":"en"},"components":{"type":"array","items":{},"description":"Template variable components (header, body, button parameters)","example":[{"type":"body","parameters":[{"type":"text","text":"John"}]}]},"account_id":{"type":"integer","description":"WhatsApp account ID (optional, uses default if omitted)"}},"required":["to","template_name"]}}}},"responses":{"200":{"description":"Template message sent","content":{"application/json":{"example":{"success":true,"data":{"messageId":"wamid.abc123...","status":"sent","template":"hello_world"}}}}},"400":{"description":"Required fields: to, template_name"},"404":{"description":"Template not found or not approved"}},"x-required-scope":"send:whatsapp"}},"/contacts/import":{"post":{"operationId":"wa_contacts_sync","summary":"Bulk Import Contacts","description":"Bulk import contacts (up to 500 per request). Each row must include a phone number; contacts are upserted by business + phone (existing contacts are updated with any new name/email/company). Rows without a phone are skipped and counted. Returns { imported, skipped }.\n\nRequired API key scope: `write:contacts`.","tags":["WhatsApp"],"requestBody":{"description":"Contacts to import","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contacts":{"type":"array","items":{},"description":"Array of contact rows: { name?, phone (required), email?, company?, notes? }. Max 500 rows."}},"required":["contacts"]}}}},"responses":{"200":{"description":"Import result with imported and skipped counts"},"400":{"description":"Missing contacts array or more than 500 rows"}},"x-required-scope":"write:contacts"}},"/labels":{"get":{"operationId":"wa_labels_list","summary":"List Labels","description":"Get all active conversation labels for the business, ordered by sort order. Includes name, color, icon, and description.\n\nRequired API key scope: `read:conversations`.","tags":["WhatsApp"],"responses":{"200":{"description":"List of labels"}},"x-required-scope":"read:conversations"}},"/quick-replies":{"get":{"operationId":"wa_quick_replies_list","summary":"List Quick Replies","description":"Get all active quick replies with their shortcut, message type, and category name.\n\nRequired API key scope: `read:templates`.","tags":["WhatsApp"],"responses":{"200":{"description":"List of quick replies"}},"x-required-scope":"read:templates"}},"/groups":{"get":{"operationId":"wa_groups_list","summary":"List Groups","description":"Get all active WhatsApp groups managed by the business, including member counts.\n\nRequired API key scope: `read:conversations`.","tags":["WhatsApp"],"responses":{"200":{"description":"List of groups"}},"x-required-scope":"read:conversations"}},"/voice/calls":{"get":{"operationId":"voice_calls_list","summary":"List Calls","description":"Get call history for your business, proxied from the voice infrastructure, with date/caller/status filtering.\n\nRequired API key scope: `read:voice`.","tags":["Voice & Softphone"],"parameters":[{"name":"from","in":"query","description":"Start date for filtering","schema":{"type":"string","example":"2026-01-01"}},{"name":"to","in":"query","description":"End date for filtering","schema":{"type":"string","example":"2026-01-31"}},{"name":"caller","in":"query","description":"Filter by caller number","schema":{"type":"string"}},{"name":"status","in":"query","description":"Call status filter","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max results (max 100)","schema":{"type":"integer"}}],"responses":{"200":{"description":"List of calls"}},"x-required-scope":"read:voice"},"post":{"operationId":"voice_call_initiate","summary":"Initiate Call","description":"Start a new outbound call via the voice infrastructure. Fires the call.started webhook on success.\n\nRequired API key scope: `manage:voice`.","tags":["Voice & Softphone"],"requestBody":{"description":"Call parameters","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","description":"Destination phone number","example":"+972501234567"},"from":{"type":"string","description":"Originating extension (default 1000)","example":"1000"},"callerId":{"type":"string","description":"Caller ID to present"},"record":{"type":"boolean","description":"Record the call"},"timeout":{"type":"integer","description":"Ring timeout in seconds"}},"required":["to"]}}}},"responses":{"201":{"description":"Call initiated"},"400":{"description":"Invalid parameters"}},"x-required-scope":"manage:voice"}},"/voice/recordings/{id}/audio":{"get":{"operationId":"voice_recording_audio","summary":"Play Recording (audio stream)","description":"Stream the recording AUDIO itself — this is the endpoint to use when someone asks to listen to or play a call. Returns audio bytes (not JSON), supports Range requests for seeking. The recording id comes from GET /voice/transcripts (call_recording_id).\n\nRequired API key scope: `read:voice`.","tags":["Voice & Softphone"],"parameters":[{"name":"id","in":"path","required":true,"description":"Recording id, e.g. from call_recording_id (may be namespaced, \"wa:<uuid>\")","schema":{"type":"string"}}],"responses":{"200":{"description":"Audio bytes (audio/mpeg or the stored content type)"},"206":{"description":"Partial content when a Range header is sent"},"404":{"description":"No stored audio for this recording id"}},"x-required-scope":"read:voice"}},"/voice/recordings/{id}":{"get":{"operationId":"voice_recording_get","summary":"Get Recording","description":"Get a call recording by ID, proxied from the voice infrastructure.\n\nRequired API key scope: `read:voice`.","tags":["Voice & Softphone"],"parameters":[{"name":"id","in":"path","required":true,"description":"Recording ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Recording details / URL"},"404":{"description":"Recording not found"}},"x-required-scope":"read:voice"}},"/voice/queues":{"get":{"operationId":"voice_queues_list","summary":"List Queues","description":"Get all call queues and their status, proxied from the voice infrastructure.\n\nRequired API key scope: `read:voice`.","tags":["Voice & Softphone"],"responses":{"200":{"description":"List of queues with member count and wait times"}},"x-required-scope":"read:voice"}},"/voice/extensions":{"get":{"operationId":"voice_extensions_list","summary":"List Extensions","description":"Get all active voice extensions for the business, including display name, caller IDs, and shared status.\n\nRequired API key scope: `read:voice`.","tags":["Voice & Softphone"],"responses":{"200":{"description":"List of extensions"}},"x-required-scope":"read:voice"}},"/voice/transcripts":{"get":{"operationId":"voice_transcripts_list","summary":"List Call Summaries","description":"List recent call transcripts/summaries for your business, newest first. Summarized calls only by default, so \"the last summarized call\" is one request — no recording id needed. EVERY row carries WHO the call was with: caller_phone plus customer_profile_id/customer_name/customer_email when the number matches a customer, so you can name the customer and link to Customers 360 instead of saying the transcript has no identifying details. Also returns sentiment, language and duration. Full transcript text is NOT included (large, and carries caller PII): fetch one with Get Call Transcript.\n\nRequired API key scope: `read:voice`.","tags":["Voice & Softphone"],"parameters":[{"name":"limit","in":"query","description":"Max rows (1-50, default 10)","schema":{"type":"integer"}},{"name":"summarized","in":"query","description":"Set 'false' to include rows that have no summary yet (default 'true')","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Rows of { call_recording_id, provider, status, summary, sentiment, language, duration_seconds, caller_phone, customer_profile_id, customer_name, customer_email, created_at, transcript_chars }"}},"x-required-scope":"read:voice"}},"/voice/transcripts/{callRecordingId}":{"get":{"operationId":"voice_transcript_get","summary":"Get Call Transcript","description":"Get the transcript and AI summary for a recorded call.\n\nRequired API key scope: `read:voice`.","tags":["Voice & Softphone"],"parameters":[{"name":"callRecordingId","in":"path","required":true,"description":"Call recording ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Transcript with provider, status, transcript text, and summary"},"404":{"description":"Transcript not found"}},"x-required-scope":"read:voice"}},"/messages/sms":{"post":{"operationId":"sms_send","summary":"Send SMS","description":"Send an SMS message.\n\nRequired API key scope: `send:sms`.","tags":["SMS"],"requestBody":{"description":"SMS content","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","description":"Recipient phone number","example":"+972501234567"},"from":{"type":"string","description":"Sender ID or number"},"message":{"type":"string","description":"Message content"}},"required":["to","message"]}}}},"responses":{"202":{"description":"SMS queued"}},"x-required-scope":"send:sms"}},"/messages/sms/bulk":{"post":{"operationId":"sms_bulk","summary":"Bulk SMS","description":"Send the same SMS message to multiple recipients (max 50 per request). Sends sequentially and returns a per-recipient result.\n\nRequired API key scope: `send:sms`.","tags":["SMS"],"requestBody":{"description":"Bulk SMS request","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"recipients":{"type":"array","items":{},"description":"List of phone numbers (max 50)"},"message":{"type":"string","description":"Message content"},"from":{"type":"string","description":"Sender ID or number (optional, uses business default)"}},"required":["recipients","message"]}}}},"responses":{"200":{"description":"Bulk send result","content":{"application/json":{"example":{"success":true,"data":{"total":3,"sent":2,"failed":1,"results":[{"to":"+972501234567","ok":true}]}}}}},"400":{"description":"More than 50 recipients or missing required fields"}},"x-required-scope":"send:sms"}},"/templates/email":{"get":{"operationId":"email_templates_list","summary":"List Templates","description":"Get all active email templates with Hebrew/English subjects and category. Use the returned template IDs with the Send Template Email endpoint.\n\nRequired API key scope: `read:templates`.","tags":["Email Marketing"],"responses":{"200":{"description":"List of templates"}},"x-required-scope":"read:templates"}},"/messages/email":{"post":{"operationId":"email_send","summary":"Send Email","description":"Send an email using a template or custom content.\n\nRequired API key scope: `send:email`.","tags":["Email Marketing"],"requestBody":{"description":"Email content","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","description":"Recipient email address","example":"user@example.com"},"subject":{"type":"string","description":"Email subject line","example":"Welcome to Lynxbe"},"body":{"type":"string","description":"Plain text email body (use body or html, not both)"},"html":{"type":"string","description":"HTML email body (use html or body, not both)","example":"<h1>Welcome</h1><p>Thanks for joining!</p>"},"from":{"type":"string","description":"Sender email address (optional, uses business default)"}},"required":["to","subject"]}}}},"responses":{"202":{"description":"Email queued"}},"x-required-scope":"send:email"}},"/messages/email/template":{"post":{"operationId":"email_send_template","summary":"Send Template Email","description":"Send an email using a pre-built template with dynamic variables. Supports Hebrew (RTL) and English templates with automatic {{variable}} substitution. Includes CC, BCC, reply-to, and subject override support.\n\nRequired API key scope: `send:email`.","tags":["Email Marketing"],"requestBody":{"description":"Template email parameters","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"string","description":"ID of the email template (UUID). Get template IDs from the List Templates endpoint","example":"6aee9edb-afa8-4a44-ac55-af3bc4817b34"},"to":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Recipient email address. String for single recipient, array for multiple"},"language":{"type":"string","description":"Template language: \"he\" (Hebrew/RTL) or \"en\" (English/LTR). Default: \"he\"","example":"he"},"variables":{"type":"object","additionalProperties":true,"description":"Key-value pairs for template {{placeholders}}. Keys must match variable names in the template","example":{"customer_name":"Israel Israeli","otp_code":"482910"}},"from":{"type":"string","description":"Sender email address. Must be a verified domain/mailbox in your account. Default: business default sender","example":"info@yourdomain.com"},"cc":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Carbon copy recipients. String or array of email addresses"},"bcc":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Blind carbon copy recipients. String or array of email addresses"},"reply_to":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Reply-to address. Where recipient replies will be directed"},"subject_override":{"type":"string","description":"Override the template subject line. Replaces the template default subject entirely"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom metadata for tracking. Returned in response and webhook events","example":{"campaign":"onboarding","user_id":"abc123"}}},"required":["template_id","to"]}}}},"responses":{"200":{"description":"Email sent successfully","content":{"application/json":{"example":{"success":true,"data":{"message_id":"01000...","transport":"ses","template_id":"6aee9edb-...","language":"he","subject":"...","to":["recipient@example.com"],"cc":["manager@example.com"],"variables_used":["customer_name","otp_code"],"metadata":{"campaign":"verification"}}}}}},"400":{"description":"Missing required fields (template_id, to), invalid email, or invalid language"},"404":{"description":"Template not found or empty"},"500":{"description":"Failed to send email (SES/SMTP error)"}},"x-required-scope":"send:email"}},"/email/campaigns":{"get":{"operationId":"email_campaigns_list","summary":"List Campaigns","description":"Get email campaigns sent by your business, with subject, recipient count, sent/failed counts, and timestamps.\n\nRequired API key scope: `read:templates`.","tags":["Email Marketing"],"responses":{"200":{"description":"List of campaigns"}},"x-required-scope":"read:templates"},"post":{"operationId":"email_campaign_send","summary":"Send Campaign","description":"Send an email campaign to a list of recipients (up to 200 per request). Provide either a template_id (rendered per recipient in the requested language) or a raw subject + html_content. Each recipient is sent individually and per-recipient results are recorded on the campaign.\n\nRequired API key scope: `send:email`.","tags":["Email Marketing"],"requestBody":{"description":"Campaign content and recipients","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"string","description":"Email template ID (alternative to subject + html_content)"},"subject":{"type":"string","description":"Email subject (required when no template_id)"},"html_content":{"type":"string","description":"HTML body (required when no template_id)"},"recipients":{"type":"array","items":{},"description":"Array of recipient email addresses. Max 200."},"language":{"type":"string","description":"Template language (en/he)"}},"required":["recipients"]}}}},"responses":{"200":{"description":"Campaign sent with per-recipient results and sent/failed counts"},"400":{"description":"Missing recipients, more than 200 recipients, or missing content"}},"x-required-scope":"send:email"}},"/bookings":{"get":{"operationId":"booking_list","summary":"List Bookings","description":"The business's bookings, earliest first. With no from / to it starts today. Filter by status, dates, employee, service or customer phone; page with limit / offset (pagination.total is the full count). Requires the booking module on the plan.\n\nRequired API key scope: `read:bookings`.","tags":["Booking"],"parameters":[{"name":"from","in":"query","description":"First day (YYYY-MM-DD). Default today, Israel time, when neither from nor to is given","schema":{"type":"string","example":"2026-10-01"}},{"name":"to","in":"query","description":"Last day (YYYY-MM-DD)","schema":{"type":"string","example":"2026-10-31"}},{"name":"status","in":"query","description":"One of: booking_request, pending_approval, pending, confirmed, awaiting_attendance_confirmation, attendance_confirmed, attendance_not_confirmed, arrived, in_service, completed, no_show, change_requested, cancellation_requested, cancelled, late_cancellation, consult_no_deal","schema":{"type":"string","example":"confirmed"}},{"name":"employee_id","in":"query","description":"Only this employee's bookings","schema":{"type":"integer","example":601}},{"name":"service_id","in":"query","description":"Only this service","schema":{"type":"integer","example":501}},{"name":"customer_phone","in":"query","description":"Customer phone in any form (0501234567, +972501234567, 972-50-123-4567): matched by its digits in 972 form","schema":{"type":"string","example":"0501234567"}},{"name":"limit","in":"query","description":"Page size (1-100, default 20; larger is clamped to 100)","schema":{"type":"integer","example":20}},{"name":"offset","in":"query","description":"Rows to skip (default 0)","schema":{"type":"integer","example":0}}],"responses":{"200":{"description":"Bookings","content":{"application/json":{"example":{"success":true,"data":[{"id":9001,"service_id":501,"employee_id":601,"customer_name":"Dana Levi","customer_phone":"0501234567","customer_email":"dana@example.com","booking_date":"2026-10-20T00:00:00.000Z","start_time":"10:00:00","end_time":"11:00:00","status":"confirmed"}],"pagination":{"limit":20,"offset":0,"total":1}}}}},"400":{"description":"A filter in the wrong shape (INVALID_INPUT, with field)"},"403":{"description":"Booking is not on the plan (MODULE_LOCKED)"}},"x-required-scope":"read:bookings"},"post":{"operationId":"booking_create","summary":"Create Booking","description":"Book a slot for a customer, e.g. from your own booking widget. Staff-blocked time and a slot already booked for the employee are refused; a group service (capacity above 1) takes bookings until its seats are full. Two simultaneous create calls for one slot never both succeed. Fires booking.created. Requires the booking module on the plan.\n\nRequired API key scope: `write:bookings`.","tags":["Booking"],"requestBody":{"description":"Booking details. At least one of customer_name, customer_phone or customer_email.","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"customer_name":{"type":"string","description":"Customer name","example":"Dana Levi"},"customer_phone":{"type":"string","description":"Customer phone","example":"0501234567"},"customer_email":{"type":"string","description":"Customer email","example":"dana@example.com"},"booking_date":{"type":"string","description":"Date (YYYY-MM-DD); default today, Israel time","example":"2026-10-20"},"start_time":{"type":"string","description":"Start time (HH:MM)","example":"10:00"},"end_time":{"type":"string","description":"End time (HH:MM); default one hour after start","example":"11:00"},"service_id":{"type":"integer","description":"One of your services (GET /api/v1/services)","example":501},"employee_id":{"type":"integer","description":"One of your employees","example":601},"party_size":{"type":"integer","description":"Seats to book (default 1). Above 1 only for a group service, up to its capacity","example":2},"notify_customer":{"type":"boolean","description":"true = send the customer the booking confirmation the business sends from its booking page (its own WhatsApp number and approved templates, or email), to the phone / email in this request - only when the business has confirmations on. Needs customer_name and a phone or email. Default false: nothing is sent, e.g. when your widget confirms the booking itself","example":false}},"required":["start_time"]}}}},"responses":{"201":{"description":"Booking created. notification: not_requested (notify_customer not true), sent (a confirmation went out), pending (still sending after 8 seconds; the booking is saved, do not retry it), or not_sent (asked, but nothing went out: confirmations off, no WhatsApp account or template, no phone / email, or the send was refused)","content":{"application/json":{"example":{"success":true,"data":{"id":9001,"booking_date":"2026-10-20T00:00:00.000Z","start_time":"10:00:00","end_time":"11:00:00","status":"confirmed","party_size":1,"customer_id":77,"notification":"not_requested"}}}}},"400":{"description":"Missing customer or start_time; a date, time or email in the wrong shape or end_time not after start_time (INVALID_INPUT); service_id is not one of your active services, or employee_id is not one of your employees - active or not (INVALID_REFERENCE); party_size not a whole number of at least 1 (INVALID_PARTY_SIZE) or above the service capacity (PARTY_SIZE_ABOVE_CAPACITY); notify_customer not a boolean; with notify_customer, customer_phone not a phone number or no customer_name (INVALID_INPUT)"},"403":{"description":"Booking is not on the plan (MODULE_LOCKED)"},"409":{"description":"The slot is blocked by staff; already booked for this employee (BOOKING_CONFLICT); or the class has no room (SLOT_FULL / NOT_ENOUGH_SEATS, with capacity and seats_remaining)","content":{"application/json":{"example":{"success":false,"error":"This session is full","code":"SLOT_FULL","capacity":3,"seats_remaining":0}}}},"503":{"description":"The plan could not be checked; try again (PLAN_CHECK_FAILED)"}},"x-required-scope":"write:bookings"}},"/bookings/{id}":{"get":{"operationId":"booking_get","summary":"Get Booking","description":"One booking of your business, with the service and employee names. Requires the booking module on the plan.\n\nRequired API key scope: `read:bookings`.","tags":["Booking"],"parameters":[{"name":"id","in":"path","required":true,"description":"Booking id","schema":{"type":"integer","example":9001}}],"responses":{"200":{"description":"The booking","content":{"application/json":{"example":{"success":true,"data":{"id":9001,"service_id":501,"employee_id":601,"customer_name":"Dana Levi","customer_phone":"0501234567","customer_email":"dana@example.com","booking_date":"2026-10-20T00:00:00.000Z","start_time":"10:00:00","end_time":"11:00:00","status":"confirmed","service_name":"Haircut","employee_name":"Noa Cohen","notes":null,"cancellation_reason":null,"created_at":"2026-10-13T09:00:00.000Z","updated_at":"2026-10-13T09:00:00.000Z"}}}}},"403":{"description":"Booking is not on the plan (MODULE_LOCKED)"},"404":{"description":"No booking with this id in your business"}},"x-required-scope":"read:bookings"},"patch":{"operationId":"booking_update","summary":"Update Booking","description":"Change a booking's notes and customer details (name, phone, email). Send only the fields to change; null or an empty string clears one. The slot, status, employee and service are not changed here - a move is Reschedule Booking, a cancellation is Cancel Appointment - and naming one of them, or any other field, is refused rather than ignored. A change may not leave the booking with no name, phone or email. A new phone moves the booking to the customer card of your business that already holds it (when only the email changes, the card that holds the new email); no such card, or details cleared, keeps the current card, and no card is created or renamed. customer_relinked says whether it moved. Works on a booking in any status (a note after the visit). Only fields that really change are saved (a phone in another format or an email in other letter case is the same value); fires booking.updated. The customer is not messaged.\n\nRequired API key scope: `write:bookings`.","tags":["Booking"],"parameters":[{"name":"id","in":"path","required":true,"description":"Booking ID","schema":{"type":"integer","example":9001}}],"requestBody":{"description":"The fields to change","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"notes":{"type":"string","description":"Notes on the booking (up to 5000 characters); null clears them","example":"Bring the X-ray"},"customer_name":{"type":"string","description":"Customer name (up to 255 characters)","example":"Dana Levi"},"customer_phone":{"type":"string","description":"Customer phone, 9-15 digits with an optional +; spaces, dashes and brackets are removed","example":"+972525550101"},"customer_email":{"type":"string","description":"Customer email; a new one is stored in lower case","example":"dana@example.com"}}}}}},"responses":{"200":{"description":"Saved; changed lists the fields that really changed (empty when the values were already the same)","content":{"application/json":{"example":{"success":true,"data":{"id":9001,"status":"confirmed","booking_date":"2026-10-20","start_time":"10:00:00","end_time":"11:00:00","notes":"Bring the X-ray","customer_name":"Dana Levi","customer_phone":"+972525550101","customer_email":"dana@example.com","customer_id":88,"customer_relinked":false,"changed":["customer_name","notes"]}}}}},"400":{"description":"No field to change (INVALID_INPUT); a value that is not valid (INVALID_INPUT, field names it); a field changed by another endpoint - status, booking_date, start_time, end_time, employee_id, service_id (FIELD_NOT_EDITABLE); an unknown field (UNKNOWN_FIELD); or the change would leave no name, phone or email (CONTACT_REQUIRED)"},"403":{"description":"Booking is not on the plan (MODULE_LOCKED)"},"404":{"description":"No booking with this id in your business"},"500":{"description":"The booking could not be updated; nothing was saved"},"503":{"description":"The plan check is temporarily unavailable (PLAN_CHECK_FAILED); retry"}},"x-required-scope":"write:bookings"}},"/availability":{"get":{"operationId":"booking_availability","summary":"Check Availability","description":"Get free time slots for a date, computed from business hours minus existing bookings and blocked slots. Calendar-sync busy times are validated at booking time.\n\nRequired API key scope: `read:bookings`.","tags":["Booking"],"parameters":[{"name":"date","in":"query","required":true,"description":"Date to check (YYYY-MM-DD)","schema":{"type":"string","example":"2026-01-15"}},{"name":"service_id","in":"query","description":"Service ID — slot length uses the service duration (default 60 minutes)","schema":{"type":"string"}},{"name":"employee_id","in":"query","description":"Specific employee (optional)","schema":{"type":"string"}}],"responses":{"200":{"description":"Available time slots","content":{"application/json":{"example":{"success":true,"data":{"date":"2026-01-15","slots":[{"start_time":"09:00","end_time":"10:00"}]}}}}}},"x-required-scope":"read:bookings"}},"/booking/staff":{"get":{"operationId":"booking_staff_list","summary":"List Booking Staff","description":"Who the app would book: the active staff of your business, by name. With service_id, only the staff allowed to perform that service, each with the length and price that person charges for it (their own setting, else the service's; a service with no length set counts 30 minutes, as the app books it). The public create and GET /api/v1/availability do not apply a person's own length: they use the service's length (60 minutes when it has none), so pass end_time to book a person's length - a slot GET /api/v1/availability shows can then still answer 409 BOOKING_CONFLICT if the longer booking does not fit. The create accepts any employee id of your business, inactive ones included, so pick from this list. Names only - no phone, email or pay. Requires the booking module on the plan.\n\nRequired API key scope: `read:bookings`.","tags":["Booking"],"parameters":[{"name":"service_id","in":"query","description":"Only staff who perform this service (an id from GET /api/v1/services)","schema":{"type":"integer","example":501}}],"responses":{"200":{"description":"data.staff; with service_id also data.service and each person's duration / price (without it data.service is null)","content":{"application/json":{"example":{"success":true,"data":{"service":{"id":501,"name":"Colour","duration_minutes":60,"price":"300.00"},"staff":[{"id":601,"name":"Avi Levi","branch_id":null,"duration_minutes":90,"price":"450.00"},{"id":602,"name":"Dana Cohen","branch_id":null,"duration_minutes":60,"price":"300.00"}]}}}}},"400":{"description":"service_id is not a positive integer (INVALID_SERVICE_ID)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Booking is not on the plan (MODULE_LOCKED), or the key has no read:bookings (\"Insufficient scope. Required: read:bookings\")"},"404":{"description":"No active service with this id in your business (SERVICE_NOT_FOUND)"},"500":{"description":"Failed to list staff (reported to us; retry)"}},"x-required-scope":"read:bookings"}},"/bookings/{id}/reschedule":{"post":{"operationId":"booking_reschedule","summary":"Reschedule Booking","description":"Move a booking to another date or time. The same checks as a new booking: staff-blocked time and a slot already booked for the employee are refused, a class takes the move only while it has seats, and two simultaneous API calls for one slot never both succeed. The employee and service stay. Only a booking still ahead can move (not arrived, in service, cancelled, completed or a no-show). Reminders are re-sent for the new time; fires booking.rescheduled with rescheduled_by \"api\". The customer is not messaged.\n\nRequired API key scope: `write:bookings`.","tags":["Booking"],"parameters":[{"name":"id","in":"path","required":true,"description":"Booking ID","schema":{"type":"integer","example":9001}}],"requestBody":{"description":"The new slot","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"booking_date":{"type":"string","description":"New date (YYYY-MM-DD)","example":"2026-10-21"},"start_time":{"type":"string","description":"New start time (HH:MM)","example":"12:00"},"end_time":{"type":"string","description":"New end time (HH:MM); default keeps the booking's length","example":"13:00"}},"required":["booking_date","start_time"]}}}},"responses":{"200":{"description":"Booking moved; previous is where it was","content":{"application/json":{"example":{"success":true,"data":{"id":9001,"booking_date":"2026-10-21","start_time":"12:00:00","end_time":"13:00:00","status":"confirmed","employee_id":601,"service_id":501,"previous":{"booking_date":"2026-10-20","start_time":"10:00:00","end_time":"11:00:00"}}}}}},"400":{"description":"booking_date / start_time missing or in the wrong shape, booking_date in the past, end_time not after start_time, the booking would run past midnight, or employee_id / service_id sent (they cannot change here) (INVALID_INPUT, with field)"},"403":{"description":"Booking is not on the plan (MODULE_LOCKED)"},"404":{"description":"No booking with this id in your business"},"409":{"description":"The new slot is blocked by staff; already booked for this employee (BOOKING_CONFLICT); the class has no room (SLOT_FULL / NOT_ENOUGH_SEATS); the booking is no longer ahead - arrived, in service, cancelled, completed or a no-show (BOOKING_NOT_ACTIVE); or it changed while moving (BOOKING_CHANGED - read it again and retry)"}},"x-required-scope":"write:bookings"}},"/bookings/{id}/confirm":{"post":{"operationId":"booking_confirm","summary":"Confirm Booking","description":"Confirm a booking that is a request, pending, waiting for approval, or has a change or cancellation request open. A cancelled booking or a no-show is not reopened here - make a new booking. The move must be one allowed from the booking's current status (409 INVALID_STATUS_TRANSITION names both); the same status again changes nothing (changed: false). Writes the status history and moves the CRM card; the business's own automation rules and workflows for this status run as for a change made in the app. The API sends its own message to the customer only with notify_customer true: the confirmation message (the business's consent, quiet-hours and channel rules apply). Fires booking.updated.\n\nRequired API key scope: `write:bookings`.","tags":["Booking"],"parameters":[{"name":"id","in":"path","required":true,"description":"Booking ID","schema":{"type":"integer","example":9001}}],"requestBody":{"description":"Optional","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Why, for the status history (up to 500 characters)","example":"Marked by the front desk"},"notify_customer":{"type":"boolean","description":"true: send the customer the business's own notice for this status (default false: no message)","example":false}}}}}},"responses":{"200":{"description":"Status set; changed is false when it already was. notification: not_requested (no notify_customer), requested (handed to the business's notice for this status), not_sent (notify_customer, but the status was already set)","content":{"application/json":{"example":{"success":true,"data":{"id":9001,"status":"confirmed","previous_status":"pending","changed":true,"notification":"not_requested","booking_date":"2026-10-20","start_time":"10:00:00","end_time":"11:00:00"}}}}},"400":{"description":"A field that is not reason or notify_customer (UNKNOWN_FIELD); a reason that is not text or is too long, or a notify_customer that is not true / false (INVALID_INPUT)"},"403":{"description":"Booking is not on the plan (MODULE_LOCKED)"},"404":{"description":"No booking with this id in your business"},"409":{"description":"Not allowed from the current status (INVALID_STATUS_TRANSITION, with from_status and to_status)"},"500":{"description":"The status could not be changed; nothing was saved"},"503":{"description":"The plan check is temporarily unavailable (PLAN_CHECK_FAILED); retry"}},"x-required-scope":"write:bookings"}},"/bookings/{id}/complete":{"post":{"operationId":"booking_complete","summary":"Complete Booking","description":"Mark a booking as completed once it has started (also corrects a no-show). The move must be one allowed from the booking's current status (409 INVALID_STATUS_TRANSITION names both); the same status again changes nothing (changed: false). Writes the status history and moves the CRM card; the business's own automation rules and workflows for this status run as for a change made in the app. The API sends its own message to the customer only with notify_customer true: the thank-you message (the business's consent, quiet-hours and channel rules apply). Fires booking.updated and booking.completed (the event an invoice for the meeting is raised from).\n\nRequired API key scope: `write:bookings`.","tags":["Booking"],"parameters":[{"name":"id","in":"path","required":true,"description":"Booking ID","schema":{"type":"integer","example":9001}}],"requestBody":{"description":"Optional","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Why, for the status history (up to 500 characters)","example":"Marked by the front desk"},"notify_customer":{"type":"boolean","description":"true: send the customer the business's own notice for this status (default false: no message)","example":false}}}}}},"responses":{"200":{"description":"Status set; changed is false when it already was. notification: not_requested (no notify_customer), requested (handed to the business's notice for this status), not_sent (notify_customer, but the status was already set)","content":{"application/json":{"example":{"success":true,"data":{"id":9001,"status":"completed","previous_status":"confirmed","changed":true,"notification":"not_requested","booking_date":"2026-10-20","start_time":"10:00:00","end_time":"11:00:00"}}}}},"400":{"description":"A field that is not reason or notify_customer (UNKNOWN_FIELD); a reason that is not text or is too long, or a notify_customer that is not true / false (INVALID_INPUT)"},"403":{"description":"Booking is not on the plan (MODULE_LOCKED)"},"404":{"description":"No booking with this id in your business"},"409":{"description":"Not allowed from the current status (INVALID_STATUS_TRANSITION, with from_status and to_status); or the booking has not started yet (BOOKING_NOT_STARTED)"},"500":{"description":"The status could not be changed; nothing was saved"},"503":{"description":"The plan check is temporarily unavailable (PLAN_CHECK_FAILED); retry"}},"x-required-scope":"write:bookings"}},"/bookings/{id}/no-show":{"post":{"operationId":"booking_no_show","summary":"Mark No-Show","description":"Mark that the customer did not come, once the booking has started (also corrects a completion). The move must be one allowed from the booking's current status (409 INVALID_STATUS_TRANSITION names both); the same status again changes nothing (changed: false). Writes the status history and moves the CRM card; the business's own automation rules and workflows for this status run as for a change made in the app. The API sends its own message to the customer only with notify_customer true: the no-show notice with the way to rebook (the business's consent, quiet-hours and channel rules apply). Fires booking.updated and the booking.no_show automation event.\n\nRequired API key scope: `write:bookings`.","tags":["Booking"],"parameters":[{"name":"id","in":"path","required":true,"description":"Booking ID","schema":{"type":"integer","example":9001}}],"requestBody":{"description":"Optional","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Why, for the status history (up to 500 characters)","example":"Marked by the front desk"},"notify_customer":{"type":"boolean","description":"true: send the customer the business's own notice for this status (default false: no message)","example":false}}}}}},"responses":{"200":{"description":"Status set; changed is false when it already was. notification: not_requested (no notify_customer), requested (handed to the business's notice for this status), not_sent (notify_customer, but the status was already set)","content":{"application/json":{"example":{"success":true,"data":{"id":9001,"status":"no_show","previous_status":"confirmed","changed":true,"notification":"not_requested","booking_date":"2026-10-20","start_time":"10:00:00","end_time":"11:00:00"}}}}},"400":{"description":"A field that is not reason or notify_customer (UNKNOWN_FIELD); a reason that is not text or is too long, or a notify_customer that is not true / false (INVALID_INPUT)"},"403":{"description":"Booking is not on the plan (MODULE_LOCKED)"},"404":{"description":"No booking with this id in your business"},"409":{"description":"Not allowed from the current status (INVALID_STATUS_TRANSITION, with from_status and to_status); or the booking has not started yet (BOOKING_NOT_STARTED)"},"500":{"description":"The status could not be changed; nothing was saved"},"503":{"description":"The plan check is temporarily unavailable (PLAN_CHECK_FAILED); retry"}},"x-required-scope":"write:bookings"}},"/bookings/{id}/cancel":{"post":{"operationId":"booking_cancel","summary":"Cancel Appointment","description":"Cancel an existing booking. Sets status to cancelled and fires the booking.cancelled webhook.\n\nRequired API key scope: `write:bookings`.","tags":["Booking"],"parameters":[{"name":"id","in":"path","required":true,"description":"Booking ID","schema":{"type":"integer"}}],"requestBody":{"description":"Cancellation reason","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Cancellation reason"}}}}}},"responses":{"200":{"description":"Booking cancelled"},"404":{"description":"Booking not found or already cancelled"}},"x-required-scope":"write:bookings"}},"/contacts":{"get":{"operationId":"customers_list","summary":"List Customers","description":"Get all customers with search and filtering.\n\nRequired API key scope: `read:contacts`.","tags":["Customers (CRM)"],"parameters":[{"name":"page","in":"query","description":"Page number","schema":{"type":"integer","default":1,"example":1}},{"name":"limit","in":"query","description":"Items per page (1-100)","schema":{"type":"integer","default":20,"example":20}},{"name":"search","in":"query","description":"Search by name, email, or phone","schema":{"type":"string"}}],"responses":{"200":{"description":"List of customers"}},"x-required-scope":"read:contacts"},"post":{"operationId":"customers_create","summary":"Create Customer","description":"Create a new customer record.\n\nRequired API key scope: `write:contacts`.","tags":["Customers (CRM)"],"requestBody":{"description":"Customer information","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"firstName":{"type":"string","description":"First name"},"lastName":{"type":"string","description":"Last name"},"email":{"type":"string","description":"Email address"},"phone":{"type":"string","description":"Phone number"},"tags":{"type":"array","items":{},"description":"Customer tags"}},"required":["firstName","phone"]}}}},"responses":{"201":{"description":"Customer created"}},"x-required-scope":"write:contacts"}},"/contacts/{id}":{"patch":{"operationId":"customers_update","summary":"Update Contact","description":"Update an existing contact. Only provided fields are changed. Fires the contact.updated webhook.\n\nRequired API key scope: `write:contacts`.","tags":["Customers (CRM)"],"parameters":[{"name":"id","in":"path","required":true,"description":"Contact ID","schema":{"type":"integer"}}],"requestBody":{"description":"Contact fields to update","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Contact name"},"phone":{"type":"string","description":"Phone number"},"email":{"type":"string","description":"Email address"},"company":{"type":"string","description":"Company name"},"notes":{"type":"string","description":"Free-text notes"}}}}}},"responses":{"200":{"description":"Contact updated"},"404":{"description":"Contact not found"}},"x-required-scope":"write:contacts"}},"/contacts/{id}/activity":{"get":{"operationId":"customers_history","summary":"Contact Activity","description":"Get a contact activity timeline merged across channels (conversations and bookings matched by phone/email), sorted newest first (max 100 entries).\n\nRequired API key scope: `read:contacts`.","tags":["Customers (CRM)"],"parameters":[{"name":"id","in":"path","required":true,"description":"Contact ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"Contact with activity timeline","content":{"application/json":{"example":{"success":true,"data":{"contact":{"id":1,"name":"..."},"activity":[{"type":"conversation","id":"...","date":"2026-01-15T10:30:00Z","summary":"..."}]}}}}},"404":{"description":"Contact not found"}},"x-required-scope":"read:contacts"}},"/parties":{"get":{"operationId":"parties_list","summary":"List People","description":"The business's people, most recently updated first, each with their channels. Paged with limit (default 20, max 100) and offset; pagination.total is the number of matching people. People being deleted or already erased, and records merged into another person, are never listed.\n\nRequired API key scope: `read:contacts`.","tags":["People (Identity Hub)"],"parameters":[{"name":"search","in":"query","description":"Part of a name (display, given, family or organisation), up to 200 characters","schema":{"type":"string"}},{"name":"tags","in":"query","description":"Comma-separated tags; a person with any of them matches (up to 20 tags of up to 64 characters)","schema":{"type":"string"}},{"name":"status","in":"query","description":"active or archived (default: both)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size (1-100, default 20)","schema":{"type":"number"}},{"name":"offset","in":"query","description":"Rows to skip (default 0)","schema":{"type":"number"}}],"responses":{"200":{"description":"People","content":{"application/json":{"example":{"success":true,"data":[{"id":"a1b2c3d4-0000-4000-8000-00000000a001","kind":"person","display_name":"Dana Levi","given_name":"Dana","family_name":"Levi","org_name":null,"preferred_language":"he","preferred_channel":"whatsapp","timezone":"Asia/Jerusalem","tags":["vip"],"segments":[],"lifecycle_stage":"customer","status":"active","created_at":"2026-09-01T08:00:00.000Z","updated_at":"2026-10-11T09:00:00.000Z","archived":false,"channels":[{"id":"b2c3d4e5-0000-4000-8000-00000000b001","kind":"phone","value":"+972501234567","label":"mobile","is_primary":true,"is_verified":true,"consent_marketing":null,"consent_utility":true,"last_inbound_at":"2026-10-10T17:20:00.000Z"}]}],"pagination":{"limit":20,"offset":0,"total":1}}}}},"400":{"description":"A filter is not valid (search, tags, status, limit, offset)"}},"x-required-scope":"read:contacts"}},"/parties/by-channel":{"get":{"operationId":"parties_by_channel","summary":"Find Person by Channel","description":"The person who owns a phone number, email, WhatsApp id or social id in this business. The value is normalised the way the platform stores it: a phone or a WhatsApp number in local or international form, an email in any case. Each kind is looked up on its own (a person who has only a WhatsApp channel is found with kind=whatsapp, not kind=phone).\n\nRequired API key scope: `read:contacts`.","tags":["People (Identity Hub)"],"parameters":[{"name":"kind","in":"query","required":true,"description":"phone, email, whatsapp, whatsapp_bsuid, sms, voice_extension, instagram_dm, fb_messenger, linkedin, x_dm, tiktok_dm, telegram, external_id or unknown","schema":{"type":"string"}},{"name":"value","in":"query","required":true,"description":"The phone, email or id on that channel (up to 320 characters)","schema":{"type":"string"}}],"responses":{"200":{"description":"The person","content":{"application/json":{"example":{"success":true,"data":{"id":"a1b2c3d4-0000-4000-8000-00000000a001","kind":"person","display_name":"Dana Levi","given_name":"Dana","family_name":"Levi","org_name":null,"preferred_language":"he","preferred_channel":"whatsapp","timezone":"Asia/Jerusalem","tags":["vip"],"segments":[],"lifecycle_stage":"customer","status":"active","created_at":"2026-09-01T08:00:00.000Z","updated_at":"2026-10-11T09:00:00.000Z","archived":false,"channels":[{"id":"b2c3d4e5-0000-4000-8000-00000000b001","kind":"phone","value":"+972501234567","label":"mobile","is_primary":true,"is_verified":true,"consent_marketing":null,"consent_utility":true,"last_inbound_at":"2026-10-10T17:20:00.000Z"}]}}}}},"400":{"description":"kind is not a channel kind; value missing or too long"},"404":{"description":"NOT_FOUND - no person in this business has this channel"}},"x-required-scope":"read:contacts"}},"/parties/{partyId}":{"get":{"operationId":"parties_get","summary":"Get Person","description":"One person with every channel and its consent flags (consent_marketing null = never asked).\n\nRequired API key scope: `read:contacts`.","tags":["People (Identity Hub)"],"parameters":[{"name":"partyId","in":"path","required":true,"description":"Person ID (UUID)","schema":{"type":"string"}}],"responses":{"200":{"description":"The person","content":{"application/json":{"example":{"success":true,"data":{"id":"a1b2c3d4-0000-4000-8000-00000000a001","kind":"person","display_name":"Dana Levi","given_name":"Dana","family_name":"Levi","org_name":null,"preferred_language":"he","preferred_channel":"whatsapp","timezone":"Asia/Jerusalem","tags":["vip"],"segments":[],"lifecycle_stage":"customer","status":"active","created_at":"2026-09-01T08:00:00.000Z","updated_at":"2026-10-11T09:00:00.000Z","archived":false,"channels":[{"id":"b2c3d4e5-0000-4000-8000-00000000b001","kind":"phone","value":"+972501234567","label":"mobile","is_primary":true,"is_verified":true,"consent_marketing":null,"consent_utility":true,"last_inbound_at":"2026-10-10T17:20:00.000Z"}]}}}}},"404":{"description":"NOT_FOUND - no such person in this business; PARTY_MERGED - the record was merged into another person, whose id is in merged_into_id"}},"x-required-scope":"read:contacts"}},"/parties/resolve":{"post":{"operationId":"parties_resolve","summary":"Find or Create Person","description":"The person who owns a channel (phone, email, WhatsApp id...) in this business, or a new person with that one channel when nobody has it. Safe to repeat: the same channel always resolves to the same person (two calls at once also end with one person). A new person starts as a lead, with the channel as primary and without consent for utility messages (an API call is not proof of consent; record it in the app); a create_party audit row is written and the party.created webhook fires. created says which happened. Use this before creating anything else for a caller.\n\nRequired API key scope: `write:contacts`.","tags":["People (Identity Hub)"],"requestBody":{"description":"The channel, and a name for a new person","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","description":"Channel kind, as in Find Person by Channel"},"value":{"type":"string","description":"The phone, email or id on that channel (up to 320 characters)"},"display_name":{"type":"string","description":"Name for a new person (up to 200 characters); an existing person keeps theirs"}},"required":["kind","value"]}}}},"responses":{"200":{"description":"The existing person (created: false)"},"201":{"description":"A new person was created","content":{"application/json":{"example":{"success":true,"data":{"id":"a1b2c3d4-0000-4000-8000-00000000a001","kind":"person","display_name":"Dana Levi","given_name":null,"family_name":null,"org_name":null,"preferred_language":null,"preferred_channel":"phone","timezone":null,"tags":[],"segments":[],"lifecycle_stage":"lead","status":"active","created_at":"2026-10-12T08:00:00.000Z","updated_at":"2026-10-12T08:00:00.000Z","archived":false,"channels":[{"id":"b2c3d4e5-0000-4000-8000-00000000b001","kind":"phone","value":"+972501234567","label":null,"is_primary":true,"is_verified":false,"consent_marketing":null,"consent_utility":false,"last_inbound_at":null}],"created":true}}}}},"400":{"description":"kind is not a channel kind; value missing, empty or too long; display_name not text or too long"},"403":{"description":"INSUFFICIENT_SCOPE - the key has no write:contacts"},"409":{"description":"PARTY_UNAVAILABLE - the person who owns this channel is being deleted or was erased; PARTY_MERGED - the owner was merged into another person, whose id is in merged_into_id"}},"x-required-scope":"write:contacts"}},"/crm/boards":{"get":{"operationId":"crm_boards_list","summary":"List Boards","description":"Get all CRM boards with item counts.\n\nRequired API key scope: `read:crm`.","tags":["CRM Boards"],"responses":{"200":{"description":"List of boards"}},"x-required-scope":"read:crm"}},"/crm/boards/{boardId}/columns":{"get":{"operationId":"crm_columns_list","summary":"List Board Columns","description":"Get a board's columns (fields) with their keys and types. Use this before creating items so column keys/types match — the item cell keys must equal these column keys.\n\nRequired API key scope: `read:crm`.","tags":["CRM Boards"],"parameters":[{"name":"boardId","in":"path","required":true,"description":"Board ID (UUID)","schema":{"type":"string"}}],"responses":{"200":{"description":"List of columns"},"404":{"description":"Board not found"}},"x-required-scope":"read:crm"},"post":{"operationId":"crm_columns_create","summary":"Create Board Columns","description":"Create one or more columns (fields) on a board. Accepts a single column object or {columns:[...]}. Idempotent on (board, key) — re-posting an existing key updates it instead of failing. Column types: text, long_text, phone, email, url, number, status, select, dropdown, checkbox, date, datetime, rating, progress.\n\nRequired API key scope: `write:crm`.","tags":["CRM Boards"],"parameters":[{"name":"boardId","in":"path","required":true,"description":"Board ID (UUID)","schema":{"type":"string"}}],"requestBody":{"description":"Column(s) to create","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Column display name (required if no key)"},"key":{"type":"string","description":"Stable column key (auto-slugged from name if omitted)"},"type":{"type":"string","description":"Column type (default text)"},"required":{"type":"boolean","description":"Whether the field is required"},"columns":{"type":"array","items":{},"description":"Array of column objects for batch creation"}}}}}},"responses":{"201":{"description":"Columns created/updated"},"400":{"description":"Invalid type or missing name/key"},"404":{"description":"Board not found"}},"x-required-scope":"write:crm"}},"/crm/boards/{boardId}/items":{"get":{"operationId":"crm_items_list","summary":"List Board Items","description":"Get all non-archived items in a CRM board, including their cell values, ordered by position.\n\nRequired API key scope: `read:crm`.","tags":["CRM Boards"],"parameters":[{"name":"boardId","in":"path","required":true,"description":"Board ID (UUID)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max results (default 20, max 100)","schema":{"type":"integer"}},{"name":"offset","in":"query","description":"Pagination offset","schema":{"type":"integer"}}],"responses":{"200":{"description":"List of items with cells"}},"x-required-scope":"read:crm"},"post":{"operationId":"crm_item_create","summary":"Create Item","description":"Create a new CRM item on a board, optionally setting column values. The card is saved first; its column values are then set exactly as Set Item Cells does (deal gate, timeline row, update_crm_cells audit, crm.cells.updated webhook, automations), and the crm.item.created webhook goes out after them. If the cells are refused (a status option that needs a paid deal) the card is still created: the 201 carries warnings.cells_not_saved with the code (LIFECYCLE_REQUIRES_DEAL, or CELLS_WRITE_FAILED - retry with Set Item Cells). Unknown keys and refused smart values are listed in warnings.\n\nRequired API key scope: `write:crm`.","tags":["CRM Boards"],"parameters":[{"name":"boardId","in":"path","required":true,"description":"Board ID (UUID)","schema":{"type":"string"}}],"requestBody":{"description":"Item details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Item name"},"group_key":{"type":"string","description":"Board group key"},"columnValues":{"type":"object","additionalProperties":true,"description":"Column values keyed by column key (JSON object)"}},"required":["name"]}}}},"responses":{"201":{"description":"Item created (check warnings.cells_not_saved when columnValues were sent)"},"404":{"description":"Board not found"}},"x-required-scope":"write:crm"}},"/crm/items/{itemId}":{"patch":{"operationId":"crm_item_update","summary":"Update Item","description":"Update a CRM item (name, group, archived flag) and optionally its column values. Fires the crm.item.updated webhook.\n\nRequired API key scope: `write:crm`.","tags":["CRM Boards"],"parameters":[{"name":"itemId","in":"path","required":true,"description":"Item ID (UUID)","schema":{"type":"string"}}],"requestBody":{"description":"Item fields to update","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Item name"},"group_key":{"type":"string","description":"Board group key"},"archived":{"type":"boolean","description":"Archive/unarchive the item"},"columnValues":{"type":"object","additionalProperties":true,"description":"Column values keyed by column key (JSON object)"}}}}}},"responses":{"200":{"description":"Item updated"},"404":{"description":"Item not found"},"409":{"description":"LIFECYCLE_REQUIRES_DEAL - a columnValues status option needs a paid deal first; nothing was changed"}},"x-required-scope":"write:crm"},"get":{"operationId":"crm_item_get","summary":"Get Item","description":"One CRM item (card) with its fields and its cell values, each cell tagged with its column key and type. Archived items are returned with archived: true. On a board set to show each user only their own cards, a key sees only cards with no owner, and none at all when the board also hides unowned cards - the same rule as List Board Items; a card a key cannot see answers 404.\n\nRequired API key scope: `read:crm`.","tags":["CRM Boards"],"parameters":[{"name":"itemId","in":"path","required":true,"description":"Item ID (UUID)","schema":{"type":"string"}}],"responses":{"200":{"description":"The item with its cells","content":{"application/json":{"example":{"success":true,"data":{"id":"6f1d2c3b-4a59-4687-9a10-b2c3d4e5f607","board_id":"1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d","task_key":"CRM-482","group_key":"new","name":"Dana Levi","owner_id":null,"customer_id":1842,"archived":false,"deal_value":1500,"currency":"ILS","stage":"lead","priority":"high","source":"website","created_at":"2026-10-01T08:00:00.000Z","updated_at":"2026-10-02T08:00:00.000Z","cells":[{"column_id":"9b8a7c6d-5e4f-4321-8a9b-0c1d2e3f4a5b","key":"phone","type":"phone","value_json":{"value":"+972501234567"},"updated_at":"2026-10-02T08:00:00.000Z"}]}}}}},"403":{"description":"BOARD_RESTRICTED_FOR_API_KEY - the board is limited to named users in the app"},"404":{"description":"Item not found"}},"x-required-scope":"read:crm"},"delete":{"operationId":"crm_item_archive","summary":"Archive Item","description":"Archive a CRM item (card), as Delete does in the app: the card is hidden from the board and lists but nothing is deleted - PATCH it with archived: false to bring it back. Writes an archive_crm_item audit row and fires the crm.item.deleted webhook and the item.deleted automations. Archiving an archived card answers 200 with already_archived: true and fires nothing again.\n\nRequired API key scope: `write:crm`.","tags":["CRM Boards"],"parameters":[{"name":"itemId","in":"path","required":true,"description":"Item ID (UUID)","schema":{"type":"string"}}],"responses":{"200":{"description":"Item archived","content":{"application/json":{"example":{"success":true,"data":{"id":"6f1d2c3b-4a59-4687-9a10-b2c3d4e5f607","archived":true,"already_archived":false}}}}},"403":{"description":"BOARD_RESTRICTED_FOR_API_KEY - the board is limited to named users in the app"},"404":{"description":"Item not found"}},"x-required-scope":"write:crm"}},"/crm/boards/{boardId}/groups":{"get":{"operationId":"crm_groups_list","summary":"List Board Groups","description":"The groups (pipeline stages) used on a board, with how many open (non-archived) cards a key can see in each, largest first, up to 100 (truncated: true when there are more). A group whose cards are all archived is listed with 0; cards with no group are counted under group_key null. Groups exist only through cards, so a stage no card was ever in is not listed. Use a group_key when creating or moving an item.\n\nRequired API key scope: `read:crm`.","tags":["CRM Boards"],"parameters":[{"name":"boardId","in":"path","required":true,"description":"Board ID (UUID)","schema":{"type":"string"}}],"responses":{"200":{"description":"Groups with card counts","content":{"application/json":{"example":{"success":true,"data":[{"group_key":"new","item_count":42},{"group_key":"qualified","item_count":17},{"group_key":"won","item_count":5}],"truncated":false}}}},"403":{"description":"BOARD_RESTRICTED_FOR_API_KEY - the board is limited to named users in the app"},"404":{"description":"Board not found"}},"x-required-scope":"read:crm"}},"/crm/items/{itemId}/cells":{"put":{"operationId":"crm_item_cells","summary":"Set Item Cells","description":"Set a CRM item's cell values by column key, the way editing a card in the app does: a status option that needs a paid deal is refused (409 LIFECYCLE_REQUIRES_DEAL, nothing saved), the card timeline gets a row, an update_crm_cells audit row is written, and the crm.cells.updated webhook and the column/status automations fire. Keys that are not columns on the card's board and smart-column values that point at a record this business does not own are reported in warnings and skipped; the other cells are saved together. PATCH Update Item columnValues follow the same rules (a refused cell write changes no card field either); Create Item sets its cells this way after the card is created.\n\nRequired API key scope: `write:crm`.","tags":["CRM Boards"],"parameters":[{"name":"itemId","in":"path","required":true,"description":"Item ID (UUID)","schema":{"type":"string"}}],"requestBody":{"description":"The cells to set","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"columnValues":{"type":"object","additionalProperties":true,"description":"Values keyed by column key, e.g. {\"status\": \"Qualified\", \"phone\": \"+972501234567\"} - see GET /api/v1/crm/boards/{boardId}/columns for the keys"}},"required":["columnValues"]}}}},"responses":{"200":{"description":"Cells saved; the card's cells after the write","content":{"application/json":{"example":{"success":true,"data":{"item_id":"6f1d2c3b-4a59-4687-9a10-b2c3d4e5f607","cells":[{"column_id":"9b8a7c6d-5e4f-4321-8a9b-0c1d2e3f4a5b","key":"status","type":"status","value_json":{"status":"Qualified"},"updated_at":"2026-10-11T08:00:00.000Z"}]},"warnings":{"unsaved_column_values":["budget"],"available_column_keys":["status","phone"],"hint":"These columnValues keys have no matching column on this board and were NOT saved."}}}}},"400":{"description":"columnValues missing, empty or not an object"},"403":{"description":"BOARD_RESTRICTED_FOR_API_KEY - the board is limited to named users in the app"},"404":{"description":"Item not found (or not visible to a key on this board)"},"409":{"description":"LIFECYCLE_REQUIRES_DEAL - a status option that needs a paid deal first; nothing was saved"}},"x-required-scope":"write:crm"}},"/crm/items/{itemId}/comments":{"get":{"operationId":"crm_item_comments_list","summary":"List Item Comments","description":"The comments on a CRM item (card), oldest first, paged with limit (default 20, max 100) and offset. A reply carries parent_comment_id. A card the key cannot see (on a board limited to named users, or an owned card on a board set to show each user only their own cards) answers 403/404 as Get Item does.\n\nRequired API key scope: `read:crm`.","tags":["CRM Boards"],"parameters":[{"name":"itemId","in":"path","required":true,"description":"Item ID (UUID)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size (1-100, default 20)","schema":{"type":"number"}},{"name":"offset","in":"query","description":"Rows to skip (default 0)","schema":{"type":"number"}}],"responses":{"200":{"description":"Comments","content":{"application/json":{"example":{"success":true,"data":[{"id":"c3d4e5f6-0718-4293-a4b5-c6d7e8f90a1b","item_id":"6f1d2c3b-4a59-4687-9a10-b2c3d4e5f607","parent_comment_id":null,"author_id":12,"author_name":"Dana Levi","body":"Called back, sending a quote tomorrow","mentions_json":[],"edited_at":null,"created_at":"2026-10-11T08:00:00.000Z","updated_at":"2026-10-11T08:00:00.000Z"}],"pagination":{"limit":20,"offset":0}}}}},"403":{"description":"BOARD_RESTRICTED_FOR_API_KEY - the board is limited to named users in the app"},"404":{"description":"Item not found"}},"x-required-scope":"read:crm"},"post":{"operationId":"crm_item_comments_create","summary":"Add Item Comment","description":"Add a comment, or a reply to one, on a CRM item (card), as commenting in the app does: the comment, a timeline row and the card's last activity are saved together, then a create_crm_comment audit row is written and the crm.comment.created webhook fires. The comment is signed by the user who created the API key, with the author name \"API: <key name>\". Mentions and attachments are app-only.\n\nRequired API key scope: `write:crm`.","tags":["CRM Boards"],"parameters":[{"name":"itemId","in":"path","required":true,"description":"Item ID (UUID)","schema":{"type":"string"}}],"requestBody":{"description":"The comment","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","description":"Comment text (up to 10,000 characters)"},"parent_comment_id":{"type":"string","description":"Reply to this comment (UUID of a comment on the same item)"}},"required":["body"]}}}},"responses":{"201":{"description":"Comment added","content":{"application/json":{"example":{"success":true,"data":{"id":"c3d4e5f6-0718-4293-a4b5-c6d7e8f90a1b","item_id":"6f1d2c3b-4a59-4687-9a10-b2c3d4e5f607","parent_comment_id":null,"author_id":12,"author_name":"API: website-sync","body":"Lead asked for a call on Sunday","mentions_json":[],"edited_at":null,"created_at":"2026-10-11T08:00:00.000Z","updated_at":"2026-10-11T08:00:00.000Z"}}}}},"400":{"description":"body missing, empty or too long; parent_comment_id not a UUID; KEY_HAS_NO_AUTHOR - the key has no creating user"},"403":{"description":"BOARD_RESTRICTED_FOR_API_KEY - the board is limited to named users in the app"},"404":{"description":"Item not found, or PARENT_COMMENT_NOT_FOUND"}},"x-required-scope":"write:crm"}},"/crm/follow-ups":{"get":{"operationId":"crm_follow_ups_list","summary":"List Follow-Ups","description":"The business's follow-up tasks, in the app's order (urgent first, then by due date), paged with limit (default 20, max 100) and offset; pagination.total is the number of matching tasks. status is the task's state now: upcoming, overdue, snoozed or completed. Tasks on boards limited to named users, and owned cards on boards set to show each user only their own cards, are left out, as in List Items.\n\nRequired API key scope: `read:crm`.","tags":["CRM Boards"],"parameters":[{"name":"status","in":"query","description":"pending (default: every open task, upcoming + overdue), upcoming, overdue, snoozed, completed or all","schema":{"type":"string"}},{"name":"item_id","in":"query","description":"Only this item's (card's) tasks (UUID)","schema":{"type":"string"}},{"name":"board_id","in":"query","description":"Only tasks on cards of this board (UUID)","schema":{"type":"string"}},{"name":"assigned_to","in":"query","description":"Only tasks assigned to this user id","schema":{"type":"number"}},{"name":"limit","in":"query","description":"Page size (1-100, default 20)","schema":{"type":"number"}},{"name":"offset","in":"query","description":"Rows to skip (default 0)","schema":{"type":"number"}}],"responses":{"200":{"description":"Follow-ups","content":{"application/json":{"example":{"success":true,"data":[{"id":"a1b2c3d4-e5f6-4789-9abc-def012345678","item_id":"6f1d2c3b-4a59-4687-9a10-b2c3d4e5f607","item_name":"Dana Levi","board_id":"0b1c2d3e-4f50-4617-8293-a4b5c6d7e8f9","assigned_to":12,"follow_up_type":"call","title":"Call back about the quote","description":null,"due_at":"2026-10-20T07:00:00.000Z","completed_at":null,"snoozed_until":null,"priority":"high","outcome":null,"created_by":12,"created_at":"2026-10-11T08:00:00.000Z","status":"upcoming"}],"pagination":{"limit":20,"offset":0,"total":1}}}}},"400":{"description":"A filter is not valid (status, item_id, board_id, assigned_to)"}},"x-required-scope":"read:crm"},"post":{"operationId":"crm_follow_ups_create","summary":"Create Follow-Up","description":"Add a follow-up task to a CRM item (card), as the app's Follow-Up Center does: the task and the card's next follow-up date are saved together, then a create_crm_follow_up audit row is written, the crm.follow_up.created webhook fires and the assignee (when it is not the key's creator) gets an in-app notification. The task is assigned to assigned_to, a user of this business, or to the user who created the API key.\n\nRequired API key scope: `write:crm`.","tags":["CRM Boards"],"requestBody":{"description":"The task","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"item_id":{"type":"string","description":"The CRM item (card) id (UUID)"},"title":{"type":"string","description":"What to do (up to 500 characters)"},"due_at":{"type":"string","description":"When: ISO 8601 date and time WITH its offset (Z or +03:00), e.g. 2026-10-20T10:00:00+03:00"},"description":{"type":"string","description":"Details (up to 10,000 characters)"},"assigned_to":{"type":"number","description":"User id of a user of this business (default: the key's creator)"},"priority":{"type":"string","description":"low, normal (default), high or urgent"},"follow_up_type":{"type":"string","description":"manual (default), call, whatsapp, email, meeting or task"}},"required":["item_id","title","due_at"]}}}},"responses":{"201":{"description":"Follow-up created","content":{"application/json":{"example":{"success":true,"data":{"id":"a1b2c3d4-e5f6-4789-9abc-def012345678","item_id":"6f1d2c3b-4a59-4687-9a10-b2c3d4e5f607","item_name":"Dana Levi","board_id":"0b1c2d3e-4f50-4617-8293-a4b5c6d7e8f9","assigned_to":12,"follow_up_type":"call","title":"Call back about the quote","description":null,"due_at":"2026-10-20T07:00:00.000Z","completed_at":null,"snoozed_until":null,"priority":"high","outcome":null,"created_by":12,"created_at":"2026-10-11T08:00:00.000Z","status":"upcoming"}}}}},"400":{"description":"A field is missing or not valid (due_at without an offset is refused); ASSIGNEE_NOT_IN_BUSINESS; KEY_CREATOR_NOT_IN_BUSINESS - no assigned_to and the key creator has left the business; KEY_HAS_NO_AUTHOR - no assigned_to and the key has no creating user"},"403":{"description":"BOARD_RESTRICTED_FOR_API_KEY - the board is limited to named users in the app"},"404":{"description":"Item not found"}},"x-required-scope":"write:crm"}},"/crm/follow-ups/{followUpId}/complete":{"post":{"operationId":"crm_follow_ups_complete","summary":"Complete Follow-Up","description":"Mark a follow-up task done, optionally with its outcome. A board set to require an outcome refuses an empty one (OUTCOME_REQUIRED), as the app does. The card's next follow-up date moves to its next open task; a complete_crm_follow_up audit row is written and the crm.follow_up.completed webhook fires.\n\nRequired API key scope: `write:crm`.","tags":["CRM Boards"],"parameters":[{"name":"followUpId","in":"path","required":true,"description":"Follow-up ID (UUID)","schema":{"type":"string"}}],"requestBody":{"description":"The outcome (optional unless the board requires it)","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"outcome":{"type":"string","description":"What happened (up to 2,000 characters)"}}}}}},"responses":{"200":{"description":"Follow-up completed","content":{"application/json":{"example":{"success":true,"data":{"id":"a1b2c3d4-e5f6-4789-9abc-def012345678","item_id":"6f1d2c3b-4a59-4687-9a10-b2c3d4e5f607","item_name":"Dana Levi","board_id":"0b1c2d3e-4f50-4617-8293-a4b5c6d7e8f9","assigned_to":12,"follow_up_type":"call","title":"Call back about the quote","description":null,"due_at":"2026-10-20T07:00:00.000Z","completed_at":"2026-10-20T07:30:00.000Z","snoozed_until":null,"priority":"high","outcome":"Booked a visit","created_by":12,"created_at":"2026-10-11T08:00:00.000Z","status":"completed"}}}}},"400":{"description":"OUTCOME_REQUIRED - the board requires an outcome; outcome too long"},"403":{"description":"BOARD_RESTRICTED_FOR_API_KEY - the board is limited to named users in the app"},"404":{"description":"Follow-up not found"},"409":{"description":"ALREADY_COMPLETED"}},"x-required-scope":"write:crm"}},"/crm/items/{itemId}/timeline":{"get":{"operationId":"crm_item_timeline_list","summary":"Get Item Timeline","description":"Everything that happened on a CRM item (card), newest first: notes, messages, calls, bookings, leads, comments and system changes. Each entry has a type and a payload whose keys depend on the type (for example a note carries text, an attachment fileName, mimeType, size and caption). Internal keys (storage keys, links, file paths) are left out at every depth; open an attachment in the app. Paged with limit (default 20, max 100) and offset; pagination.total is the number of entries. A card the key cannot see answers 403/404 as Get Item does.\n\nRequired API key scope: `read:crm`.","tags":["CRM Boards"],"parameters":[{"name":"itemId","in":"path","required":true,"description":"Item ID (UUID)","schema":{"type":"string"}},{"name":"type","in":"query","description":"Only entries of this type, e.g. note, message_received, message_sent, booking_created, system. A type no entry has returns an empty list; empty means no filter.","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size (1-100, default 20)","schema":{"type":"number"}},{"name":"offset","in":"query","description":"Rows to skip (default 0)","schema":{"type":"number"}}],"responses":{"200":{"description":"Timeline entries","content":{"application/json":{"example":{"success":true,"data":[{"id":"d4e5f607-1829-4a3b-8c4d-5e6f7a8b9c0d","item_id":"6f1d2c3b-4a59-4687-9a10-b2c3d4e5f607","type":"note","created_by":12,"created_at":"2026-10-11T09:00:00.000Z","payload":{"text":"Sent the price list","source":"public_api","created_by_name":"API: website-sync"}},{"id":"e5f60718-293a-4b4c-9d5e-6f7a8b9c0d1e","item_id":"6f1d2c3b-4a59-4687-9a10-b2c3d4e5f607","type":"booking_created","created_by":null,"created_at":"2026-10-10T12:00:00.000Z","payload":{"title":"Booking created","bookingId":812,"serviceName":"Consultation","dateTime":"2026-10-14T10:00:00+03:00"}}],"pagination":{"limit":20,"offset":0,"total":2}}}}},"400":{"description":"type is not a word (lowercase letters and _ only)"},"403":{"description":"BOARD_RESTRICTED_FOR_API_KEY - the board is limited to named users in the app"},"404":{"description":"Item not found"}},"x-required-scope":"read:crm"},"post":{"operationId":"crm_item_timeline_note","summary":"Add Item Note","description":"Add a note to a CRM item's (card's) timeline, as the app's \"add note\" does: the note and the card's last activity time are saved together, then a create_crm_timeline_entry audit row is written and the crm.timeline.created webhook fires. The note is signed \"API: <key name>\" and attributed to the user who created the key, who must still be a user of the business. Notes only: attachments and the other entry types stay in the app.\n\nRequired API key scope: `write:crm`.","tags":["CRM Boards"],"parameters":[{"name":"itemId","in":"path","required":true,"description":"Item ID (UUID)","schema":{"type":"string"}}],"requestBody":{"description":"The note","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"Note text (up to 10,000 characters)"},"type":{"type":"string","description":"Optional; only \"note\" is accepted"}},"required":["text"]}}}},"responses":{"201":{"description":"Note added","content":{"application/json":{"example":{"success":true,"data":{"id":"d4e5f607-1829-4a3b-8c4d-5e6f7a8b9c0d","item_id":"6f1d2c3b-4a59-4687-9a10-b2c3d4e5f607","type":"note","created_by":12,"created_at":"2026-10-11T09:00:00.000Z","payload":{"text":"Sent the price list","source":"public_api","created_by_name":"API: website-sync"}}}}}},"400":{"description":"text missing, empty or too long; type other than note; KEY_HAS_NO_AUTHOR - the key has no creating user; KEY_CREATOR_NOT_IN_BUSINESS - the key creator has left the business"},"403":{"description":"BOARD_RESTRICTED_FOR_API_KEY - the board is limited to named users in the app"},"404":{"description":"Item not found"}},"x-required-scope":"write:crm"}},"/leads":{"post":{"operationId":"crm_lead_capture","summary":"Capture Lead","description":"Capture an external lead (website form, landing page) into a CRM board. Auto-creates the customer and fires the lead.created webhook. When the business has CRM switched off or not in its plan, the lead is still saved but no CRM card is written: the response carries crmItemId null and crm_item_skipped (crm_turned_off | crm_not_in_plan); the lead.created webhook carries the same field.\n\nRequired API key scope: `write:contacts`.","tags":["CRM Boards"],"requestBody":{"description":"Lead details. At least one of name, phone or email is required (400 otherwise).","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Lead full name"},"phone":{"type":"string","description":"Phone number"},"email":{"type":"string","description":"Email address"},"message":{"type":"string","description":"Free-text message from the lead"},"source":{"type":"string","description":"Lead source label (e.g. website, facebook)"}}}}}},"responses":{"201":{"description":"Lead captured (into a CRM board unless crm_item_skipped is set)","content":{"application/json":{"example":{"success":true,"data":{"submissionId":9,"crmItemId":null,"createdAt":"2026-10-05T09:00:00.000Z","customer_id":77,"crm_item_skipped":"crm_not_in_plan"}}}}},"400":{"description":"None of name, phone or email was sent"},"401":{"description":"Unauthorized"}},"x-required-scope":"write:contacts"}},"/services":{"get":{"operationId":"services_list","summary":"List Services","description":"The business's active bookable services, by name - show them before POST /api/v1/bookings. Requires the booking module on the plan.\n\nRequired API key scope: `read:bookings`.","tags":["Services"],"responses":{"200":{"description":"Active services","content":{"application/json":{"example":{"success":true,"data":[{"id":501,"name":"Haircut","description":"Wash, cut and dry","category":"Hair","duration_minutes":45,"price":"120.00"}]}}}},"403":{"description":"Booking is not on the plan (MODULE_LOCKED)"}},"x-required-scope":"read:bookings"},"post":{"operationId":"services_create","summary":"Create Service","description":"Create a new bookable service. Created services are active immediately.\n\nRequired API key scope: `write:bookings`.","tags":["Services"],"requestBody":{"description":"Service details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Service name"},"description":{"type":"string","description":"Service description"},"category":{"type":"string","description":"Service category"},"duration_minutes":{"type":"integer","description":"Duration in minutes","example":60},"price":{"type":"number","description":"Price","example":150}},"required":["name"]}}}},"responses":{"201":{"description":"Service created"}},"x-required-scope":"write:bookings"}},"/services/{id}":{"get":{"operationId":"services_get","summary":"Get Service","description":"One service of your business, active or not - a deactivated service answers with active false, not 404. Use it to read back an id from GET /api/v1/services, a booking or a webhook. Requires the booking module on the plan.\n\nRequired API key scope: `read:bookings`.","tags":["Services"],"parameters":[{"name":"id","in":"path","required":true,"description":"Service id","schema":{"type":"integer","example":501}}],"responses":{"200":{"description":"The service","content":{"application/json":{"example":{"success":true,"data":{"id":501,"name":"Haircut","description":"Wash, cut and dry","category":"Hair","duration_minutes":45,"price":"120.00","currency":"ILS","active":true,"capacity":null,"is_online":false,"is_in_person":true,"requires_employee":true,"color":"#aa3366","online_meeting_provider":null,"custom_form_fields":null,"created_at":"2026-10-01T09:00:00.000Z","updated_at":"2026-10-02T09:00:00.000Z"}}}}},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Booking is not on the plan (MODULE_LOCKED), or the key has no read:bookings (\"Insufficient scope. Required: read:bookings\")"},"404":{"description":"No service with this id in your business (SERVICE_NOT_FOUND)"},"500":{"description":"Failed to read service (reported to us; retry)"}},"x-required-scope":"read:bookings"},"patch":{"operationId":"services_update","summary":"Update Service","description":"Change a service of your business - only the fields you send, with the same checks as the services screen; a field not listed below is refused (UNKNOWN_FIELD). Fires services.updated. Send active: true to bring a deactivated service back. Requires the booking module on the plan.\n\nRequired API key scope: `write:bookings`.","tags":["Services"],"parameters":[{"name":"id","in":"path","required":true,"description":"Service id","schema":{"type":"integer","example":501}}],"requestBody":{"description":"Any of these fields (at least one).","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Name, 1-255 characters, no markup","example":"Haircut"},"description":{"type":"string","description":"Description"},"category":{"type":"string","description":"Category, no markup","example":"Hair"},"duration_minutes":{"type":"integer","description":"Length in whole minutes","example":50},"price":{"type":"number","description":"Price, 0-99999999.99","example":130},"currency":{"type":"string","description":"Currency code, up to 10 characters","example":"ILS"},"active":{"type":"boolean","description":"false hides it from GET /api/v1/services and new bookings"},"capacity":{"type":"integer","description":"Seats for a group class, 1-500; null = one customer"},"is_online":{"type":"boolean","description":"Held online"},"is_in_person":{"type":"boolean","description":"Held in person"},"requires_employee":{"type":"boolean","description":"A staff member must be assigned"},"color":{"type":"string","description":"Calendar colour #rrggbb; empty string = none","example":"#aa3366"},"online_meeting_provider":{"type":"string","description":"zoom, google_meet or none"},"custom_form_fields":{"type":"array","items":{},"description":"Questions the customer answers when booking (array)"}}}}}},"responses":{"200":{"description":"The service after the change","content":{"application/json":{"example":{"success":true,"data":{"id":501,"name":"Haircut","description":"Wash, cut and dry","category":"Hair","duration_minutes":50,"price":"130.00","currency":"ILS","active":true,"capacity":null,"is_online":false,"is_in_person":true,"requires_employee":true,"color":"#aa3366","online_meeting_provider":null,"custom_form_fields":null,"created_at":"2026-10-01T09:00:00.000Z","updated_at":"2026-10-14T09:00:00.000Z"}}}}},"400":{"description":"A value the services screen would refuse, or nothing to change (INVALID_INPUT); a field not listed (UNKNOWN_FIELD)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Booking is not on the plan (MODULE_LOCKED), or the key has no write:bookings (\"Insufficient scope. Required: write:bookings\")"},"404":{"description":"No service with this id in your business (SERVICE_NOT_FOUND)"},"500":{"description":"Failed to update service (reported to us; retry)"},"503":{"description":"The plan could not be checked; nothing was changed (PLAN_CHECK_FAILED)"}},"x-required-scope":"write:bookings"}},"/services/{id}/deactivate":{"post":{"operationId":"services_deactivate","summary":"Deactivate Service","description":"Take a service off sale, as deleting it in the app does: it leaves GET /api/v1/services and cannot be booked; existing bookings stay. Fires services.updated with active false. Already inactive (or two calls at once): 200, changed once. Bring it back with PATCH active: true. Requires the booking module on the plan.\n\nRequired API key scope: `write:bookings`.","tags":["Services"],"parameters":[{"name":"id","in":"path","required":true,"description":"Service id","schema":{"type":"integer","example":501}}],"responses":{"200":{"description":"The service, active false"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Booking is not on the plan (MODULE_LOCKED), or the key has no write:bookings"},"404":{"description":"No service with this id in your business (SERVICE_NOT_FOUND)"},"500":{"description":"Failed to deactivate service (reported to us; retry)"},"503":{"description":"The plan could not be checked; nothing was changed (PLAN_CHECK_FAILED)"}},"x-required-scope":"write:bookings"}},"/marketing/insights/overview":{"get":{"operationId":"marketing_insights_overview","summary":"Marketing Insights Overview","description":"What needs your attention in marketing right now: customers due a review request, win-back candidates, anomalies, active experiments, recommended next actions, and a one-line headline. Requires Marketing on the plan.\n\nRequired API key scope: `read:analytics`.","tags":["Analytics"],"responses":{"200":{"description":"Overview","content":{"application/json":{"example":{"success":true,"data":{"reputation_due":18,"winback_candidates":0,"anomalies":0,"experiments_active":0,"next_actions":18,"headline":"Needs attention: 18 recommended customer actions, 18 customers to ask for a review."}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:analytics, or the plan has no Marketing"}},"x-required-scope":"read:analytics"}},"/analytics/summary":{"get":{"operationId":"analytics_dashboard","summary":"Analytics Summary","description":"Get a cross-module analytics summary: conversation, message, and contact counts, booking stats, and revenue for the selected period.\n\nRequired API key scope: `read:analytics`.","tags":["Analytics"],"parameters":[{"name":"period","in":"query","description":"Time period","schema":{"type":"string","enum":["7d","30d","90d"],"default":"30d"}}],"responses":{"200":{"description":"Analytics summary","content":{"application/json":{"example":{"success":true,"data":{"period":"30d","conversations":120,"messages":3400,"bookings":{"total":45,"confirmed":38,"cancelled":4},"revenue":15200,"contacts":210}}}}}},"x-required-scope":"read:analytics"}},"/analytics/reports":{"post":{"operationId":"analytics_reports","summary":"Generate Report","description":"Generate an aggregated report for your business over a date range, grouped by day, week, or month. Returns rows of { period, count, total? }.\n\nRequired API key scope: `read:analytics`.","tags":["Analytics"],"requestBody":{"description":"Report parameters","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","description":"Report type","enum":["bookings","revenue","messages","contacts"]},"date_from":{"type":"string","description":"Start date (inclusive)","example":"2026-01-01"},"date_to":{"type":"string","description":"End date (inclusive)","example":"2026-01-31"},"group_by":{"type":"string","description":"Aggregation period","enum":["day","week","month"]}},"required":["type","date_from","date_to"]}}}},"responses":{"200":{"description":"Report rows: [{ period, count, total? }]"},"400":{"description":"Invalid type, dates, or group_by"}},"x-required-scope":"read:analytics"}},"/flows":{"get":{"operationId":"flows_list","summary":"List Flows","description":"Get automation flows for the services linked to your business, with slug, trigger type, and status.\n\nRequired API key scope: `read:flows`.","tags":["Automation Flows"],"responses":{"200":{"description":"List of flows"},"404":{"description":"No service linked to this business"}},"x-required-scope":"read:flows"},"post":{"operationId":"flows_create","summary":"Create Flow","description":"Create a new automation flow (created in draft status) on the service linked to your business. Webhook-triggered flows also get a flow webhook registration.\n\nRequired API key scope: `write:flows`.","tags":["Automation Flows"],"requestBody":{"description":"Flow definition","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Flow name"},"slug":{"type":"string","description":"Flow slug (generated from name if omitted)"},"description":{"type":"string","description":"Flow description"},"trigger_type":{"type":"string","description":"Trigger type","enum":["manual","webhook"]},"trigger_config":{"type":"object","additionalProperties":true,"description":"Trigger configuration"},"nodes":{"type":"array","items":{},"description":"Flow nodes"},"edges":{"type":"array","items":{},"description":"Flow connections"}},"required":["name"]}}}},"responses":{"201":{"description":"Flow created (draft)"},"404":{"description":"No service linked to this business"}},"x-required-scope":"write:flows"}},"/flows/{id}/execute":{"post":{"operationId":"flows_execute","summary":"Execute Flow","description":"Manually trigger a flow execution. The flow must belong to a service linked to your business and be active. Returns the flow run ID; execution continues asynchronously.\n\nRequired API key scope: `trigger:flows`.","tags":["Automation Flows"],"parameters":[{"name":"id","in":"path","required":true,"description":"Flow ID","schema":{"type":"string"}}],"requestBody":{"description":"Execution input","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"trigger_data":{"type":"object","additionalProperties":true,"description":"Trigger payload passed to the flow"},"variables":{"type":"object","additionalProperties":true,"description":"Input variables for the run"}}}}}},"responses":{"202":{"description":"Execution started (returns run ID)"},"404":{"description":"Flow not found or not active"}},"x-required-scope":"trigger:flows"}},"/invoices":{"get":{"operationId":"invoices_list","summary":"List Invoices","description":"Get all invoices with filtering.\n\nRequired API key scope: `read:payments`.","tags":["Invoicing"],"parameters":[{"name":"status","in":"query","description":"Filter by status","schema":{"type":"string","enum":["draft","sent","paid","overdue","cancelled"]}},{"name":"customerId","in":"query","description":"Filter by customer","schema":{"type":"string"}}],"responses":{"200":{"description":"List of invoices"}},"x-required-scope":"read:payments"},"post":{"operationId":"invoices_create","summary":"Create Invoice","description":"Create a draft invoice with an official document number assigned by the document numbering service. Amount is the total including VAT; tax_amount is the VAT portion. Set finalize=true to finalize immediately. Fires the invoice.created webhook.\n\nRequired API key scope: `manage:invoices`.","tags":["Invoicing"],"requestBody":{"description":"Invoice details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"number","description":"Total amount including VAT"},"currency":{"type":"string","description":"Currency code","example":"ILS"},"tax_amount":{"type":"number","description":"VAT portion of the amount"},"tax_rate":{"type":"number","description":"VAT rate"},"line_items":{"type":"array","items":{},"description":"Invoice line items"},"customer_name":{"type":"string","description":"Customer name"},"customer_email":{"type":"string","description":"Customer email"},"customer_phone":{"type":"string","description":"Customer phone"},"finalize":{"type":"boolean","description":"Finalize the invoice immediately after creation"}},"required":["amount"]}}}},"responses":{"201":{"description":"Draft invoice created with document number"},"400":{"description":"Invalid invoice data"}},"x-required-scope":"manage:invoices"}},"/invoices/{id}/send":{"post":{"operationId":"invoices_send","summary":"Send Invoice","description":"Email the invoice to the customer with the generated PDF attached. Draft invoices are automatically flipped to sent. Fires the invoice.sent webhook.\n\nRequired API key scope: `manage:invoices`.","tags":["Invoicing"],"parameters":[{"name":"id","in":"path","required":true,"description":"Invoice ID","schema":{"type":"string"}}],"requestBody":{"description":"Send options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","description":"Override recipient email (defaults to the invoice customer email)"},"language":{"type":"string","description":"PDF/email language (en/he)"}}}}}},"responses":{"200":{"description":"Invoice emailed with PDF"},"404":{"description":"Invoice not found"}},"x-required-scope":"manage:invoices"}},"/payments/transactions":{"get":{"operationId":"payments_history","summary":"Payment History","description":"Charges of your business, newest first: amount, currency, status, the customer id and the payment request it paid. Filter by customer, payment request, status and creation day (Israel time); pagination.total counts every match. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `read:payments`.","tags":["Payments"],"parameters":[{"name":"status","in":"query","description":"Only charges in this status (authorized = an escrow hold on the card, voided = a released hold)","schema":{"type":"string","enum":["succeeded","pending","processing","failed","refunded","authorized","voided"]}},{"name":"customer_id","in":"query","description":"Only charges of this customer (uuid)","schema":{"type":"string","example":"0d9e8f7a-6b5c-4d3e-8f21-a1b2c3d4e5f6"}},{"name":"payment_request_id","in":"query","description":"Only charges made through this payment request (uuid)","schema":{"type":"string","example":"9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"}},{"name":"date_from","in":"query","description":"Created on or after this day, YYYY-MM-DD, Israel time","schema":{"type":"string","example":"2026-10-01"}},{"name":"date_to","in":"query","description":"Created on or before this day, YYYY-MM-DD, Israel time","schema":{"type":"string","example":"2026-10-31"}},{"name":"limit","in":"query","description":"Rows per page, 1-100 (default 20)","schema":{"type":"integer","example":20}},{"name":"offset","in":"query","description":"Rows to skip (default 0)","schema":{"type":"integer","example":0}}],"responses":{"200":{"description":"One page of charges and the total","content":{"application/json":{"example":{"success":true,"data":[{"id":"6f1c2a9e-0b7d-4c1e-9a55-2f3e4d5c6b7a","amount":236,"currency":"ILS","status":"succeeded","customer_id":"0d9e8f7a-6b5c-4d3e-8f21-a1b2c3d4e5f6","payment_request_id":"9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d","created_at":"2026-10-16T09:12:00.000Z"}],"pagination":{"limit":20,"offset":0,"total":1}}}}},"400":{"description":"Unknown status (INVALID_STATUS); limit outside 1-100 or a negative offset (INVALID_PAGE); customer_id or payment_request_id not a uuid (INVALID_CUSTOMER_ID, INVALID_PAYMENT_REQUEST_ID); a day that is not a real YYYY-MM-DD date (INVALID_DATE); date_from after date_to (INVALID_DATE_RANGE)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks the scope"},"500":{"description":"Failed to list transactions (reported to us; retry)"}},"x-required-scope":"read:payments"}},"/payments/transactions/{id}":{"get":{"operationId":"payments_transaction_get","summary":"Get Transaction","description":"One charge of your business: amount, status, capture status (an escrow hold), method, provider, customer, payment request, invoice, installments, the card's last four digits and brand, the confirmation code, and why it failed. Never the provider's raw answer, the payer id or device data. Another business's id answers 404. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `read:payments`.","tags":["Payments"],"parameters":[{"name":"id","in":"path","required":true,"description":"Transaction uuid","schema":{"type":"string"}}],"responses":{"200":{"description":"The charge","content":{"application/json":{"example":{"success":true,"data":{"id":"6f1c2a9e-0b7d-4c1e-9a55-2f3e4d5c6b7a","amount":236,"currency":"ILS","status":"succeeded","capture_status":null,"payment_method_type":"CARD","provider":"tranzila","customer_id":"0d9e8f7a-6b5c-4d3e-8f21-a1b2c3d4e5f6","payment_request_id":"9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d","invoice_id":null,"installments":1,"card_last4":"4242","card_brand":"visa","confirmation_code":"0123456","failure_code":null,"failure_message":null,"processed_at":"2026-10-16T09:12:05.000Z","failed_at":null,"created_at":"2026-10-16T09:12:00.000Z"}}}}},"400":{"description":"The id is not a uuid (INVALID_TRANSACTION_ID)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks read:payments"},"404":{"description":"No such charge in this business (TRANSACTION_NOT_FOUND)"},"500":{"description":"Failed to get transaction (reported to us; retry)"}},"x-required-scope":"read:payments"}},"/payments/transactions/{id}/timeline":{"get":{"operationId":"payments_transaction_timeline","summary":"Transaction Timeline","description":"What happened to one charge of your business, oldest first: created, each status change (status_changed carries prev_status / new_status), a refund. Each event names who acted (actor_type, for example db, admin, customer, provider) and, when recorded, the amount, currency, provider, capture status, whether a refund was partial and its reason. Charges from before 2026-07-28 have no events (an empty list). No staff names and no provider traffic. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `read:payments`.","tags":["Payments"],"parameters":[{"name":"id","in":"path","required":true,"description":"Transaction uuid","schema":{"type":"string"}}],"responses":{"200":{"description":"The charge's current status and its events","content":{"application/json":{"example":{"success":true,"data":{"transaction_id":"6f1c2a9e-0b7d-4c1e-9a55-2f3e4d5c6b7a","status":"refunded","events":[{"id":10,"event_type":"created","prev_status":null,"new_status":"pending","actor_type":"db","details":{"provider":"tranzila"},"created_at":"2026-10-16T09:12:00.000Z"},{"id":11,"event_type":"status_changed","prev_status":"pending","new_status":"succeeded","actor_type":"db","details":{},"created_at":"2026-10-16T09:12:05.000Z"},{"id":12,"event_type":"status_changed","prev_status":"succeeded","new_status":"refunded","actor_type":"db","details":{},"created_at":"2026-10-17T08:00:00.000Z"},{"id":13,"event_type":"refund_processed","prev_status":null,"new_status":null,"actor_type":"admin","details":{"amount":236,"currency":"ILS","partial":false,"reason":"Duplicate charge"},"created_at":"2026-10-17T08:00:00.000Z"}]}}}}},"400":{"description":"The id is not a uuid (INVALID_TRANSACTION_ID)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks read:payments"},"404":{"description":"No such charge in this business (TRANSACTION_NOT_FOUND)"},"500":{"description":"Failed to get transaction timeline (reported to us; retry)"}},"x-required-scope":"read:payments"}},"/payments/refund-requests":{"post":{"operationId":"payments_refund","summary":"Create Refund Request","description":"Ask to refund a charge of your business. The request waits in pending until an admin approves it in the app - no money moves on this call. Only a completed charge inside the refund window (180 days) with money left on it; one open request per charge. Without amount it asks for everything not yet refunded. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `write:payments`.","tags":["Payments"],"requestBody":{"description":"Refund request details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"transaction_id":{"type":"string","description":"Charge to refund (UUID, from GET /api/v1/payments/transactions)","example":"6f1c2a9e-0b7d-4c1e-9a55-2f3e4d5c6b7a"},"amount":{"type":"number","description":"Amount to refund, up to what is left on the charge (default: all of it)","example":100},"reason":{"type":"string","description":"Why (up to 1000 characters; the admin sees it)","example":"Order cancelled"}},"required":["transaction_id"]}}}},"responses":{"201":{"description":"Request created, waiting for admin approval","content":{"application/json":{"example":{"success":true,"data":{"id":"41","amount":100,"status":"pending","created_at":"2026-10-16T10:00:00.000Z","note":"Refund requests require admin approval before processing"}}}}},"400":{"description":"transaction_id is not a UUID (INVALID_TRANSACTION_ID); amount is not a number, is 0 or more than is left (BAD_AMOUNT)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks the scope"},"404":{"description":"No such charge in your business (TRANSACTION_NOT_FOUND)"},"409":{"description":"The charge cannot be refunded - failed, pending or fully refunded (NOT_REFUNDABLE); past the refund window (WINDOW_PASSED); a request is already open (ALREADY_PENDING)"},"500":{"description":"Failed to create refund request (reported to us; retry)"},"503":{"description":"The plan could not be checked; nothing was written (PLAN_CHECK_FAILED)"}},"x-required-scope":"write:payments"},"get":{"operationId":"payments_refund_requests_list","summary":"List Refund Requests","description":"Refund requests of your business, newest first, every status unless you filter: the charge, customer, amount, reason, status (pending waits for an admin and has no refund_id or resolved_at yet; approved carries the refund_id), where it came from (requested_via: public_api, receipt_link, wa_bot) and when it was resolved. Filter by charge, customer, status and creation day (Israel time); pagination.total counts every match. Never the customer's email or phone. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `read:payments`.","tags":["Payments"],"parameters":[{"name":"status","in":"query","description":"Only requests in this status (default: all)","schema":{"type":"string","enum":["pending","approved","rejected"]}},{"name":"transaction_id","in":"query","description":"Only requests for this charge (uuid)","schema":{"type":"string","example":"6f1c2a9e-0b7d-4c1e-9a55-2f3e4d5c6b7a"}},{"name":"customer_id","in":"query","description":"Only requests of this customer (uuid)","schema":{"type":"string","example":"0d9e8f7a-6b5c-4d3e-8f21-a1b2c3d4e5f6"}},{"name":"date_from","in":"query","description":"Created on or after this day, YYYY-MM-DD, Israel time","schema":{"type":"string","example":"2026-10-01"}},{"name":"date_to","in":"query","description":"Created on or before this day, YYYY-MM-DD, Israel time","schema":{"type":"string","example":"2026-10-31"}},{"name":"limit","in":"query","description":"Rows per page, 1-100 (default 20)","schema":{"type":"integer","example":20}},{"name":"offset","in":"query","description":"Rows to skip (default 0)","schema":{"type":"integer","example":0}}],"responses":{"200":{"description":"One page of refund requests and the total","content":{"application/json":{"example":{"success":true,"data":[{"id":"41","transaction_id":"6f1c2a9e-0b7d-4c1e-9a55-2f3e4d5c6b7a","customer_id":"0d9e8f7a-6b5c-4d3e-8f21-a1b2c3d4e5f6","amount":100,"currency":"ILS","reason":"Order cancelled","status":"approved","requested_via":"public_api","refund_id":"2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e","resolved_at":"2026-10-17T08:00:00.000Z","created_at":"2026-10-16T10:00:00.000Z"}],"pagination":{"limit":20,"offset":0,"total":1}}}}},"400":{"description":"Unknown status (INVALID_STATUS); limit outside 1-100 or a negative offset (INVALID_PAGE); transaction_id or customer_id not a uuid (INVALID_TRANSACTION_ID, INVALID_CUSTOMER_ID); a day that is not a real YYYY-MM-DD date (INVALID_DATE); date_from after date_to (INVALID_DATE_RANGE)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks read:payments"},"500":{"description":"Failed to list refund requests (reported to us; retry)"}},"x-required-scope":"read:payments"}},"/payments/refunds":{"get":{"operationId":"payments_refunds_list","summary":"List Refunds","description":"Refunds of your business, newest first: the charge, amount, currency, status, reason, why it failed (outcome_unknown true = it failed without a clear answer from the provider, so the money may have moved: check with the provider before you refund again), and what became of its credit note (credit_note_state: issued, no_document_to_credit, failed, needs_review, not_applicable, refund_failed or none; credit_note_invoice_id only when the note is in your business). Filter by charge, status and creation day (Israel time); pagination.total counts every match. Never the provider's raw answer or refund id. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `read:payments`.","tags":["Payments"],"parameters":[{"name":"status","in":"query","description":"Only refunds in this status","schema":{"type":"string","enum":["pending","processing","succeeded","failed"]}},{"name":"transaction_id","in":"query","description":"Only refunds of this charge (uuid)","schema":{"type":"string","example":"6f1c2a9e-0b7d-4c1e-9a55-2f3e4d5c6b7a"}},{"name":"date_from","in":"query","description":"Created on or after this day, YYYY-MM-DD, Israel time","schema":{"type":"string","example":"2026-10-01"}},{"name":"date_to","in":"query","description":"Created on or before this day, YYYY-MM-DD, Israel time","schema":{"type":"string","example":"2026-10-31"}},{"name":"limit","in":"query","description":"Rows per page, 1-100 (default 20)","schema":{"type":"integer","example":20}},{"name":"offset","in":"query","description":"Rows to skip (default 0)","schema":{"type":"integer","example":0}}],"responses":{"200":{"description":"One page of refunds and the total","content":{"application/json":{"example":{"success":true,"data":[{"id":"2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e","transaction_id":"6f1c2a9e-0b7d-4c1e-9a55-2f3e4d5c6b7a","amount":100,"currency":"ILS","status":"succeeded","outcome_unknown":false,"reason":"Order cancelled","failure_code":null,"failure_message":null,"credit_note_invoice_id":"7e8f9a0b-1c2d-4e3f-8a4b-5c6d7e8f9a0b","credit_note_state":"issued","processed_at":"2026-10-17T08:00:05.000Z","failed_at":null,"created_at":"2026-10-17T08:00:00.000Z"}],"pagination":{"limit":20,"offset":0,"total":1}}}}},"400":{"description":"Unknown status (INVALID_STATUS); limit outside 1-100 or a negative offset (INVALID_PAGE); transaction_id not a uuid (INVALID_TRANSACTION_ID); a day that is not a real YYYY-MM-DD date (INVALID_DATE); date_from after date_to (INVALID_DATE_RANGE)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks read:payments"},"500":{"description":"Failed to list refunds (reported to us; retry)"}},"x-required-scope":"read:payments"}},"/payments/refund-requests/{id}":{"get":{"operationId":"payments_refund_request_get","summary":"Get Refund Request","description":"One refund request of your business (the id the create answered): the list fields and the charge's full amount. Another business's id answers 404. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `read:payments`.","tags":["Payments"],"parameters":[{"name":"id","in":"path","required":true,"description":"Refund request id (a whole number, as text)","schema":{"type":"string"}}],"responses":{"200":{"description":"The refund request","content":{"application/json":{"example":{"success":true,"data":{"id":"41","transaction_id":"6f1c2a9e-0b7d-4c1e-9a55-2f3e4d5c6b7a","customer_id":"0d9e8f7a-6b5c-4d3e-8f21-a1b2c3d4e5f6","amount":100,"currency":"ILS","reason":"Order cancelled","status":"approved","requested_via":"public_api","refund_id":"2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e","resolved_at":"2026-10-17T08:00:00.000Z","created_at":"2026-10-16T10:00:00.000Z","transaction_amount":236}}}}},"400":{"description":"The id is not a positive whole number (INVALID_REFUND_REQUEST_ID)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks read:payments"},"404":{"description":"No such refund request in this business (REFUND_REQUEST_NOT_FOUND)"},"500":{"description":"Failed to get refund request (reported to us; retry)"}},"x-required-scope":"read:payments"}},"/payments/customers":{"get":{"operationId":"payments_customers_list","summary":"List Billing Customers","description":"Billing customers of your business (the customer_id a payment request, charge or refund request carries), newest first: name, contact, address, tax id, client type, payment terms, currency, credit limit, price list, revenue and debt so far, and customer_profile_id, the customer card (Customer 360) behind it. Filter by text, card, client type and debt; pagination.total counts every match. Never bank details, staff notes or staff ids. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `read:payments`.","tags":["Payments"],"parameters":[{"name":"search","in":"query","description":"Text in the name, email, phone or tax id, 1-100 characters (% and _ match themselves)","schema":{"type":"string","example":"dana"}},{"name":"customer_profile_id","in":"query","description":"Only the billing customer of this customer card (positive integer)","schema":{"type":"integer","example":512}},{"name":"client_type","in":"query","description":"Only this client type","schema":{"type":"string","enum":["individual","business"]}},{"name":"has_debt","in":"query","description":"true = only customers who owe; false = only customers who owe nothing or are in credit","schema":{"type":"string","enum":["true","false"]}},{"name":"limit","in":"query","description":"Rows per page, 1-100 (default 20)","schema":{"type":"integer","example":20}},{"name":"offset","in":"query","description":"Rows to skip (default 0)","schema":{"type":"integer","example":0}}],"responses":{"200":{"description":"One page of billing customers and the total","content":{"application/json":{"example":{"success":true,"data":[{"id":"0d9e8f7a-6b5c-4d3e-8f21-a1b2c3d4e5f6","customer_profile_id":512,"name":"Dana Levi","email":"dana@example.com","phone":"972501234567","address_line1":"Herzl 1","address_line2":null,"city":"Tel Aviv","state":null,"postal_code":"6100000","country":"IL","client_type":"business","tax_id":"515555555","tax_exempt":false,"payment_terms":"shotef","payment_terms_days":30,"default_currency":"ILS","contact_person":"Dana","tags":["vip"],"total_revenue":1200.5,"total_debt":0,"credit_limit":5000,"price_list_id":null,"last_activity_at":"2026-10-17T08:00:00.000Z","created_at":"2026-10-01T08:00:00.000Z","updated_at":"2026-10-17T08:00:00.000Z"}],"pagination":{"limit":20,"offset":0,"total":1}}}}},"400":{"description":"search longer than 100 characters (INVALID_SEARCH); customer_profile_id not a positive whole number (INVALID_CUSTOMER_PROFILE_ID); unknown client_type (INVALID_CLIENT_TYPE); has_debt not true / false (INVALID_HAS_DEBT); limit outside 1-100 or a negative offset (INVALID_PAGE)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks read:payments"},"500":{"description":"Failed to list customers (reported to us; retry)"}},"x-required-scope":"read:payments"},"post":{"operationId":"payments_customer_create","summary":"Create Billing Customer","description":"Find or create a billing customer of your business: a live customer with the same email (else, with no email, the same phone in any written form) is answered unchanged with created false - change it with PATCH. Two calls at once with the same email (or, with no email, the same phone) get one customer. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `write:payments`.","tags":["Payments"],"requestBody":{"description":"The customer; email or phone is required","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Customer name (text up to 255 characters, or null)","example":"Dana Levi"},"email":{"type":"string","description":"Email, stored lower-case; finds an existing customer (text up to 255 characters, or null)","example":"dana@example.com"},"phone":{"type":"string","description":"Phone, up to 50 characters with at least one digit, stored as 972...; with no email, finds an existing customer in any written form (050..., +972...)","example":"0501234567"},"address_line1":{"type":"string","description":"Address (text up to 255 characters, or null)"},"address_line2":{"type":"string","description":"Address, second line (text up to 255 characters, or null)"},"city":{"type":"string","description":"City (text up to 100 characters, or null)"},"state":{"type":"string","description":"State / region (text up to 100 characters, or null)"},"postal_code":{"type":"string","description":"Postal code (text up to 20 characters, or null)"},"country":{"type":"string","description":"Country code (text up to 2 characters, or null)","example":"IL"},"tax_id":{"type":"string","description":"Tax id (ח.פ / ע.מ) (text up to 20 characters, or null)"},"contact_person":{"type":"string","description":"Contact person (text up to 255 characters, or null)"},"client_type":{"type":"string","description":"individual or business","enum":["individual","business"]},"tax_exempt":{"type":"boolean","description":"true or false"},"payment_terms":{"type":"string","description":"Payment terms","enum":["immediate","shotef","shotef_plus_30","shotef_plus_60","shotef_plus_90","net_15","net_30","net_60","net_90","eom"]},"payment_terms_days":{"type":"integer","description":"Whole number 0-365, or null"},"default_currency":{"type":"string","description":"Three capital letters","example":"ILS"},"tags":{"type":"array","items":{},"description":"Up to 50 texts of 1-50 characters"},"customer_profile_id":{"type":"integer","description":"The customer card of your business to link (positive integer); not sent = the card with this email / phone, else a new card","example":512}}}}}},"responses":{"200":{"description":"A customer with this email / phone already exists; answered unchanged (nothing you sent is written), created false"},"201":{"description":"Created; the customer fields (as GET /payments/customers/{id}) and created true","content":{"application/json":{"example":{"success":true,"data":{"id":"0d9e8f7a-6b5c-4d3e-8f21-a1b2c3d4e5f6","customer_profile_id":512,"name":"Dana Levi","email":"dana@example.com","phone":"972501234567","address_line1":"Herzl 1","address_line2":null,"city":"Tel Aviv","state":null,"postal_code":"6100000","country":"IL","client_type":"business","tax_id":"515555555","tax_exempt":false,"payment_terms":"shotef","payment_terms_days":30,"default_currency":"ILS","contact_person":"Dana","tags":["vip"],"total_revenue":1200.5,"total_debt":0,"credit_limit":5000,"price_list_id":null,"last_activity_at":"2026-10-17T08:00:00.000Z","created_at":"2026-10-01T08:00:00.000Z","updated_at":"2026-10-17T08:00:00.000Z","created":true}}}}},"400":{"description":"No email and no phone (CUSTOMER_CONTACT_REQUIRED); a key outside the fields above, e.g. bank details, notes, metadata (UNKNOWN_FIELD, field names it); a value of the wrong kind or longer than its field (INVALID_FIELD, field names it), e.g. a phone with no digit or customer_profile_id not a positive whole number; a malformed email (INVALID_EMAIL); the body is not an object (INVALID_BODY)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks write:payments"},"404":{"description":"customer_profile_id is not a live customer card of your business (CUSTOMER_PROFILE_NOT_FOUND)"},"409":{"description":"Another billing customer of this business already has this email (CUSTOMER_EMAIL_IN_USE)"},"500":{"description":"Failed to create customer (reported to us; retry)"},"503":{"description":"The plan could not be checked; nothing was written - retry (PLAN_CHECK_FAILED)"}},"x-required-scope":"write:payments"}},"/payments/customers/{id}":{"get":{"operationId":"payments_customer_get","summary":"Get Billing Customer","description":"One billing customer of your business: the list fields. A deleted customer or another business's id answers 404. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `read:payments`.","tags":["Payments"],"parameters":[{"name":"id","in":"path","required":true,"description":"Billing customer id (uuid)","schema":{"type":"string"}}],"responses":{"200":{"description":"The billing customer","content":{"application/json":{"example":{"success":true,"data":{"id":"0d9e8f7a-6b5c-4d3e-8f21-a1b2c3d4e5f6","customer_profile_id":512,"name":"Dana Levi","email":"dana@example.com","phone":"972501234567","address_line1":"Herzl 1","address_line2":null,"city":"Tel Aviv","state":null,"postal_code":"6100000","country":"IL","client_type":"business","tax_id":"515555555","tax_exempt":false,"payment_terms":"shotef","payment_terms_days":30,"default_currency":"ILS","contact_person":"Dana","tags":["vip"],"total_revenue":1200.5,"total_debt":0,"credit_limit":5000,"price_list_id":null,"last_activity_at":"2026-10-17T08:00:00.000Z","created_at":"2026-10-01T08:00:00.000Z","updated_at":"2026-10-17T08:00:00.000Z"}}}}},"400":{"description":"The id is not a uuid (INVALID_CUSTOMER_ID)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks read:payments"},"404":{"description":"No such billing customer in this business (CUSTOMER_NOT_FOUND)"},"500":{"description":"Failed to get customer (reported to us; retry)"}},"x-required-scope":"read:payments"},"patch":{"operationId":"payments_customer_update","summary":"Update Billing Customer","description":"Change the fields you send on a billing customer of your business; the rest stay. A customer always keeps an email or a phone and a customer card. Answers the customer. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `write:payments`.","tags":["Payments"],"parameters":[{"name":"id","in":"path","required":true,"description":"Billing customer id (uuid)","schema":{"type":"string"}}],"requestBody":{"description":"Any of the create fields","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Customer name (text up to 255 characters, or null)","example":"Dana Levi"},"email":{"type":"string","description":"Email, stored lower-case; null clears it while the customer has a phone (text up to 255 characters, or null)","example":"dana@example.com"},"phone":{"type":"string","description":"Phone, up to 50 characters with at least one digit, stored as 972...; null clears it while the customer has an email","example":"0501234567"},"address_line1":{"type":"string","description":"Address (text up to 255 characters, or null)"},"address_line2":{"type":"string","description":"Address, second line (text up to 255 characters, or null)"},"city":{"type":"string","description":"City (text up to 100 characters, or null)"},"state":{"type":"string","description":"State / region (text up to 100 characters, or null)"},"postal_code":{"type":"string","description":"Postal code (text up to 20 characters, or null)"},"country":{"type":"string","description":"Country code (text up to 2 characters, or null)","example":"IL"},"tax_id":{"type":"string","description":"Tax id (ח.פ / ע.מ) (text up to 20 characters, or null)"},"contact_person":{"type":"string","description":"Contact person (text up to 255 characters, or null)"},"client_type":{"type":"string","description":"individual or business","enum":["individual","business"]},"tax_exempt":{"type":"boolean","description":"true or false"},"payment_terms":{"type":"string","description":"Payment terms","enum":["immediate","shotef","shotef_plus_30","shotef_plus_60","shotef_plus_90","net_15","net_30","net_60","net_90","eom"]},"payment_terms_days":{"type":"integer","description":"Whole number 0-365, or null"},"default_currency":{"type":"string","description":"Three capital letters","example":"ILS"},"tags":{"type":"array","items":{},"description":"Up to 50 texts of 1-50 characters"},"customer_profile_id":{"type":"integer","description":"Move the customer to another customer card of your business (positive integer; never null - a billing customer stays linked to a card)","example":512}}}}}},"responses":{"200":{"description":"Changed; the customer fields","content":{"application/json":{"example":{"success":true,"data":{"id":"0d9e8f7a-6b5c-4d3e-8f21-a1b2c3d4e5f6","customer_profile_id":512,"name":"Dana Levi","email":"dana@example.com","phone":"972501234567","address_line1":"Herzl 1","address_line2":null,"city":"Tel Aviv","state":null,"postal_code":"6100000","country":"IL","client_type":"business","tax_id":"515555555","tax_exempt":false,"payment_terms":"shotef","payment_terms_days":30,"default_currency":"ILS","contact_person":"Dana","tags":["vip"],"total_revenue":1200.5,"total_debt":0,"credit_limit":5000,"price_list_id":null,"last_activity_at":"2026-10-17T08:00:00.000Z","created_at":"2026-10-01T08:00:00.000Z","updated_at":"2026-10-17T08:00:00.000Z"}}}}},"400":{"description":"The id is not a uuid (INVALID_CUSTOMER_ID); no field sent (NO_FIELDS); a key outside the fields above, e.g. bank details, notes, metadata (UNKNOWN_FIELD, field names it); a value of the wrong kind or longer than its field, or customer_profile_id null (INVALID_FIELD); the change would leave no email and no phone (CUSTOMER_CONTACT_REQUIRED); a malformed email (INVALID_EMAIL); the body is not an object (INVALID_BODY)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks write:payments"},"404":{"description":"No such live billing customer in this business (CUSTOMER_NOT_FOUND), or customer_profile_id is not a live card of it (CUSTOMER_PROFILE_NOT_FOUND)"},"409":{"description":"Another billing customer of this business already has this email (CUSTOMER_EMAIL_IN_USE)"},"500":{"description":"Failed to update customer (reported to us; retry)"},"503":{"description":"The plan could not be checked; nothing was written - retry (PLAN_CHECK_FAILED)"}},"x-required-scope":"write:payments"}},"/payments/customers/resolve":{"post":{"operationId":"payments_customer_resolve","summary":"Resolve Billing Customer","description":"The billing customer of a customer card (Customer 360; the customer_id POST /contacts answers): found when the card already has one, else created from the card's own name, email and phone (nothing you send is written onto the card). Two API calls for one card at once get one billing customer. Answers the customer and created. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `write:payments`.","tags":["Payments"],"requestBody":{"description":"The customer card","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"customer_profile_id":{"type":"integer","description":"A customer card of your business (positive integer)","example":512}},"required":["customer_profile_id"]}}}},"responses":{"200":{"description":"The card already had one; created is false"},"201":{"description":"Created a billing customer for the card","content":{"application/json":{"example":{"success":true,"data":{"id":"0d9e8f7a-6b5c-4d3e-8f21-a1b2c3d4e5f6","customer_profile_id":512,"name":"Dana Levi","email":"dana@example.com","phone":"972501234567","address_line1":"Herzl 1","address_line2":null,"city":"Tel Aviv","state":null,"postal_code":"6100000","country":"IL","client_type":"business","tax_id":"515555555","tax_exempt":false,"payment_terms":"shotef","payment_terms_days":30,"default_currency":"ILS","contact_person":"Dana","tags":["vip"],"total_revenue":1200.5,"total_debt":0,"credit_limit":5000,"price_list_id":null,"last_activity_at":"2026-10-17T08:00:00.000Z","created_at":"2026-10-01T08:00:00.000Z","updated_at":"2026-10-17T08:00:00.000Z","created":true}}}}},"400":{"description":"customer_profile_id missing or not a positive whole number (INVALID_CUSTOMER_PROFILE_ID)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks write:payments"},"404":{"description":"No such customer card in this business, or it is archived (CUSTOMER_PROFILE_NOT_FOUND)"},"409":{"description":"Another billing customer of this business already has the card's email (CUSTOMER_EMAIL_IN_USE)"},"500":{"description":"Failed to resolve customer (reported to us; retry)"},"503":{"description":"The plan could not be checked; nothing was written - retry (PLAN_CHECK_FAILED)"}},"x-required-scope":"write:payments"}},"/payment-requests":{"post":{"operationId":"payments_request_create","summary":"Create Payment Request","description":"Create a payment link for a customer. amount is what you type; vat_basis says whether it already includes VAT (gross, the default) or VAT is added on top (net). The answer's amount is always what the customer pays. Send amount or line_items; with line_items the amount is their sum and a sent amount is ignored. invoice_id collects an existing invoice (the answer carries it); a deposit charges part of the deal. Nothing is sent to the customer - share payment_link yourself. Fires payment_request.created. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `write:payments`.","tags":["Payments"],"requestBody":{"description":"Payment request details; send amount or line_items","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"number","description":"Amount, more than 0 (a number or a decimal string). Required unless line_items is sent; ignored when it is","example":100},"vat_basis":{"type":"string","description":"gross = amount includes VAT (default); net = VAT is added on top","enum":["gross","net"],"example":"net"},"currency":{"type":"string","description":"Three-letter currency code (default ILS)","example":"ILS"},"description":{"type":"string","description":"What the payment is for","example":"Consultation, October"},"customer_name":{"type":"string","description":"Customer name","example":"Dana Levi"},"customer_phone":{"type":"string","description":"Customer phone, kept on the request (to link a customer card send customer_id or customer_profile_id)","example":"0501234567"},"customer_email":{"type":"string","description":"Customer email, kept on the request (to link a customer card send customer_id or customer_profile_id)","example":"dana@example.com"},"customer_id":{"type":"string","description":"A billing customer of your business (UUID); another business's id reads as not found","example":"3f2e1d0c-9b8a-4c7d-8e6f-5a4b3c2d1e0f"},"customer_profile_id":{"type":"integer","description":"A customer card of your business (positive integer); its billing customer is found or created","example":512},"invoice_id":{"type":"string","description":"An open invoice of your business (UUID) this request collects: same currency, vat_basis gross, at most the open balance, no deposit, one live request per invoice","example":"7c6b5a4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d"},"line_items":{"type":"array","items":{},"description":"Lines of { description, quantity (above 0, default 1), unit_price (a number, default 0; negative = a discount line), catalog_item_id?, sku? }; the amount is their sum","example":[{"description":"Lesson","quantity":2,"unit_price":59}]},"deposit_amount":{"type":"number","description":"Charge only this part now, above 0 and below the full amount (on the same vat_basis)","example":300},"deposit_percent":{"type":"number","description":"Charge only this percent of the full amount now (used when deposit_amount is not sent)","example":30},"expires_at":{"type":"string","description":"When the link stops working (ISO date or date-time); more than one day in the past is refused","example":"2026-11-01"},"reference":{"type":"string","description":"Your own reference, text up to 255 characters","example":"ORDER-1042"}}}}}},"responses":{"201":{"description":"Request created; amount is the total the customer pays","content":{"application/json":{"example":{"success":true,"data":{"id":"9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d","amount":118,"currency":"ILS","description":"Consultation, October","status":"draft","vat_basis":"net","net_amount":100,"vat_amount":18,"vat_rate":18,"expires_at":null,"created_at":"2026-10-16T10:00:00.000Z","payment_link":"https://app.lynxbe.co.il/pay/checkout/0f3c9a","invoice_id":"7c6b5a4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d"}}}}},"400":{"description":"amount missing, not a number or 0 after rounding to the agora (INVALID_AMOUNT); a line item without a quantity above 0 or a numeric unit_price (INVALID_LINE_ITEMS); currency not three letters (INVALID_CURRENCY); vat_basis not gross / net (INVALID_VAT_BASIS); the total with VAT over the limit (AMOUNT_OVER_LIMIT); a deposit not above 0 and below the full amount (INVALID_DEPOSIT); expires_at not a date or more than a day in the past (INVALID_EXPIRES_AT); reference not text up to 255 (INVALID_REFERENCE); customer_id not a UUID (INVALID_CUSTOMER_ID); customer_profile_id not a positive integer (INVALID_CUSTOMER_PROFILE_ID); invoice_id not a UUID (INVALID_INVOICE_ID); the invoice is not payable or has nothing open (INVOICE_NOT_PAYABLE), another currency (INVOICE_CURRENCY_MISMATCH), vat_basis net (INVOICE_VAT_BASIS_MUST_BE_GROSS), an amount above its open balance (INVOICE_AMOUNT_ABOVE_OUTSTANDING), a deposit (INVOICE_DEPOSIT_UNSUPPORTED) or another customer (INVOICE_CUSTOMER_MISMATCH)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks the scope"},"404":{"description":"invoice_id, customer_id or customer_profile_id not found in your business (INVOICE_NOT_FOUND, CUSTOMER_NOT_FOUND, CUSTOMER_PROFILE_NOT_FOUND)"},"409":{"description":"The invoice already has a live payment request; details.existing_request_id names it (INVOICE_REQUEST_EXISTS); customer_id and customer_profile_id name different people (CUSTOMER_IDENTITY_MISMATCH)"},"500":{"description":"Failed to create payment request (reported to us; retry)"},"503":{"description":"The plan could not be checked; nothing was written (PLAN_CHECK_FAILED)"}},"x-required-scope":"write:payments"},"get":{"operationId":"payments_request_list","summary":"List Payment Requests","description":"Payment requests of your business, newest first, with the status the app shows and pagination.total (the rows matching the filters): an open link past its expiry reads expired; pending is sent, canceled is cancelled (both spellings work in ?status). Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `read:payments`.","tags":["Payments"],"parameters":[{"name":"status","in":"query","description":"Only requests showing this status (authorized = an escrow hold)","schema":{"type":"string","enum":["draft","sent","paid","expired","cancelled","authorized","active"]}},{"name":"customer_id","in":"query","description":"Only requests of this billing customer (the customer_id the request detail answers)","schema":{"type":"string","example":"11111111-2222-4333-8444-555555555555"}},{"name":"invoice_id","in":"query","description":"Only requests that collect this invoice","schema":{"type":"string","example":"66666666-7777-4888-9999-000000000000"}},{"name":"reference","in":"query","description":"Only requests with exactly this reference (up to 255 characters)","schema":{"type":"string","example":"ORD-7"}},{"name":"date_from","in":"query","description":"Created on or after this day, YYYY-MM-DD (Israel time)","schema":{"type":"string","example":"2026-10-01"}},{"name":"date_to","in":"query","description":"Created on or before this day, YYYY-MM-DD (Israel time)","schema":{"type":"string","example":"2026-10-31"}},{"name":"limit","in":"query","description":"Rows per page, 1-100 (default 20)","schema":{"type":"integer","example":20}},{"name":"offset","in":"query","description":"Rows to skip (default 0)","schema":{"type":"integer","example":0}}],"responses":{"200":{"description":"One page of payment requests","content":{"application/json":{"example":{"success":true,"data":[{"id":"9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d","amount":118,"currency":"ILS","description":"Consultation, October","status":"sent","vat_basis":"net","net_amount":100,"vat_amount":18,"vat_rate":18,"expires_at":"2026-10-23T10:00:00.000Z","created_at":"2026-10-16T10:00:00.000Z","payment_link":"https://app.lynxbe.co.il/pay/checkout/0f3c9a"}],"pagination":{"limit":20,"offset":0,"total":1}}}}},"400":{"description":"Unknown status (INVALID_STATUS); limit outside 1-100 or a negative offset (INVALID_PAGE); customer_id not a uuid (INVALID_CUSTOMER_ID); invoice_id not a uuid (INVALID_INVOICE_ID); reference over 255 characters (INVALID_REFERENCE); a day that is not YYYY-MM-DD (INVALID_DATE); date_from after date_to (INVALID_DATE_RANGE). An empty value is no filter"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks the scope"},"500":{"description":"Failed to list payment requests (reported to us; retry)"}},"x-required-scope":"read:payments"}},"/payment-requests/{id}":{"get":{"operationId":"payments_request_get","summary":"Get Payment Request","description":"One payment request of your business, with the status the app shows, what was paid so far (a paid request reads at least its amount), its reference, customer, linked invoice and line items. Another business's id answers 404. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `read:payments`.","tags":["Payments"],"parameters":[{"name":"id","in":"path","required":true,"description":"Payment request uuid","schema":{"type":"string"}}],"responses":{"200":{"description":"The payment request","content":{"application/json":{"example":{"success":true,"data":{"id":"9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d","amount":118,"currency":"ILS","description":"Consultation, October","status":"paid","vat_basis":"net","net_amount":100,"vat_amount":18,"vat_rate":18,"expires_at":"2026-10-23T10:00:00.000Z","created_at":"2026-10-16T10:00:00.000Z","payment_link":"https://app.lynxbe.co.il/pay/checkout/0f3c9a","amount_paid":118,"paid_at":"2026-10-17T08:12:00.000Z","reference":"ORD-7","customer_id":"11111111-2222-4333-8444-555555555555","invoice_id":"66666666-7777-4888-9999-000000000000","line_items":[{"description":"Consultation hour","quantity":1,"unit_price":100}]}}}}},"400":{"description":"The id is not a uuid (INVALID_PAYMENT_REQUEST_ID)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks read:payments"},"404":{"description":"No such payment request in this business (PAYMENT_REQUEST_NOT_FOUND)"},"500":{"description":"Failed to get payment request (reported to us; retry)"}},"x-required-scope":"read:payments"},"patch":{"operationId":"payments_request_update","summary":"Update Payment Request","description":"Correct what an unpaid request charges - the amount or the line items it is summed from, the VAT basis, the description - and keep its link and history. Send only what changes; amount or line items replace the old figure (an empty line_items list = a plain amount). The status changes through /cancel, the expiry through /extend. Same values answer 200 with changed false. Audited with before / after. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `write:payments`.","tags":["Payments"],"parameters":[{"name":"id","in":"path","required":true,"description":"Payment request uuid","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"number","description":"New amount (> 0) on the VAT basis; ignored when line_items has lines"},"line_items":{"type":"array","items":{},"description":"Lines { description (1-200 chars), quantity > 0 (default 1), unit_price >= 0, sku?, catalog_item_id? }; the amount is their sum"},"vat_basis":{"type":"string","description":"gross (the amount includes VAT) or net (VAT is added)","enum":["gross","net"]},"description":{"type":"string","description":"Up to 500 characters; empty or null clears it"},"currency":{"type":"string","description":"Only the request's own currency is accepted; another one is a new request"}}}}}},"responses":{"200":{"description":"The request as GET reads it, with changed","content":{"application/json":{"example":{"success":true,"changed":true,"data":{"id":"9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d","amount":118,"currency":"ILS","description":"Consultation, October","status":"sent","vat_basis":"net","net_amount":100,"vat_amount":18,"vat_rate":18,"expires_at":"2026-10-23T10:00:00.000Z","created_at":"2026-10-16T10:00:00.000Z","payment_link":"https://app.lynxbe.co.il/pay/checkout/0f3c9a","amount_paid":0,"paid_at":null,"reference":"ORD-7","customer_id":null,"invoice_id":null,"line_items":[{"description":"Consultation hour","quantity":1,"unit_price":100}]}}}}},"400":{"description":"A field PATCH does not take (UNKNOWN_FIELD, details.fields); nothing to change (PAYMENT_REQUEST_NOTHING_TO_UPDATE); amount not > 0 (PAYMENT_REQUEST_INVALID_AMOUNT); a bad line (PAYMENT_REQUEST_INVALID_LINE_ITEMS); bad basis (INVALID_VAT_BASIS); description over 500 (PAYMENT_REQUEST_INVALID_DESCRIPTION); over the ceiling (PAYMENT_REQUEST_AMOUNT_TOO_HIGH); under the minimum partial payment (PAYMENT_REQUEST_MIN_PARTIAL_ABOVE_AMOUNT); another currency (PAYMENT_REQUEST_CURRENCY_LOCKED); the id is not a uuid (INVALID_PAYMENT_REQUEST_ID)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks write:payments; the plan check failed (PLAN_CHECK_FAILED, 503)"},"404":{"description":"No such payment request in this business (PAYMENT_REQUEST_NOT_FOUND)"},"409":{"description":"Paid (PAYMENT_REQUEST_ALREADY_PAID), cancelled (PAYMENT_REQUEST_CANCELLED), money received or a charge in progress (PAYMENT_REQUEST_HAS_PAYMENT), a state that cannot be edited (PAYMENT_REQUEST_NOT_EDITABLE), a deposit / balance / escrow request (PAYMENT_REQUEST_KIND_LOCKED), or the customer opened the payment page in the last 30 minutes (PAYMENT_REQUEST_CHECKOUT_OPEN)"},"500":{"description":"Failed to update payment request (reported to us; retry)"}},"x-required-scope":"write:payments"}},"/payment-requests/{id}/cancel":{"post":{"operationId":"payments_request_cancel","summary":"Cancel Payment Request","description":"Cancel an open payment request of your business: its link stops taking payments. No money moves and no message is sent. A request already cancelled answers 200 with changed false (nothing written). Audited; fires payment_request.cancelled. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `write:payments`.","tags":["Payments"],"parameters":[{"name":"id","in":"path","required":true,"description":"Payment request uuid","schema":{"type":"string"}}],"responses":{"200":{"description":"The request as GET reads it, with changed (false when it was already cancelled)","content":{"application/json":{"example":{"success":true,"changed":true,"data":{"id":"9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d","amount":118,"currency":"ILS","description":"Consultation, October","status":"cancelled","vat_basis":"net","net_amount":100,"vat_amount":18,"vat_rate":18,"expires_at":"2026-10-23T10:00:00.000Z","created_at":"2026-10-16T10:00:00.000Z","payment_link":"https://app.lynxbe.co.il/pay/checkout/0f3c9a","amount_paid":0,"paid_at":null,"reference":"ORD-7","customer_id":null,"invoice_id":null,"line_items":[{"description":"Consultation hour","quantity":1,"unit_price":100}]}}}}},"400":{"description":"The id is not a uuid (INVALID_PAYMENT_REQUEST_ID)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks write:payments; the plan check failed (PLAN_CHECK_FAILED, 503)"},"404":{"description":"No such payment request in this business (PAYMENT_REQUEST_NOT_FOUND)"},"409":{"description":"Already paid - refund it instead (PAYMENT_REQUEST_ALREADY_PAID); part of it was already paid (PAYMENT_REQUEST_HAS_PAYMENT); money is held on the card by an escrow hold - release the hold in the app (PAYMENT_REQUEST_HOLD_ON_CARD); a split bill or another state closed from its own screen (PAYMENT_REQUEST_STATE_LOCKED)"},"500":{"description":"Failed to cancel payment request (reported to us; retry)"}},"x-required-scope":"write:payments"}},"/payment-requests/{id}/extend":{"post":{"operationId":"payments_request_extend","summary":"Extend Payment Request","description":"Give a payment link more time: the new expiry is days from now (not from the old expiry), so a link that lapsed can be paid again (a request marked expired reads sent again). Audited with the old and new expiry. Requires Lynxbe Pay on the plan.\n\nRequired API key scope: `write:payments`.","tags":["Payments"],"parameters":[{"name":"id","in":"path","required":true,"description":"Payment request uuid","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"integer","description":"Whole days from now, 1-365; default 30"}}}}}},"responses":{"200":{"description":"The request as GET reads it, with the new expires_at","content":{"application/json":{"example":{"success":true,"changed":true,"data":{"id":"9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d","amount":118,"currency":"ILS","description":"Consultation, October","status":"sent","vat_basis":"net","net_amount":100,"vat_amount":18,"vat_rate":18,"expires_at":"2026-11-17T10:00:00.000Z","created_at":"2026-10-16T10:00:00.000Z","payment_link":"https://app.lynxbe.co.il/pay/checkout/0f3c9a","amount_paid":0,"paid_at":null,"reference":"ORD-7","customer_id":null,"invoice_id":null,"line_items":[{"description":"Consultation hour","quantity":1,"unit_price":100}]}}}}},"400":{"description":"days is not a whole number 1-365 (INVALID_DAYS); the id is not a uuid (INVALID_PAYMENT_REQUEST_ID)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Lynxbe Pay is not on the plan (MODULE_LOCKED), or the key lacks write:payments; the plan check failed (PLAN_CHECK_FAILED, 503)"},"404":{"description":"No such payment request in this business (PAYMENT_REQUEST_NOT_FOUND)"},"409":{"description":"Already paid (PAYMENT_REQUEST_ALREADY_PAID) or cancelled (PAYMENT_REQUEST_CANCELLED) - create a new request; a split bill or another state extended from its own screen (PAYMENT_REQUEST_STATE_LOCKED)"},"500":{"description":"Failed to extend payment request (reported to us; retry)"}},"x-required-scope":"write:payments"}},"/catalog":{"get":{"operationId":"catalog_list","summary":"List Catalog","description":"Get active commerce catalog items (menu/products) for external storefronts. Requires Commerce Catalog on the plan.\n\nRequired API key scope: `read:catalog`.","tags":["Commerce & Orders"],"responses":{"200":{"description":"Catalog items"},"403":{"description":"Forbidden - key lacks read:catalog, or the plan has no Commerce Catalog"}},"x-required-scope":"read:catalog"}},"/catalog/items/{id}":{"get":{"operationId":"catalog_item_get","summary":"Get Catalog Item","description":"One active catalog item by id. A deactivated item answers 404.\n\nRequired API key scope: `read:catalog`.","tags":["Commerce & Orders"],"parameters":[{"name":"id","in":"path","required":true,"description":"Catalog item uuid","schema":{"type":"string"}}],"responses":{"200":{"description":"Catalog item","content":{"application/json":{"example":{"success":true,"data":{"id":"3f9a2c1e-7b4d-4e8a-9c21-5d6e7f8a9b0c","item_type":"product","category_id":"8c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f","category":"Coffee","sku":"CB-1","name":"Coffee beans 1kg","description":"Medium roast","image_url":null,"base_price":120,"currency":"ILS","unit":"kg","default_pack_size":null,"track_inventory":true,"stock_quantity":42,"sort_order":0,"metadata":{},"created_at":"2026-09-01T10:00:00.000Z","updated_at":"2026-09-20T08:30:00.000Z"}}}}},"400":{"description":"Bad request - CATALOG_ITEM_ID_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:catalog, or the plan has no Commerce Catalog"},"404":{"description":"CATALOG_ITEM_NOT_FOUND - no such active item in this business"}},"x-required-scope":"read:catalog"}},"/catalog/categories":{"get":{"operationId":"catalog_categories","summary":"List Catalog Categories","description":"Active catalog categories in display order. parent_id links sub-categories.\n\nRequired API key scope: `read:catalog`.","tags":["Commerce & Orders"],"parameters":[{"name":"limit","in":"query","description":"Rows (1-100)","schema":{"type":"integer","default":100}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Categories","content":{"application/json":{"example":{"success":true,"data":[{"id":"8c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f","parent_id":null,"name":"Coffee","description":null,"image_url":null,"sort_order":0}],"total":1,"limit":100,"offset":0}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:catalog, or the plan has no Commerce Catalog"}},"x-required-scope":"read:catalog"}},"/storefront/config":{"get":{"operationId":"storefront_config","summary":"Storefront Config","description":"The business storefront presentation and checkout policy (store name, currency, VAT, pickup/delivery, returns, terms links) for external storefronts.\n\nRequired API key scope: `read:catalog`.","tags":["Commerce & Orders"],"responses":{"200":{"description":"Storefront configuration","content":{"application/json":{"example":{"success":true,"data":{"store_name":"My Store","currency":"ILS","vat_inclusive":true,"pickup_enabled":true,"delivery_enabled":true,"delivery_flat_rate":25,"free_delivery_from":300,"returns_days":14,"terms_url":null,"privacy_url":null}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks the read:catalog scope"},"404":{"description":"Business not found"}},"x-required-scope":"read:catalog"}},"/orders":{"post":{"operationId":"orders_create","summary":"Create Order","description":"Create an order with items. Fires the order.created webhook.\n\nRequired API key scope: `write:orders`.","tags":["Commerce & Orders"],"requestBody":{"description":"Order details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"customer_name":{"type":"string","description":"Customer name"},"customer_phone":{"type":"string","description":"Customer phone"},"items":{"type":"array","items":{},"description":"Array of { item_id, quantity, notes }"},"notes":{"type":"string","description":"Order notes"}},"required":["items"]}}}},"responses":{"201":{"description":"Order created"}},"x-required-scope":"write:orders"},"get":{"operationId":"orders_list","summary":"List Orders","description":"Get recent orders with statuses.\n\nRequired API key scope: `read:orders`.","tags":["Commerce & Orders"],"parameters":[{"name":"limit","in":"query","description":"Max results (default 20)","schema":{"type":"integer"}},{"name":"status","in":"query","description":"Filter by status","schema":{"type":"string"}}],"responses":{"200":{"description":"List of orders"}},"x-required-scope":"read:orders"}},"/orders/{id}":{"get":{"operationId":"orders_get","summary":"Get POS Order","description":"One POS order with its items.\n\nRequired API key scope: `read:orders`.","tags":["Commerce & Orders"],"parameters":[{"name":"id","in":"path","required":true,"description":"Order uuid","schema":{"type":"string"}}],"responses":{"200":{"description":"Order","content":{"application/json":{"example":{"success":true,"data":{"id":"6b1e2d3c-4a5f-4e6d-8c7b-9a0f1e2d3c4b","order_number":"1042","order_type":"takeaway","status":"ready","subtotal":96,"discount_amount":0,"tax_amount":13.95,"total":96,"customer_name":"Dana","customer_phone":"+972501234567","order_source":"api","delivery_stage":null,"opened_at":"2026-09-29T11:02:00.000Z","closed_at":null,"items":[{"id":"...","menu_item_id":"...","name_he":"פיצה","name_en":"Pizza","quantity":2,"unit_price":48,"status":"ready","voided":false}]}}}}},"400":{"description":"Bad request - ORDER_ID_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks the scope, or the plan has no POS"},"404":{"description":"ORDER_NOT_FOUND - no such order in this business"}},"x-required-scope":"read:orders"}},"/orders/{id}/status":{"patch":{"operationId":"orders_status","summary":"Update POS Order Status","description":"Move an order through the kitchen flow: in_progress, ready or closed. Closing frees its table. Voiding and reopening stay in the app. The live POS screen updates at once.\n\nRequired API key scope: `write:orders`.","tags":["Commerce & Orders"],"parameters":[{"name":"id","in":"path","required":true,"description":"Order uuid","schema":{"type":"string"}}],"requestBody":{"description":"The new status","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"in_progress, ready or closed","example":"ready"}},"required":["status"]}}}},"responses":{"200":{"description":"Updated order","content":{"application/json":{"example":{"success":true,"data":{"id":"6b1e2d3c-4a5f-4e6d-8c7b-9a0f1e2d3c4b","order_number":"1042","status":"ready","previous_status":"in_progress"}}}}},"400":{"description":"Bad request - ORDER_ID_INVALID or STATUS_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks the scope, or the plan has no POS"},"404":{"description":"ORDER_NOT_FOUND - no such order in this business"},"409":{"description":"ORDER_FINISHED - the order is already closed or voided; reopening stays in the app"}},"x-required-scope":"write:orders"}},"/products":{"get":{"operationId":"products_list","summary":"List Products","description":"Get products with SKU, price, and category.\n\nRequired API key scope: `read:catalog`.","tags":["Commerce & Orders"],"parameters":[{"name":"category","in":"query","description":"Filter by category","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max results (default 20, max 100)","schema":{"type":"integer"}},{"name":"offset","in":"query","description":"Pagination offset","schema":{"type":"integer"}}],"responses":{"200":{"description":"List of products"}},"x-required-scope":"read:catalog"},"post":{"operationId":"products_create","summary":"Create Product","description":"Create a new product. Fires the product.created webhook.\n\nRequired API key scope: `write:catalog`.","tags":["Commerce & Orders"],"requestBody":{"description":"Product details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Product name"},"description":{"type":"string","description":"Product description"},"category":{"type":"string","description":"Product category"},"sku":{"type":"string","description":"Stock keeping unit"},"price":{"type":"number","description":"Price"},"currency":{"type":"string","description":"Currency code (default ILS)"}},"required":["name"]}}}},"responses":{"201":{"description":"Product created"}},"x-required-scope":"write:catalog"}},"/contracts":{"get":{"operationId":"contracts_list","summary":"List Contracts","description":"Get contract documents for your business with document number, client, value, and status.\n\nRequired API key scope: `read:contracts`.","tags":["Contracts"],"parameters":[{"name":"status","in":"query","description":"Filter by document status","schema":{"type":"string"}},{"name":"category","in":"query","description":"Filter by category (e.g. contract, proposal)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max results (default 20, max 100)","schema":{"type":"integer"}},{"name":"offset","in":"query","description":"Pagination offset","schema":{"type":"integer"}}],"responses":{"200":{"description":"List of contracts"}},"x-required-scope":"read:contracts"},"post":{"operationId":"contracts_create","summary":"Create Contract / Proposal","description":"Create a new contract or proposal in draft status on the service linked to your business, with per-signer access tokens for later signing.\n\nRequired API key scope: `write:contracts`.","tags":["Contracts"],"requestBody":{"description":"Contract details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Document title"},"category":{"type":"string","description":"Document category","enum":["contract","proposal"]},"content":{"type":"object","additionalProperties":true,"description":"Document content/data"},"client_name":{"type":"string","description":"Client name"},"client_email":{"type":"string","description":"Client email"},"client_phone":{"type":"string","description":"Client phone"},"total_value":{"type":"number","description":"Total value of the document"},"currency":{"type":"string","description":"Currency code","example":"ILS"},"signers":{"type":"array","items":{},"description":"List of signers: { name, email, phone? }"}},"required":["title"]}}}},"responses":{"201":{"description":"Draft contract/proposal created"},"404":{"description":"No service linked to this business"}},"x-required-scope":"write:contracts"}},"/contracts/{id}":{"get":{"operationId":"contracts_get_status","summary":"Get Contract Status","description":"Get a single contract document including its signers and their signing status.\n\nRequired API key scope: `read:contracts`.","tags":["Contracts"],"parameters":[{"name":"id","in":"path","required":true,"description":"Contract document ID (UUID)","schema":{"type":"string"}}],"responses":{"200":{"description":"Contract with signers","content":{"application/json":{"example":{"success":true,"data":{"id":"...","title":"Service Agreement","status":"sent","signers":[{"name":"...","email":"...","status":"pending","signed_at":null}]}}}}},"404":{"description":"Contract not found"}},"x-required-scope":"read:contracts"}},"/proposals":{"get":{"operationId":"proposals_list","summary":"List Proposals","description":"Get proposal documents for your business with client, value, and status.\n\nRequired API key scope: `read:contracts`.","tags":["Contracts"],"parameters":[{"name":"status","in":"query","description":"Filter by document status","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max results (default 20, max 100)","schema":{"type":"integer"}},{"name":"offset","in":"query","description":"Pagination offset","schema":{"type":"integer"}}],"responses":{"200":{"description":"List of proposals"}},"x-required-scope":"read:contracts"}},"/contracts/{id}/send":{"post":{"operationId":"contracts_sign","summary":"Send for Signing","description":"Mark a draft contract or proposal as sent and get per-signer signing links. The API does NOT dispatch email or WhatsApp — you deliver the returned links to the signers yourself. Fires the contract.sent (or proposal.sent) webhook.\n\nRequired API key scope: `write:contracts`.","tags":["Contracts"],"parameters":[{"name":"id","in":"path","required":true,"description":"Contract ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Document marked sent; returns signing links: [{ signer, url }]"},"404":{"description":"Contract not found or not in a sendable status"}},"x-required-scope":"write:contracts"}},"/employees":{"get":{"operationId":"workforce_employees","summary":"List Employees","description":"Get all employees.\n\nRequired API key scope: `people:read`.","tags":["Workforce Management"],"responses":{"200":{"description":"List of employees"}},"x-required-scope":"people:read"},"post":{"operationId":"workforce_employee_create","summary":"Create Employee","description":"Create a workforce employee.\n\nRequired API key scope: `people:write`.","tags":["Workforce Management"],"requestBody":{"description":"Employee details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"full_name":{"type":"string","description":"Full name"},"israeli_id":{"type":"string","description":"Government ID"},"phone":{"type":"string","description":"Phone number"},"email":{"type":"string","description":"Email"}},"required":["full_name"]}}}},"responses":{"201":{"description":"Employee created"}},"x-required-scope":"people:write"}},"/tasks":{"get":{"operationId":"tasks_list","summary":"List Tasks","description":"Get workforce tasks with optional status and assignee filters.\n\nRequired API key scope: `read:tasks`.","tags":["Workforce Management"],"parameters":[{"name":"status","in":"query","description":"Filter by status","schema":{"type":"string"}},{"name":"assignee","in":"query","description":"Filter by assignee","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max results (default 50)","schema":{"type":"integer"}}],"responses":{"200":{"description":"List of tasks"}},"x-required-scope":"read:tasks"},"post":{"operationId":"tasks_create","summary":"Create Task","description":"Create a workforce task. Fires the workforce.task.created webhook.\n\nRequired API key scope: `write:tasks`.","tags":["Workforce Management"],"requestBody":{"description":"Task details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Task title"},"description":{"type":"string","description":"Task description"},"assignee":{"type":"string","description":"Assignee identifier"},"priority":{"type":"string","description":"Priority (default normal)"},"due_at":{"type":"string","description":"Due date/time (ISO)"}},"required":["title"]}}}},"responses":{"201":{"description":"Task created"}},"x-required-scope":"write:tasks"}},"/employees/{id}":{"get":{"operationId":"workforce_employee_get","summary":"Get Employee","description":"Get one employee by ID.\n\nRequired API key scope: `people:read`.","tags":["Workforce Management"],"parameters":[{"name":"id","in":"path","required":true,"description":"Employee ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Employee detail"}},"x-required-scope":"people:read"},"patch":{"operationId":"workforce_employee_update","summary":"Update Employee","description":"Patch workforce employee fields.\n\nRequired API key scope: `people:write`.","tags":["Workforce Management"],"parameters":[{"name":"id","in":"path","required":true,"description":"Employee ID","schema":{"type":"string"}}],"requestBody":{"description":"Employee patch","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"full_name":{"type":"string","description":"Full name"},"phone":{"type":"string","description":"Phone number"},"email":{"type":"string","description":"Email"},"status":{"type":"string","description":"Employee status"}}}}}},"responses":{"200":{"description":"Employee updated"}},"x-required-scope":"people:write"}},"/attendance":{"get":{"operationId":"workforce_attendance_list","summary":"List Attendance","description":"Get attendance entries by employee and date range.\n\nRequired API key scope: `attendance:read`.","tags":["Workforce Management"],"parameters":[{"name":"employeeId","in":"query","description":"Employee ID","schema":{"type":"string"}},{"name":"from","in":"query","description":"Start date","schema":{"type":"string"}},{"name":"to","in":"query","description":"End date","schema":{"type":"string"}}],"responses":{"200":{"description":"Attendance entries"}},"x-required-scope":"attendance:read"},"post":{"operationId":"workforce_attendance_create","summary":"Create Attendance","description":"Create a manual attendance entry.\n\nRequired API key scope: `attendance:write`.","tags":["Workforce Management"],"requestBody":{"description":"Attendance entry","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"employeeId":{"type":"string","description":"Employee ID"},"clockIn":{"type":"string","description":"Clock-in timestamp"},"clockOut":{"type":"string","description":"Clock-out timestamp"},"method":{"type":"string","description":"Reporting method"}},"required":["employeeId","clockIn"]}}}},"responses":{"201":{"description":"Attendance created"}},"x-required-scope":"attendance:write"}},"/events":{"get":{"operationId":"workforce_events_list","summary":"List Events","description":"List workforce events and absences.\n\nRequired API key scope: `events:read`.","tags":["Workforce Management"],"responses":{"200":{"description":"Events"}},"x-required-scope":"events:read"},"post":{"operationId":"workforce_events_create","summary":"Create Event","description":"Create a workforce event.\n\nRequired API key scope: `events:write`.","tags":["Workforce Management"],"requestBody":{"description":"Event details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"employeeId":{"type":"string","description":"Employee ID"},"type":{"type":"string","description":"Event type"},"date":{"type":"string","description":"Event date"},"notes":{"type":"string","description":"Notes"}},"required":["type","date"]}}}},"responses":{"201":{"description":"Event created"}},"x-required-scope":"events:write"}},"/reports/monthly":{"get":{"operationId":"workforce_monthly_report","summary":"Monthly Report","description":"Get monthly attendance report.\n\nRequired API key scope: `reports:read`.","tags":["Workforce Management"],"parameters":[{"name":"month","in":"query","required":true,"description":"Month in YYYY-MM","schema":{"type":"string"}}],"responses":{"200":{"description":"Monthly report"}},"x-required-scope":"reports:read"}},"/payroll/export":{"get":{"operationId":"workforce_payroll_export","summary":"Payroll Export","description":"The payroll export files built for a month (newest first, up to 20), with the file content for your payroll vendor. Use a file whose status is completed and signed_off_at is set (signed off by the business); file_sha256 lets you verify it. Requires Payroll on the plan.\n\nRequired API key scope: `payroll:read`.","tags":["Workforce Management"],"parameters":[{"name":"month","in":"query","required":true,"description":"Month in YYYY-MM","schema":{"type":"string","example":"2026-09"}}],"responses":{"200":{"description":"Payroll exports","content":{"application/json":{"example":{"success":true,"data":[{"id":"3a2b1c0d-9e8f-4a7b-8c6d-5e4f3a2b1c0d","year":2026,"month":9,"vendor":"hilan","status":"completed","file_name":"payroll-2026-09.csv","row_count":14,"file_sha256":"9c1f...","file_content":"employee_id,hours,...","validation_summary":{"errors":0,"warnings":1},"signed_off_at":"2026-10-01T07:30:00.000Z","created_at":"2026-09-30T16:00:00.000Z","updated_at":"2026-10-01T07:30:00.000Z"}]}}}},"400":{"description":"Bad request - month is required as YYYY-MM"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks payroll:read, or the plan has no Payroll"}},"x-required-scope":"payroll:read"}},"/attendance/clock-in":{"post":{"operationId":"workforce_clock_in","summary":"Clock In","description":"Record employee clock in. Rejected if the employee already has an open attendance log (not clocked out).\n\nRequired API key scope: `attendance:write`.","tags":["Workforce Management"],"requestBody":{"description":"Clock in data","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"employee_id":{"type":"string","description":"Employee ID"},"latitude":{"type":"number","description":"GPS latitude (optional)"},"longitude":{"type":"number","description":"GPS longitude (optional)"}},"required":["employee_id"]}}}},"responses":{"201":{"description":"Clock in recorded"},"400":{"description":"Employee already has an open attendance log"}},"x-required-scope":"attendance:write"}},"/attendance/clock-out":{"post":{"operationId":"workforce_clock_out","summary":"Clock Out","description":"Record employee clock out. Closes the open attendance log and computes total hours.\n\nRequired API key scope: `attendance:write`.","tags":["Workforce Management"],"requestBody":{"description":"Clock out data","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"employee_id":{"type":"string","description":"Employee ID"},"latitude":{"type":"number","description":"Optional. Where the punch was made. If the business has attendance geofencing on and you SEND a location, it must fall inside an allowed area or the call is refused with 403 GEOFENCE_OUTSIDE. Omit it and the punch is treated as a manual entry, exactly as on the admin side."},"longitude":{"type":"number","description":"Optional. See latitude."}},"required":["employee_id"]}}}},"responses":{"200":{"description":"Clock out recorded with total hours"},"404":{"description":"No open attendance log found for employee"}},"x-required-scope":"attendance:write"}},"/shifts":{"get":{"operationId":"workforce_shifts","summary":"List Shifts","description":"Get the shift schedule (excludes cancelled shifts, max 200 results).\n\nRequired API key scope: `read:workforce`.","tags":["Workforce Management"],"parameters":[{"name":"start_date","in":"query","description":"Filter shifts from this date (YYYY-MM-DD)","schema":{"type":"string"}},{"name":"end_date","in":"query","description":"Filter shifts up to this date (YYYY-MM-DD)","schema":{"type":"string"}},{"name":"employee_id","in":"query","description":"Filter by employee","schema":{"type":"string"}}],"responses":{"200":{"description":"Shift schedule"}},"x-required-scope":"read:workforce"}},"/field-sales/visits":{"get":{"operationId":"fieldsales_visits","summary":"List Visits","description":"Get field sales visits with status and date filters, including check-in/check-out times and outcomes.\n\nRequired API key scope: `read:fieldsales`.","tags":["Field Sales"],"parameters":[{"name":"status","in":"query","description":"Filter by visit status","schema":{"type":"string"}},{"name":"date","in":"query","description":"Filter by scheduled date (YYYY-MM-DD)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max results (default 50)","schema":{"type":"integer"}}],"responses":{"200":{"description":"List of visits"}},"x-required-scope":"read:fieldsales"}},"/field-sales/orders":{"get":{"operationId":"fieldsales_orders","summary":"List Field Orders","description":"Get orders created during field sales visits, with totals and delivery dates.\n\nRequired API key scope: `read:fieldsales`.","tags":["Field Sales"],"parameters":[{"name":"status","in":"query","description":"Filter by order status","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max results (default 50)","schema":{"type":"integer"}}],"responses":{"200":{"description":"List of field sales orders"}},"x-required-scope":"read:fieldsales"}},"/coupons":{"get":{"operationId":"coupons_list","summary":"List Coupons","description":"Get all coupons for your business with code, discount, usage counts, and validity window.\n\nRequired API key scope: `read:coupons`.","tags":["Coupons"],"responses":{"200":{"description":"List of coupons"}},"x-required-scope":"read:coupons"},"post":{"operationId":"coupons_create","summary":"Create Coupon","description":"Create a new coupon for your business. A code is unique within your business - a duplicate returns 409.\n\nRequired API key scope: `write:coupons`.","tags":["Coupons"],"requestBody":{"description":"Coupon details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Coupon code (unique within your business)"},"name":{"type":"string","description":"Coupon display name"},"discount_type":{"type":"string","description":"Discount type","enum":["percentage","fixed_amount"]},"discount_value":{"type":"number","description":"Discount value"},"service_ids":{"type":"array","items":{},"description":"Restrict the coupon to specific services"},"min_amount":{"type":"number","description":"Minimum order amount"},"max_uses":{"type":"integer","description":"Maximum number of uses"},"valid_from":{"type":"string","description":"Validity start date"},"valid_until":{"type":"string","description":"Validity end date"}},"required":["code","discount_value"]}}}},"responses":{"201":{"description":"Coupon created"},"409":{"description":"Coupon code already exists"}},"x-required-scope":"write:coupons"}},"/coupons/{id}":{"get":{"operationId":"coupons_get","summary":"Get Coupon","description":"One coupon of your business, active or not, with its usage count and the services it is limited to (service_ids, null = any). Requires coupons on the plan.\n\nRequired API key scope: `read:coupons`.","tags":["Coupons"],"parameters":[{"name":"id","in":"path","required":true,"description":"Coupon id","schema":{"type":"integer","example":77}}],"responses":{"200":{"description":"The coupon","content":{"application/json":{"example":{"success":true,"data":{"id":77,"code":"SUMMER10","name":"Summer sale","discount_type":"percentage","discount_value":"10.00","min_amount":null,"max_uses":50,"used_count":3,"service_ids":null,"valid_from":"2026-06-01T00:00:00.000Z","valid_until":"2026-09-01T00:00:00.000Z","active":true,"created_at":"2026-05-20T09:00:00.000Z","updated_at":"2026-10-14T09:00:00.000Z"}}}}},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Coupons are not on the plan (MODULE_LOCKED), or the key lacks the scope (\"Insufficient scope. Required: read:coupons\")"},"404":{"description":"No coupon with this id in your business (COUPON_NOT_FOUND)"}},"x-required-scope":"read:coupons"},"patch":{"operationId":"coupons_update","summary":"Update Coupon","description":"Change a coupon of your business - only the fields you send, by the same rules as the app's coupon form: code 2-40 letters / digits / - / _ (stored upper-case, unique in your business), percentage at most 100 (judged with the stored type when you change type or value), end date after start (when you change a date). active is not changed here - use activate / deactivate. Error messages are in Hebrew; branch on code. Requires coupons on the plan.\n\nRequired API key scope: `write:coupons`.","tags":["Coupons"],"parameters":[{"name":"id","in":"path","required":true,"description":"Coupon id","schema":{"type":"integer","example":77}}],"requestBody":{"description":"Any of these fields (at least one).","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"2-40 characters: A-Z, 0-9, - and _","example":"SUMMER10"},"name":{"type":"string","description":"1-200 characters","example":"Summer sale"},"discount_type":{"type":"string","description":"percentage or fixed_amount (fixed is accepted and stored as fixed_amount)","example":"percentage"},"discount_value":{"type":"number","description":"Above 0; at most 100 for a percentage","example":10},"min_amount":{"type":"number","description":"Minimum order amount, 0 or more"},"max_uses":{"type":"integer","description":"Uses allowed, at least 1","example":50},"service_ids":{"type":"array","items":{},"description":"Service ids the coupon is limited to"},"valid_from":{"type":"string","description":"Start date","example":"2026-06-01"},"valid_until":{"type":"string","description":"End date, after the start","example":"2026-09-01"}}}}}},"responses":{"200":{"description":"The coupon after the change"},"400":{"description":"A value the app would refuse (INVALID_CODE, INVALID_NAME, INVALID_TYPE, INVALID_VALUE, INVALID_PERCENTAGE, INVALID_MIN_AMOUNT, INVALID_MAX_USES, INVALID_SERVICES, INVALID_DATE, INVALID_RANGE), nothing to change (NO_FIELDS), or a field not listed / active (UNKNOWN_FIELD)"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Coupons are not on the plan (MODULE_LOCKED), or the key lacks the scope (\"Insufficient scope. Required: write:coupons\")"},"404":{"description":"No coupon with this id in your business (COUPON_NOT_FOUND)"},"409":{"description":"Another coupon of your business has this code (CODE_EXISTS)"},"503":{"description":"The plan could not be checked; nothing was changed (PLAN_CHECK_FAILED)"}},"x-required-scope":"write:coupons"}},"/coupons/{id}/activate":{"post":{"operationId":"coupons_activate","summary":"Activate Coupon","description":"Turn a coupon on so it can be used. Already active (or two calls at once): 200, changed once. Requires coupons on the plan.\n\nRequired API key scope: `write:coupons`.","tags":["Coupons"],"parameters":[{"name":"id","in":"path","required":true,"description":"Coupon id","schema":{"type":"integer","example":77}}],"responses":{"200":{"description":"The coupon, active true"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Coupons are not on the plan (MODULE_LOCKED), or the key lacks the scope (\"Insufficient scope. Required: write:coupons\")"},"404":{"description":"No coupon with this id in your business (COUPON_NOT_FOUND)"},"503":{"description":"The plan could not be checked; nothing was changed (PLAN_CHECK_FAILED)"}},"x-required-scope":"write:coupons"}},"/coupons/{id}/deactivate":{"post":{"operationId":"coupons_deactivate","summary":"Deactivate Coupon","description":"Turn a coupon off: it stops validating and cannot be used; its history stays. Already inactive (or two calls at once): 200, changed once. Requires coupons on the plan.\n\nRequired API key scope: `write:coupons`.","tags":["Coupons"],"parameters":[{"name":"id","in":"path","required":true,"description":"Coupon id","schema":{"type":"integer","example":77}}],"responses":{"200":{"description":"The coupon, active false"},"401":{"description":"Unauthorized - invalid or missing API key"},"403":{"description":"Coupons are not on the plan (MODULE_LOCKED), or the key lacks the scope (\"Insufficient scope. Required: write:coupons\")"},"404":{"description":"No coupon with this id in your business (COUPON_NOT_FOUND)"},"503":{"description":"The plan could not be checked; nothing was changed (PLAN_CHECK_FAILED)"}},"x-required-scope":"write:coupons"}},"/coupons/validate":{"post":{"operationId":"coupons_validate","summary":"Validate Coupon","description":"Validate a coupon code against your business coupons and legacy global coupons. Checks active status, validity window, usage limit, minimum amount, and service restrictions. Returns { valid, discount_amount, reason? }.\n\nRequired API key scope: `read:coupons`.","tags":["Coupons"],"requestBody":{"description":"Validation request","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"Coupon code"},"serviceId":{"type":"string","description":"Service the coupon is applied to"},"amount":{"type":"number","description":"Order amount (for discount calculation and min_amount check)"}},"required":["code"]}}}},"responses":{"200":{"description":"Validation result: { valid, discount_amount, reason? }"}},"x-required-scope":"read:coupons"}},"/notifications/templates":{"get":{"operationId":"notif_templates_list","summary":"List Templates","description":"Get active notification templates for your business, plus shared system templates. Includes bilingual titles/content, type, and channel.\n\nRequired API key scope: `read:notifications`.","tags":["Notifications"],"responses":{"200":{"description":"List of templates"}},"x-required-scope":"read:notifications"}},"/notifications/send":{"post":{"operationId":"notif_send","summary":"Send Notification","description":"Send an existing notification to a target recipient over the notification channels. The notification must belong to your business or be a system notification.\n\nRequired API key scope: `send:notifications`.","tags":["Notifications"],"requestBody":{"description":"Notification details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"notification_id":{"type":"string","description":"Notification (template) ID to send"},"target":{"type":"object","additionalProperties":true,"description":"Recipient target — object with email, phone, or userId","example":{"email":"user@example.com"}},"language":{"type":"string","description":"Language for the notification content (en/he)"}},"required":["notification_id","target"]}}}},"responses":{"202":{"description":"Notification queued"},"404":{"description":"Notification not found"}},"x-required-scope":"send:notifications"}},"/notifications/history":{"get":{"operationId":"notif_history","summary":"Notification History","description":"Get notification delivery history for your business: per-send channel, recipient, status, error message, and sent time (latest 100).\n\nRequired API key scope: `read:notifications`.","tags":["Notifications"],"responses":{"200":{"description":"Delivery history (up to 100 most recent sends)"}},"x-required-scope":"read:notifications"}},"/social/accounts":{"get":{"operationId":"social_accounts","summary":"List Accounts","description":"Get connected Meta (Facebook/Instagram) accounts with page IDs and status.\n\nRequired API key scope: `read:social`.","tags":["Social Media"],"responses":{"200":{"description":"Connected accounts"}},"x-required-scope":"read:social"}},"/social/posts":{"get":{"operationId":"social_posts_list","summary":"List Posts","description":"Your posts, newest first, with their status and per-platform publish results.\n\nRequired API key scope: `read:social`.","tags":["Social Media"],"parameters":[{"name":"status","in":"query","description":"Post status","schema":{"type":"string","enum":["DRAFT","IN_REVIEW","APPROVED","SCHEDULED","QUEUED","PUBLISHING","PUBLISHED","FAILED","CANCELED","REPORTED"]}},{"name":"limit","in":"query","description":"Rows (1-100)","schema":{"type":"integer","default":20}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Posts","content":{"application/json":{"example":{"success":true,"data":[{"id":"8a7b6c5d-4e3f-4a2b-9c1d-0e9f8a7b6c5d","caption":"New season, new menu","platforms":{"facebook":true,"instagram":true},"status":"PUBLISHED","scheduled_at":"2026-09-29T08:00:00.000Z","published_at":"2026-09-29T08:00:05.000Z","platform_results":{"facebook":{"ok":true}},"created_at":"2026-09-28T12:00:00.000Z"}],"limit":20,"offset":0}}}},"400":{"description":"Bad request - STATUS_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:social, or the plan has no Social"}},"x-required-scope":"read:social"},"post":{"operationId":"social_post","summary":"Create Post","description":"Create a social media post. Without scheduled_at it is a DRAFT (published only from the app). With scheduled_at it is SCHEDULED and is published automatically at that time; if your business requires post approval it is IN_REVIEW instead and waits for approval in the app.\n\nRequired API key scope: `write:social`.","tags":["Social Media"],"requestBody":{"description":"Post content","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"caption":{"type":"string","description":"Post text/caption"},"platforms":{"type":"array","items":{},"description":"Target platforms"},"account_id":{"type":"string","description":"Social account ID (optional, uses default account)"},"scheduled_at":{"type":"string","description":"Schedule time (ISO 8601, optional)"}},"required":["caption","platforms"]}}}},"responses":{"201":{"description":"Post created as draft/scheduled (id + status)"}},"x-required-scope":"write:social"}},"/webhooks":{"get":{"operationId":"webhooks_list","summary":"List Webhooks","description":"Get all configured webhooks for your business.\n\nRequired API key scope: `read:webhooks`.","tags":["Webhooks"],"responses":{"200":{"description":"List of webhooks"}},"x-required-scope":"read:webhooks"},"post":{"operationId":"webhooks_create","summary":"Create Webhook","description":"Create a new webhook endpoint. The HMAC signing secret is returned once at creation — store it to verify the X-Lynxbe-Signature header on deliveries.\n\nRequired API key scope: `manage:webhooks`.","tags":["Webhooks"],"requestBody":{"description":"Webhook configuration","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"Webhook URL (HTTPS recommended)"},"events":{"type":"array","items":{},"description":"Event types to subscribe to (see GET /api/v1/webhook-events), or [\"*\"] for all"},"description":{"type":"string","description":"Free-text description"}},"required":["url","events"]}}}},"responses":{"201":{"description":"Webhook created (includes one-time signing secret)"}},"x-required-scope":"manage:webhooks"}},"/webhooks/{id}":{"patch":{"operationId":"webhooks_update","summary":"Update Webhook","description":"Update webhook URL, events, description, or enable/disable it.\n\nRequired API key scope: `manage:webhooks`.","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"description":"Webhook ID","schema":{"type":"string"}}],"requestBody":{"description":"Fields to update","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"Webhook URL"},"events":{"type":"array","items":{},"description":"Event types"},"description":{"type":"string","description":"Description"},"is_active":{"type":"boolean","description":"Enable/disable (re-enabling resets failure count)"}}}}}},"responses":{"200":{"description":"Webhook updated"},"404":{"description":"Webhook not found"}},"x-required-scope":"manage:webhooks"},"delete":{"operationId":"webhooks_delete","summary":"Delete Webhook","description":"Delete a webhook subscription permanently.\n\nRequired API key scope: `manage:webhooks`.","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"description":"Webhook ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Webhook deleted"},"404":{"description":"Webhook not found"}},"x-required-scope":"manage:webhooks"}},"/webhooks/{id}/test":{"post":{"operationId":"webhooks_test","summary":"Test Webhook","description":"Send a test event to a webhook.\n\nRequired API key scope: `manage:webhooks`.","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"description":"Webhook ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Test sent"}},"x-required-scope":"manage:webhooks"}},"/webhooks/{id}/logs":{"get":{"operationId":"webhooks_logs","summary":"Delivery Logs","description":"Get webhook delivery history with status codes and errors.\n\nRequired API key scope: `read:webhooks`.","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"description":"Webhook ID","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max results (default 50, max 200)","schema":{"type":"integer"}},{"name":"offset","in":"query","description":"Pagination offset","schema":{"type":"integer"}}],"responses":{"200":{"description":"Delivery log entries"}},"x-required-scope":"read:webhooks"}},"/webhook-events":{"get":{"operationId":"webhooks_events","summary":"List Event Types","description":"Get the catalog of all webhook event types you can subscribe to.","tags":["Webhooks"],"responses":{"200":{"description":"Map of event type → description"}}}},"/audit":{"get":{"operationId":"audit_list","summary":"List Audit Logs","description":"Get recent audit log entries for your business (admin actions with entity type and ID), newest first.\n\nRequired API key scope: `read:audit`.","tags":["Audit Log"],"parameters":[{"name":"limit","in":"query","description":"Max results (default 20, max 100)","schema":{"type":"integer"}}],"responses":{"200":{"description":"Audit log entries"}},"x-required-scope":"read:audit"}},"/audit/export":{"get":{"operationId":"audit_export","summary":"Export Audit Logs","description":"Export audit logs to CSV (default) or JSON, capped at 10,000 rows. Filter with from/to date query params.\n\nRequired API key scope: `read:audit`.","tags":["Audit Log"],"parameters":[{"name":"format","in":"query","description":"Export format","schema":{"type":"string","enum":["csv","json"],"default":"csv"}},{"name":"from","in":"query","description":"Only entries created at or after this time (ISO 8601)","schema":{"type":"string","example":"2026-10-01"}},{"name":"to","in":"query","description":"Only entries created at or before this time (ISO 8601)","schema":{"type":"string","example":"2026-10-31"}}],"responses":{"200":{"description":"The audit entries as a CSV file (format=csv) or as JSON { success, data } (format=json)"}},"x-required-scope":"read:audit"}},"/users":{"get":{"operationId":"users_list","summary":"List Users","description":"Get all admin users assigned to the business, with name, email, role, and active status.\n\nRequired API key scope: `read:team`.","tags":["Users & Teams"],"responses":{"200":{"description":"List of users"}},"x-required-scope":"read:team"}},"/teams":{"get":{"operationId":"teams_list","summary":"List Teams","description":"Get all teams for the business.\n\nRequired API key scope: `read:team`.","tags":["Users & Teams"],"responses":{"200":{"description":"List of teams"}},"x-required-scope":"read:team"}},"/users/invite":{"post":{"operationId":"users_invite","summary":"Invite User","description":"Invite a new user to the business. Only the restricted roles employee, support, and viewer are allowed (default employee) — any other role is rejected with 403. Creates the user with a 7-day invite token and returns/sends the invite link. Duplicate email returns 409.\n\nRequired API key scope: `manage:users`.","tags":["Users & Teams"],"requestBody":{"description":"Invite details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","description":"Email address"},"name":{"type":"string","description":"Full name"},"role":{"type":"string","description":"User role (restricted allow-list; default employee)","enum":["employee","support","viewer"]}},"required":["email","name"]}}}},"responses":{"201":{"description":"Invite created (invite token valid for 7 days)"},"403":{"description":"Role not allowed via the public API"},"409":{"description":"A user with this email already exists"}},"x-required-scope":"manage:users"}},"/compliance/consents/{contactId}":{"get":{"operationId":"compliance_consent","summary":"Get Consent Status","description":"Get the latest consent status per consent type for a contact.\n\nRequired API key scope: `read:compliance`.","tags":["Compliance"],"parameters":[{"name":"contactId","in":"path","required":true,"description":"Contact ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Latest consent record per consent type"}},"x-required-scope":"read:compliance"}},"/compliance/data-requests":{"post":{"operationId":"compliance_deletion","summary":"Submit Data Request","description":"Submit a GDPR data deletion or export request for a contact. Identify the contact by ID, email, or phone.\n\nRequired API key scope: `write:compliance`.","tags":["Compliance"],"requestBody":{"description":"Data request","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"contact_id":{"type":"string","description":"Contact ID (optional if email/phone given)"},"contact_email":{"type":"string","description":"Contact email (optional)"},"contact_phone":{"type":"string","description":"Contact phone (optional)"},"request_type":{"type":"string","description":"Type of request (default deletion)","enum":["deletion","export"]},"notes":{"type":"string","description":"Reason / notes"}}}}}},"responses":{"202":{"description":"Data request submitted"}},"x-required-scope":"write:compliance"}},"/compliance/retention-policies":{"get":{"operationId":"compliance_retention","summary":"Get Retention Policies","description":"Get data retention policies per data type (retention days, auto-delete, delete method).\n\nRequired API key scope: `read:compliance`.","tags":["Compliance"],"responses":{"200":{"description":"Retention policies"}},"x-required-scope":"read:compliance"}},"/me":{"get":{"operationId":"account_me","summary":"API Key Info","description":"Get information about the current API key, including scopes and rate limits.","tags":["Account"],"responses":{"200":{"description":"API key details including name, scopes, environment, and rate limits"}}}},"/capabilities":{"get":{"operationId":"account_capabilities","summary":"Capability Manifest","description":"Self-describing manifest of everything this key can do: live endpoints WITH their request-body field schemas, granted scopes, webhook events, the CRM column-type vocabulary (types + value formats + status/select config), and the entity model (customer 360 vs contact vs CRM card). Filtered to your key's scopes. Fetch this to discover the platform instead of hardcoding paths.","tags":["Account"],"responses":{"200":{"description":"Capability manifest scoped to the API key"}}}},"/branding/pages":{"get":{"operationId":"branding_pages_list","summary":"List Pages (Public API)","description":"List branding pages for your business via the Public API, with slug, title, status, homepage flag, and sort order.\n\nRequired API key scope: `read:branding`.","tags":["Branding Pages"],"responses":{"200":{"description":"Array of pages with status and metadata"}},"x-required-scope":"read:branding"},"post":{"operationId":"branding_page_create","summary":"Create Page (Public API)","description":"Create a new branding page in draft status via the Public API. Slug must be lowercase letters, digits, and hyphens, and unique per business (409 on conflict).\n\nRequired API key scope: `write:branding`.","tags":["Branding Pages"],"requestBody":{"description":"Page data","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Page title"},"slug":{"type":"string","description":"URL slug (^[a-z0-9-]+$, unique per business)"},"draft_blocks":{"type":"object","additionalProperties":true,"description":"Block-based content JSON"},"is_homepage":{"type":"boolean","description":"Set as homepage"}},"required":["title","slug"]}}}},"responses":{"201":{"description":"Created page (draft)"},"409":{"description":"Slug already exists for this business"}},"x-required-scope":"write:branding"}},"/branding/pages/{pageId}/publish":{"put":{"operationId":"branding_page_publish","summary":"Publish Page (Public API)","description":"Publish a page via the Public API: snapshots draft blocks to published blocks and sets status to published. Fires the page.published webhook.\n\nRequired API key scope: `write:branding`.","tags":["Branding Pages"],"parameters":[{"name":"pageId","in":"path","required":true,"description":"Page UUID","schema":{"type":"string"}}],"responses":{"200":{"description":"Published page"},"404":{"description":"Page not found"}},"x-required-scope":"write:branding"}},"/dialer/campaigns":{"get":{"operationId":"dialer_campaigns_list","summary":"List Campaigns","description":"Get dialer campaigns with mode and status.\n\nRequired API key scope: `read:dialer`.","tags":["Dialer"],"parameters":[{"name":"limit","in":"query","description":"Max results (default 20, max 100)","schema":{"type":"integer"}},{"name":"offset","in":"query","description":"Pagination offset","schema":{"type":"integer"}}],"responses":{"200":{"description":"List of dialer campaigns"}},"x-required-scope":"read:dialer"},"post":{"operationId":"dialer_campaign_create","summary":"Create Campaign","description":"Create a new dialer campaign in draft status. Fires the dialer.campaign_created webhook.\n\nRequired API key scope: `write:dialer`.","tags":["Dialer"],"requestBody":{"description":"Campaign details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Campaign name"},"dialer_mode":{"type":"string","description":"Dialer mode (optional, uses default)"},"default_caller_id":{"type":"string","description":"Default caller ID for outbound calls"}},"required":["name"]}}}},"responses":{"201":{"description":"Campaign created (status: draft)"}},"x-required-scope":"write:dialer"}},"/dialer/campaigns/{id}/leads":{"post":{"operationId":"dialer_leads_add","summary":"Add Leads","description":"Add leads to a dialer campaign (max 500 per request). Returns the number of leads inserted.\n\nRequired API key scope: `write:dialer`.","tags":["Dialer"],"parameters":[{"name":"id","in":"path","required":true,"description":"Campaign ID (UUID)","schema":{"type":"string"}}],"requestBody":{"description":"Leads to add","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"leads":{"type":"array","items":{},"description":"Array of { phone (required), name?, email?, priority? } (max 500)"}},"required":["leads"]}}}},"responses":{"201":{"description":"Leads added (inserted count)"},"400":{"description":"More than 500 leads or missing phone"},"404":{"description":"Campaign not found"}},"x-required-scope":"write:dialer"}},"/dialer/campaigns/{id}/analytics":{"get":{"operationId":"dialer_campaign_stats","summary":"Campaign Analytics","description":"Get lead counts per status and average call duration for a dialer campaign.\n\nRequired API key scope: `read:dialer`.","tags":["Dialer"],"parameters":[{"name":"id","in":"path","required":true,"description":"Campaign ID (UUID)","schema":{"type":"string"}}],"responses":{"200":{"description":"Campaign statistics (leads by status, average call duration)"},"404":{"description":"Campaign not found"}},"x-required-scope":"read:dialer"}},"/checklists/templates":{"get":{"operationId":"checklists_templates_list","summary":"List Templates","description":"Get checklist templates for the business.\n\nRequired API key scope: `read:checklists`.","tags":["Checklists"],"responses":{"200":{"description":"List of checklist templates"}},"x-required-scope":"read:checklists"}},"/checklists/campaigns":{"get":{"operationId":"checklists_campaigns_list","summary":"List Campaigns","description":"Get checklist campaigns with status.\n\nRequired API key scope: `read:checklists`.","tags":["Checklists"],"responses":{"200":{"description":"List of checklist campaigns"}},"x-required-scope":"read:checklists"}},"/checklists/campaigns/{id}/clients":{"get":{"operationId":"checklists_campaign_status","summary":"Campaign Client Progress","description":"Get per-client progress for a checklist campaign (status, progress percent, completed vs total items).\n\nRequired API key scope: `read:checklists`.","tags":["Checklists"],"parameters":[{"name":"id","in":"path","required":true,"description":"Campaign ID (UUID)","schema":{"type":"string"}}],"responses":{"200":{"description":"Client progress list"},"404":{"description":"Campaign not found"}},"x-required-scope":"read:checklists"}},"/meeting-polls/{id}":{"get":{"operationId":"meeting_poll_get","summary":"Get Meeting Poll","description":"One poll with its slots, a yes/maybe/no tally per slot, and its participants (name, contact, whether they responded). Participants' personal voting links are never returned.\n\nRequired API key scope: `read:meetings`.","tags":["Meeting Polls"],"parameters":[{"name":"id","in":"path","required":true,"description":"Poll uuid","schema":{"type":"string"}}],"responses":{"200":{"description":"Poll","content":{"application/json":{"example":{"success":true,"data":{"id":"5b4a3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d","title":"Quarterly review","description":null,"location_type":"zoom","duration_minutes":60,"timezone":"Asia/Jerusalem","status":"open","final_slot_id":null,"confirmed_at":null,"response_deadline":"2026-10-05T21:00:00.000Z","created_at":"2026-09-29T08:00:00.000Z","updated_at":"2026-09-29T08:00:00.000Z","public_link":"https://app.lynxbe.co.il/meeting/abc123","slots":[{"id":"...","slot_start":"2026-10-06T07:00:00.000Z","slot_end":"2026-10-06T08:00:00.000Z","is_final":false,"votes":{"yes":3,"maybe":1,"no":0}}],"participants":[{"id":"...","name":"Dana","email":"dana@example.com","phone":null,"role":"attendee","status":"responded","responded_at":"2026-09-29T10:00:00.000Z"}]}}}}},"400":{"description":"Bad request - POLL_ID_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:meetings, or the plan has no Meeting Polls"},"404":{"description":"POLL_NOT_FOUND - no such poll in this business"}},"x-required-scope":"read:meetings"}},"/meeting-polls":{"get":{"operationId":"meeting_polls_list","summary":"List Polls","description":"Get meeting availability polls with status and public token.\n\nRequired API key scope: `read:meetings`.","tags":["Meeting Polls"],"parameters":[{"name":"limit","in":"query","description":"Max results (default 20, max 100)","schema":{"type":"integer"}},{"name":"offset","in":"query","description":"Pagination offset","schema":{"type":"integer"}}],"responses":{"200":{"description":"List of meeting polls"}},"x-required-scope":"read:meetings"},"post":{"operationId":"meeting_poll_create","summary":"Create Poll","description":"Create a meeting availability poll. Returns the poll with its public voting link (/meeting/<token>). Fires the meeting_poll.created webhook.\n\nRequired API key scope: `write:meetings`.","tags":["Meeting Polls"],"requestBody":{"description":"Poll details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Poll title"},"description":{"type":"string","description":"Poll description"},"duration_minutes":{"type":"integer","description":"Meeting duration in minutes"},"slots":{"type":"array","items":{},"description":"Proposed time slots"}},"required":["title"]}}}},"responses":{"201":{"description":"Poll created with public voting link"}},"x-required-scope":"write:meetings"}},"/zoom/transcripts":{"get":{"operationId":"zoom_transcripts_list","summary":"List Zoom Meeting Transcripts","description":"Recorded Zoom meetings of the business, newest first, without the full text. With q, only meetings whose transcript, topic or participants match, each with the passages around the match.\n\nRequired API key scope: `read:meeting_transcripts`.","tags":["Meeting Polls"],"parameters":[{"name":"q","in":"query","description":"Words or a name to find in the transcript, topic or participants","schema":{"type":"string"}},{"name":"participant","in":"query","description":"Match the participant names only","schema":{"type":"string"}},{"name":"date_from","in":"query","description":"Meetings that started on or after this day (YYYY-MM-DD, Israel time) or instant","schema":{"type":"string"}},{"name":"date_to","in":"query","description":"Meetings that started on or before the end of this day (YYYY-MM-DD, Israel time) or instant","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max results (default 50, max 200)","schema":{"type":"integer"}},{"name":"offset","in":"query","description":"Pagination offset","schema":{"type":"integer"}}],"responses":{"200":{"description":"Meetings","content":{"application/json":{"example":{"success":true,"total":1,"data":[{"id":"8f0c...","zoom_meeting_id":"81234567890","topic":"Project review","started_at":"2026-08-02T16:00:00.000Z","duration_minutes":40,"participants":"Dana, Yossi","source":"asr","language":"he","chars":18500,"matches":["…we agreed on the timeline…"]}]}}}},"400":{"description":"TRANSCRIPT_QUERY_INVALID - a date is not a date"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:meeting_transcripts, or the plan has no Meeting Scheduler"}},"x-required-scope":"read:meeting_transcripts"}},"/zoom/transcripts/{id}":{"get":{"operationId":"zoom_transcript_get","summary":"Get Zoom Meeting Transcript","description":"One recorded Zoom meeting with its full transcript (machine-made; up to 150,000 characters). The id is the transcript id or the Zoom meeting id.\n\nRequired API key scope: `read:meeting_transcripts`.","tags":["Meeting Polls"],"parameters":[{"name":"id","in":"path","required":true,"description":"Transcript id (uuid) or Zoom meeting id","schema":{"type":"string"}}],"responses":{"200":{"description":"Meeting with full_text"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:meeting_transcripts, or the plan has no Meeting Scheduler"},"404":{"description":"TRANSCRIPT_NOT_FOUND - no such transcript in this business"}},"x-required-scope":"read:meeting_transcripts"}},"/broadcasts":{"get":{"operationId":"broadcasts_list","summary":"List Broadcasts","description":"Get broadcast jobs with recipient totals and funnel counters. Counters are cumulative stages: sent_count (reached WhatsApp) >= delivered_count >= read_count; pending_count + sent_count + failed_count + skipped_count = total_recipients. Before 2026-09-30 sent_count meant \"sent but not yet delivered\".\n\nRequired API key scope: `read:broadcasts`.","tags":["Broadcast Campaigns"],"parameters":[{"name":"limit","in":"query","description":"Max results (default 20, max 100)","schema":{"type":"integer"}},{"name":"offset","in":"query","description":"Pagination offset","schema":{"type":"integer"}}],"responses":{"200":{"description":"List of broadcast jobs"}},"x-required-scope":"read:broadcasts"}},"/broadcasts/{id}":{"get":{"operationId":"broadcast_status","summary":"Broadcast Status","description":"Get a single broadcast job. The *_count fields are cumulative funnel stages (sent >= delivered >= read, plus pending_count); recipients_by_status is the disjoint latest status of each recipient.\n\nRequired API key scope: `read:broadcasts`.","tags":["Broadcast Campaigns"],"parameters":[{"name":"id","in":"path","required":true,"description":"Broadcast job ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Broadcast job with recipient status breakdown"},"404":{"description":"Broadcast not found"}},"x-required-scope":"read:broadcasts"}},"/agents/installations":{"get":{"operationId":"ai_agents_installations","summary":"List installed agents","description":"The business's installed AI agents with status and config KEYS only (values may hold recipients or tokens).\n\nRequired API key scope: `agents:read`.","tags":["AI Agents"],"responses":{"200":{"description":"Installed agents","content":{"application/json":{"example":{"success":true,"data":{"installations":[{"id":"1f162ea1-...","agent_key":"morning_brief","status":"active","config_keys":["locale"]}]}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks the agents:read scope"}},"x-required-scope":"agents:read"},"post":{"operationId":"ai_agents_install","summary":"Install an agent","description":"Install one agent from the catalog for this business, or re-activate and reconfigure it if already installed. Required config fields are validated against the catalog entry.\n\nRequired API key scope: `agents:manage`.","tags":["AI Agents"],"requestBody":{"description":"The agent and its config","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_key":{"type":"string","description":"Agent key from the catalog","example":"marketing_ai_visibility_agent"},"config":{"type":"object","additionalProperties":true,"description":"Config values keyed by the catalog config_fields","example":{"language":"he","shadow_mode":true}}},"required":["agent_key"]}}}},"responses":{"201":{"description":"Installed","content":{"application/json":{"example":{"success":true,"data":{"installation":{"id":"...","agent_key":"marketing_ai_visibility_agent","status":"active","config_keys":["language","shadow_mode"]}}}}}},"403":{"description":"Forbidden - key lacks the agents:manage scope"},"404":{"description":"Unknown agent (or not available to this business)"},"422":{"description":"A required config field is missing"}},"x-required-scope":"agents:manage"}},"/agents/approvals":{"get":{"operationId":"ai_agents_approvals","summary":"List approvals","description":"The AI-agent approvals queue by status (pending by default; approved / rejected / expired), newest first, with the proposal a human reads. Read-only: deciding stays in the app.\n\nRequired API key scope: `agents:read`.","tags":["AI Agents"],"parameters":[{"name":"status","in":"query","description":"Queue status","schema":{"type":"string","enum":["pending","approved","rejected","expired"],"default":"pending"}},{"name":"limit","in":"query","description":"Max rows (1-50)","schema":{"type":"integer","default":20,"example":20}}],"responses":{"200":{"description":"Approvals","content":{"application/json":{"example":{"success":true,"data":{"status":"pending","approvals":[{"id":"...","agent_key":"marketing_seo_blog_agent","action_key":"propose_blog_draft","proposal":{"title":"..."}}]}}}}},"400":{"description":"Bad request - unknown status"},"401":{"description":"Unauthorized - Invalid or missing API key"}},"x-required-scope":"agents:read"}},"/agents/installations/{id}/run":{"post":{"operationId":"ai_agents_run","summary":"Trigger an agent run","description":"Run one installed agent now, as an api trigger: the installation's rate_limit_per_day applies and the run is audited with the key id. Approval-gated actions land in the approvals queue, never execute directly. A run skipped for depleted AI credits answers 402 INSUFFICIENT_CREDITS.\n\nRequired API key scope: `agents:run`.","tags":["AI Agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"Installation id (UUID) from List installed agents","schema":{"type":"string","example":"1f162ea1-b8d0-47a0-89f0-16ab727b0685"}}],"requestBody":{"description":"Run options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"dry_run":{"type":"boolean","description":"Skip deliveries, approvals and cursor advances. The model call still runs and is metered against AI credits.","example":false}}}}}},"responses":{"200":{"description":"Run result","content":{"application/json":{"example":{"success":true,"data":{"run_id":"...","status":"needs_approval","agent_key":"marketing_seo_blog_agent","dry_run":false,"summary":"..."}}}}},"400":{"description":"Bad request - id is not a UUID"},"402":{"description":"AI credits depleted (INSUFFICIENT_CREDITS)"},"403":{"description":"Forbidden - key lacks the agents:run scope"},"404":{"description":"Not found - no such installation for this business"}},"x-required-scope":"agents:run"}},"/agents/catalog":{"get":{"operationId":"ai_agents_catalog","summary":"Agent catalog","description":"Every agent in the roster an integrator can install: identity, category, triggers, declared actions (with their approval posture) and config field keys. Never the prompts.\n\nRequired API key scope: `agents:read`.","tags":["AI Agents"],"responses":{"200":{"description":"The roster","content":{"application/json":{"example":{"success":true,"data":{"agents":[{"key":"marketing_seo_blog_agent","category":"marketing","actions":[{"key":"propose_blog_draft","approval":true}]}]}}}}},"401":{"description":"Unauthorized - Invalid or missing API key"}},"x-required-scope":"agents:read"}},"/agents/digest":{"get":{"operationId":"ai_agents_digest","summary":"Read a digest","description":"The latest weekly (engagement: daily) digest of one type for this business - narrative, wins, misses, next-period plan and the cross-channel aggregates. The platform output policy is applied to every model-written text.\n\nRequired API key scope: `agents:read`.","tags":["AI Agents"],"parameters":[{"name":"type","in":"query","description":"Digest type","schema":{"type":"string","enum":["customer","revenue","engagement","market"],"default":"customer"}}],"responses":{"200":{"description":"The digest (digest: null when none was generated yet)","content":{"application/json":{"example":{"success":true,"data":{"digest_type":"customer","digest":{"period":{"type":"weekly","iso_period_key":"2026-W36"},"narrative":"...","top_wins":[],"top_misses":[]}}}}}},"400":{"description":"Unknown digest type"},"401":{"description":"Unauthorized - Invalid or missing API key"}},"x-required-scope":"agents:read"}},"/agents/approvals/{id}/decide":{"post":{"operationId":"ai_agents_decide","summary":"Decide an approval","description":"Approve or reject one pending approval of this business. An approved action executes through the same runtime path as the app; both outcomes fire the ai_agent.approval.decided webhook.\n\nRequired API key scope: `agents:approve`.","tags":["AI Agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"Approval id (UUID) from List approvals","schema":{"type":"string"}}],"requestBody":{"description":"The decision","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"decision":{"type":"string","description":"approved or rejected","enum":["approved","rejected"],"example":"approved"},"note":{"type":"string","description":"Optional decision note (≤500 chars)"}},"required":["decision"]}}}},"responses":{"200":{"description":"Decided","content":{"application/json":{"example":{"success":true,"data":{"approval_id":"...","status":"approved","executed":true,"detail":"..."}}}}},"403":{"description":"Forbidden - key lacks the agents:approve scope"},"404":{"description":"Not found - no such approval for this business"},"409":{"description":"Conflict - the approval is no longer pending"},"422":{"description":"decision must be approved or rejected"}},"x-required-scope":"agents:approve"}},"/agents/installations/{id}":{"patch":{"operationId":"ai_agents_update","summary":"Update an installation","description":"Reconfigure, pause or resume one installation, or change its daily run cap (1-500).\n\nRequired API key scope: `agents:manage`.","tags":["AI Agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"Installation id (UUID)","schema":{"type":"string"}}],"requestBody":{"description":"Fields to change (any subset)","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"config":{"type":"object","additionalProperties":true,"description":"Replacement config (validated)"},"status":{"type":"string","description":"active or paused","enum":["active","paused"]},"rate_limit_per_day":{"type":"integer","description":"Daily run cap for scheduled/api runs (1-500)","example":24}}}}}},"responses":{"200":{"description":"Updated"},"404":{"description":"Not found - no such installation for this business"},"422":{"description":"Invalid config, status or cap"}},"x-required-scope":"agents:manage"},"delete":{"operationId":"ai_agents_uninstall","summary":"Uninstall an agent","description":"Remove one installation. Its past runs and approvals remain for history.\n\nRequired API key scope: `agents:manage`.","tags":["AI Agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"Installation id (UUID)","schema":{"type":"string"}}],"responses":{"200":{"description":"Uninstalled","content":{"application/json":{"example":{"success":true,"data":{"uninstalled":true,"id":"...","agent_key":"..."}}}}},"404":{"description":"Not found - no such installation for this business"}},"x-required-scope":"agents:manage"}},"/documents":{"get":{"operationId":"documents_list","summary":"List Documents","description":"Newest first. Filter by type, employee, or documents expiring within N days.\n\nRequired API key scope: `read:documents`.","tags":["Documents"],"parameters":[{"name":"document_type","in":"query","description":"Document type (e.g. id_card, contract, certificate)","schema":{"type":"string"}},{"name":"employee_id","in":"query","description":"User id or employee uuid","schema":{"type":"string"}},{"name":"expiring_within_days","in":"query","description":"Only documents expiring between today and N days from now (0-365)","schema":{"type":"integer","example":30}},{"name":"limit","in":"query","description":"Max rows (1-100)","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Documents","content":{"application/json":{"example":{"success":true,"data":[{"id":"7b1f0c3e-2a4d-4c8e-9f10-5d6e7a8b9c01","employee_id":"142","employee_name":"Dana Levi","department_name":"Sales","document_type":"id_card","title":"Teudat Zehut","description":null,"file_name":"id.pdf","file_size":182340,"mime_type":"application/pdf","expiry_date":"2027-03-01","upload_date":"2026-09-01T08:12:00.000Z","scan_status":"clean"}],"limit":50,"offset":0}}}},"400":{"description":"Bad request - DOCUMENT_TYPE_INVALID, EMPLOYEE_ID_INVALID or EXPIRY_WINDOW_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:documents, or the plan has no Workforce module"}},"x-required-scope":"read:documents"}},"/documents/{id}":{"get":{"operationId":"documents_get","summary":"Get Document","description":"One document by id.\n\nRequired API key scope: `read:documents`.","tags":["Documents"],"parameters":[{"name":"id","in":"path","required":true,"description":"Document uuid","schema":{"type":"string"}}],"responses":{"200":{"description":"Document","content":{"application/json":{"example":{"success":true,"data":{"id":"7b1f0c3e-2a4d-4c8e-9f10-5d6e7a8b9c01","employee_id":"142","employee_name":"Dana Levi","department_name":"Sales","document_type":"id_card","title":"Teudat Zehut","description":null,"file_name":"id.pdf","file_size":182340,"mime_type":"application/pdf","expiry_date":"2027-03-01","upload_date":"2026-09-01T08:12:00.000Z","scan_status":"clean"}}}}},"400":{"description":"Bad request - DOCUMENT_ID_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:documents, or the plan has no Workforce module"},"404":{"description":"DOCUMENT_NOT_FOUND - no such document in this business"}},"x-required-scope":"read:documents"}},"/expenses":{"get":{"operationId":"expenses_list","summary":"List Expenses","description":"Newest expense date first, paged.\n\nRequired API key scope: `read:expenses`.","tags":["Expenses"],"parameters":[{"name":"date_from","in":"query","description":"From expense date (YYYY-MM-DD)","schema":{"type":"string","example":"2026-09-01"}},{"name":"date_to","in":"query","description":"To expense date (YYYY-MM-DD)","schema":{"type":"string","example":"2026-09-30"}},{"name":"status","in":"query","description":"Expense status","schema":{"type":"string","enum":["draft","pending_approval","approved","active","reimbursed","rejected","voided"]}},{"name":"supplier_id","in":"query","description":"Supplier uuid","schema":{"type":"string"}},{"name":"category_id","in":"query","description":"Expense category uuid","schema":{"type":"string"}},{"name":"search","in":"query","description":"Text in description, reference, category or supplier (max 100)","schema":{"type":"string"}},{"name":"page","in":"query","description":"Page number","schema":{"type":"integer","default":1}},{"name":"limit","in":"query","description":"Rows per page (1-100)","schema":{"type":"integer","default":20}}],"responses":{"200":{"description":"Expenses","content":{"application/json":{"example":{"success":true,"data":[{"id":"c2d9e1a4-6b7f-4e21-9a3c-1f2e3d4c5b6a","expense_date":"2026-09-14","due_date":null,"description":"Office rent - September","amount":5850,"vat_amount":892.37,"amount_before_vat":4957.63,"currency":"ILS","status":"approved","category_id":"0b6f3a1e-2c4d-4e5f-8a9b-0c1d2e3f4a5b","category":"Rent","supplier_id":null,"supplier_name":null,"payment_method":"bank_transfer","reference_number":"INV-2231","document_type":"tax_invoice","document_number":"2231","is_tax_deductible":true,"vat_deductible":true,"is_billable":false,"customer_name":null,"project_name":null,"tags":[],"has_receipt":true,"paid_at":null,"created_at":"2026-09-14T09:02:11.000Z"}],"pagination":{"page":1,"limit":20,"total":1,"pages":1}}}}},"400":{"description":"Bad request - DATE_INVALID, ID_INVALID, STATUS_INVALID or SEARCH_TOO_LONG"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:expenses, or the plan has no Expense Management"}},"x-required-scope":"read:expenses"}},"/expenses/summary":{"get":{"operationId":"expenses_summary","summary":"Expense Summary","description":"Totals for the same filters as the list, by category and by payment method. Without a status filter, voided and rejected expenses are left out.\n\nRequired API key scope: `read:expenses`.","tags":["Expenses"],"parameters":[{"name":"date_from","in":"query","description":"From expense date (YYYY-MM-DD)","schema":{"type":"string","example":"2026-09-01"}},{"name":"date_to","in":"query","description":"To expense date (YYYY-MM-DD)","schema":{"type":"string","example":"2026-09-30"}},{"name":"status","in":"query","description":"Expense status","schema":{"type":"string","enum":["draft","pending_approval","approved","active","reimbursed","rejected","voided"]}},{"name":"supplier_id","in":"query","description":"Supplier uuid","schema":{"type":"string"}},{"name":"category_id","in":"query","description":"Expense category uuid","schema":{"type":"string"}},{"name":"search","in":"query","description":"Text in description, reference, category or supplier (max 100)","schema":{"type":"string"}}],"responses":{"200":{"description":"Totals","content":{"application/json":{"example":{"success":true,"data":{"total_amount":5850,"total_vat":892.37,"count":1,"by_category":[{"category":"Rent","total":5850,"count":1}],"by_payment_method":[{"payment_method":"bank_transfer","total":5850,"count":1}]}}}}},"400":{"description":"Bad request - DATE_INVALID, ID_INVALID, STATUS_INVALID or SEARCH_TOO_LONG"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:expenses, or the plan has no Expense Management"}},"x-required-scope":"read:expenses"}},"/expenses/{id}":{"get":{"operationId":"expenses_get","summary":"Get Expense","description":"One expense by id.\n\nRequired API key scope: `read:expenses`.","tags":["Expenses"],"parameters":[{"name":"id","in":"path","required":true,"description":"Expense uuid","schema":{"type":"string"}}],"responses":{"200":{"description":"Expense","content":{"application/json":{"example":{"success":true,"data":{"id":"c2d9e1a4-6b7f-4e21-9a3c-1f2e3d4c5b6a","expense_date":"2026-09-14","due_date":null,"description":"Office rent - September","amount":5850,"vat_amount":892.37,"amount_before_vat":4957.63,"currency":"ILS","status":"approved","category_id":"0b6f3a1e-2c4d-4e5f-8a9b-0c1d2e3f4a5b","category":"Rent","supplier_id":null,"supplier_name":null,"payment_method":"bank_transfer","reference_number":"INV-2231","document_type":"tax_invoice","document_number":"2231","is_tax_deductible":true,"vat_deductible":true,"is_billable":false,"customer_name":null,"project_name":null,"tags":[],"has_receipt":true,"paid_at":null,"created_at":"2026-09-14T09:02:11.000Z"}}}}},"400":{"description":"Bad request - EXPENSE_ID_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:expenses, or the plan has no Expense Management"},"404":{"description":"EXPENSE_NOT_FOUND - no such expense in this business"}},"x-required-scope":"read:expenses"}},"/commerce/orders":{"get":{"operationId":"commerce_orders_list","summary":"List B2B Orders","description":"Newest first, without lines.\n\nRequired API key scope: `read:commerce_orders`.","tags":["B2B Orders & Pricing"],"parameters":[{"name":"status","in":"query","description":"Order status","schema":{"type":"string","enum":["draft","submitted","pending_approval","approved","rejected","shipped","delivered","cancelled"]}},{"name":"date_from","in":"query","description":"Created on or after this UTC day (YYYY-MM-DD)","schema":{"type":"string"}},{"name":"date_to","in":"query","description":"Created on or before the end of this UTC day (YYYY-MM-DD)","schema":{"type":"string"}},{"name":"search","in":"query","description":"Text in PO number or notes (max 100)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Rows (1-100)","schema":{"type":"integer","default":20}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Orders","content":{"application/json":{"example":{"success":true,"data":[{"id":"5e0c2b7a-1d3f-4a6b-9c8d-7e6f5a4b3c2d","party_id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","party_name":"Cafe Hadar","source":"portal","status":"approved","subtotal":1200,"discount_total":60,"total":1140,"currency":"ILS","po_number":"PO-7781","delivery_date":"2026-10-02","notes":null,"invoice_id":null,"submitted_at":"2026-09-28T07:40:00.000Z","approved_at":"2026-09-28T09:05:00.000Z","shipped_at":null,"delivered_at":null,"created_at":"2026-09-28T07:35:00.000Z"}],"total":1,"limit":20,"offset":0}}}},"400":{"description":"Bad request - STATUS_INVALID, DATE_INVALID or SEARCH_TOO_LONG"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:commerce_orders, or the plan has no Commerce Orders"}},"x-required-scope":"read:commerce_orders"}},"/commerce/orders/{id}":{"get":{"operationId":"commerce_orders_get","summary":"Get B2B Order","description":"One order with its lines.\n\nRequired API key scope: `read:commerce_orders`.","tags":["B2B Orders & Pricing"],"parameters":[{"name":"id","in":"path","required":true,"description":"Order uuid","schema":{"type":"string"}}],"responses":{"200":{"description":"Order","content":{"application/json":{"example":{"success":true,"data":{"id":"5e0c2b7a-1d3f-4a6b-9c8d-7e6f5a4b3c2d","party_id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","party_name":"Cafe Hadar","source":"portal","status":"approved","subtotal":1200,"discount_total":60,"total":1140,"currency":"ILS","po_number":"PO-7781","delivery_date":"2026-10-02","notes":null,"invoice_id":null,"submitted_at":"2026-09-28T07:40:00.000Z","approved_at":"2026-09-28T09:05:00.000Z","shipped_at":null,"delivered_at":null,"created_at":"2026-09-28T07:35:00.000Z","lines":[{"id":"...","catalog_item_id":"...","item_name":"Coffee beans 1kg","item_sku":"CB-1","quantity":10,"unit":"kg","unit_price":120,"discount_percent":5,"line_total":1140}]}}}}},"400":{"description":"Bad request - ORDER_ID_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:commerce_orders, or the plan has no Commerce Orders"},"404":{"description":"ORDER_NOT_FOUND - no such order in this business"}},"x-required-scope":"read:commerce_orders"}},"/commerce/prices":{"get":{"operationId":"commerce_price_get","summary":"Get Customer Price","description":"The price of one catalog item for one customer, exactly as the app computes it: the price list for the customer type, else the default list, else the catalog price; then the list discount and the customer discount.\n\nRequired API key scope: `read:pricing`.","tags":["B2B Orders & Pricing"],"parameters":[{"name":"item_id","in":"query","required":true,"description":"Catalog item uuid","schema":{"type":"string"}},{"name":"party_id","in":"query","description":"Customer (party) uuid. Omit for the default price.","schema":{"type":"string"}}],"responses":{"200":{"description":"Price","content":{"application/json":{"example":{"success":true,"data":{"catalog_item_id":"3f9a2c1e-7b4d-4e8a-9c21-5d6e7f8a9b0c","party_id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","unit_price":120,"discount_percent":5,"customer_discount_percent":3,"effective_price":110.58,"currency":"ILS","price_list_id":"9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a","price_list_name":"Wholesale","min_quantity":5,"pack_size":1,"visibility":"visible"}}}}},"400":{"description":"Bad request - ITEM_ID_INVALID or PARTY_ID_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:pricing, or the plan has no Commerce Pricing"},"404":{"description":"CATALOG_ITEM_NOT_FOUND - no such active item in this business"}},"x-required-scope":"read:pricing"}},"/commerce/price-lists":{"get":{"operationId":"commerce_price_lists","summary":"List Price Lists","description":"Your price lists (deleted ones are never listed).\n\nRequired API key scope: `read:pricing`.","tags":["B2B Orders & Pricing"],"parameters":[{"name":"limit","in":"query","description":"Rows (1-100)","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Price lists","content":{"application/json":{"example":{"success":true,"data":[{"id":"9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a","name":"Wholesale","description":null,"customer_type":"wholesale","segment_id":null,"is_default":false,"valid_from":null,"valid_to":null,"is_active":true,"created_at":"2026-09-01T10:00:00.000Z","updated_at":"2026-09-01T10:00:00.000Z"}],"total":1,"limit":50,"offset":0}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:pricing, or the plan has no Commerce Pricing"}},"x-required-scope":"read:pricing"}},"/retail/sales":{"get":{"operationId":"retail_sales_list","summary":"List Sales","description":"Newest first. Refunds are sales with negative amounts that cite the original in refunded_sale_id.\n\nRequired API key scope: `read:retail_sales`.","tags":["Retail Register"],"parameters":[{"name":"date_from","in":"query","description":"From this UTC day (YYYY-MM-DD)","schema":{"type":"string"}},{"name":"date_to","in":"query","description":"To the end of this UTC day (YYYY-MM-DD)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Rows (1-100)","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Sales","content":{"application/json":{"example":{"success":true,"data":[{"id":"4c3b2a19-8f7e-4d6c-9b5a-3f2e1d0c9b8a","receipt_number":231,"session_id":"2b1a0f9e-8d7c-4b6a-9f5e-4d3c2b1a0f9e","customer_name":null,"subtotal":84.75,"discount_amount":0,"vat_amount":15.25,"total":100,"currency":"ILS","status":"completed","refunded_sale_id":null,"invoice_id":null,"sold_at":"2026-09-29T10:14:00.000Z"}],"limit":50,"offset":0}}}},"400":{"description":"Bad request - DATE_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:retail_sales, or the plan has no Retail Register"}},"x-required-scope":"read:retail_sales"}},"/retail/sales/{id}":{"get":{"operationId":"retail_sales_get","summary":"Get Sale","description":"One receipt with its items and tenders (tender type and amounts only).\n\nRequired API key scope: `read:retail_sales`.","tags":["Retail Register"],"parameters":[{"name":"id","in":"path","required":true,"description":"Sale uuid","schema":{"type":"string"}}],"responses":{"200":{"description":"Sale","content":{"application/json":{"example":{"success":true,"data":{"id":"4c3b2a19-8f7e-4d6c-9b5a-3f2e1d0c9b8a","receipt_number":231,"session_id":"2b1a0f9e-8d7c-4b6a-9f5e-4d3c2b1a0f9e","customer_name":null,"subtotal":84.75,"discount_amount":0,"vat_amount":15.25,"total":100,"currency":"ILS","status":"completed","refunded_sale_id":null,"invoice_id":null,"sold_at":"2026-09-29T10:14:00.000Z","items":[{"id":"...","name":"Notebook A5","quantity":2,"unit_price":50,"line_total":100}],"payments":[{"id":"...","tender_code":"cash","amount":100,"change_given":0,"created_at":"2026-09-29T10:14:00.000Z"}]}}}}},"400":{"description":"Bad request - SALE_ID_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:retail_sales, or the plan has no Retail Register"},"404":{"description":"SALE_NOT_FOUND - no such sale in this business"}},"x-required-scope":"read:retail_sales"}},"/retail/summary":{"get":{"operationId":"retail_summary","summary":"Sales Summary","description":"Count, total (net of refunds), VAT, refunds (negative) and the amount taken per tender at the counter (refunds do not reduce tenders). Without dates: today (UTC).\n\nRequired API key scope: `read:retail_sales`.","tags":["Retail Register"],"parameters":[{"name":"date_from","in":"query","description":"From this UTC day (YYYY-MM-DD)","schema":{"type":"string"}},{"name":"date_to","in":"query","description":"To the end of this UTC day (YYYY-MM-DD)","schema":{"type":"string"}}],"responses":{"200":{"description":"Summary","content":{"application/json":{"example":{"success":true,"data":{"date_from":"2026-09-29T00:00:00.000Z","date_to":null,"sales":12,"total":1840,"vat":280.68,"refunds":-60,"tenders":[{"tender_code":"card","amount":1500},{"tender_code":"cash","amount":340}]}}}}},"400":{"description":"Bad request - DATE_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:retail_sales, or the plan has no Retail Register"}},"x-required-scope":"read:retail_sales"}},"/customer-portal/sessions":{"post":{"operationId":"customer_portal_session","summary":"Create Portal Session","description":"Creates a session for one customer and returns its token and links. Send the card_update_url to the customer; the token is a credential, treat it like a password. Default lifetime 30 minutes.\n\nRequired API key scope: `write:customer_portal`.","tags":["Customer Portal"],"requestBody":{"description":"The customer and session options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"customer_id":{"type":"string","description":"Customer uuid (as returned by this endpoint). Or identify the customer by customer_email or customer_phone instead","example":"d4c3b2a1-9f8e-4d7c-8b6a-5f4e3d2c1b0a"},"customer_email":{"type":"string","description":"Customer email (exact, case-insensitive), when customer_id is not sent","example":"dana@example.com"},"customer_phone":{"type":"string","description":"Customer phone as stored, when neither customer_id nor customer_email is sent","example":"+972501234567"},"return_url":{"type":"string","description":"Absolute https URL to send the customer back to","example":"https://shop.example.com/account"},"expires_in_hours":{"type":"integer","description":"Lifetime in hours, 1-72 (default 30 minutes)","example":24}}}}}},"responses":{"201":{"description":"Session created","content":{"application/json":{"example":{"success":true,"data":{"id":"7e6d5c4b-3a29-4f18-8e07-d6c5b4a39281","customer":{"id":"d4c3b2a1-9f8e-4d7c-8b6a-5f4e3d2c1b0a","name":"Dana Levi"},"token":"9f2c...64 hex chars","expires_at":"2026-09-30T10:00:00.000Z","return_url":"https://shop.example.com/account","card_update_url":"https://app.lynxbe.co.il/pay/card-update/9f2c...","portal_api_url":"https://api.lynxbe.co.il/api/pay/portal/9f2c..."}}}}},"400":{"description":"Bad request - CUSTOMER_REQUIRED, CUSTOMER_ID_INVALID, CUSTOMER_LOOKUP_INVALID, RETURN_URL_INVALID or EXPIRY_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks write:customer_portal, or MODULE_LOCKED: the plan has no Customer Portal"},"404":{"description":"CUSTOMER_NOT_FOUND - no such customer in this business"},"409":{"description":"CUSTOMER_AMBIGUOUS - more than one customer has that email/phone; send customer_id"}},"x-required-scope":"write:customer_portal"}},"/team-chat/channels":{"get":{"operationId":"team_chat_channels","summary":"List Channels","description":"Open (not archived) channels, by name. Direct and group messages are not listed.\n\nRequired API key scope: `read:team_chat`.","tags":["Team Chat"],"responses":{"200":{"description":"Channels","content":{"application/json":{"example":{"success":true,"data":[{"id":"2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f","name":"general","description":null,"created_at":"2026-05-01T09:00:00.000Z"}]}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:team_chat, or the plan has no Team Chat"}},"x-required-scope":"read:team_chat"}},"/team-chat/channels/{id}/messages":{"post":{"operationId":"team_chat_post","summary":"Post Bot Message","description":"Posts a message into a channel as the Lynxbe Bot. Channel members see it live.\n\nRequired API key scope: `write:team_chat`.","tags":["Team Chat"],"parameters":[{"name":"id","in":"path","required":true,"description":"Channel uuid","schema":{"type":"string"}}],"requestBody":{"description":"The message","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"Message text (max 4000)","example":"New order #1042 is waiting for approval"}},"required":["text"]}}}},"responses":{"201":{"description":"Posted","content":{"application/json":{"example":{"success":true,"data":{"id":"9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a","channel_id":"2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f","created_at":"2026-09-29T11:00:00.000Z"}}}}},"400":{"description":"Bad request - CHANNEL_ID_INVALID, TEXT_REQUIRED or TEXT_TOO_LONG"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks write:team_chat, or the plan has no Team Chat"},"404":{"description":"CHANNEL_NOT_FOUND - not an open channel of this business (direct messages are never accepted)"}},"x-required-scope":"write:team_chat"}},"/marketplace/installations":{"get":{"operationId":"marketplace_installations","summary":"List Installed Apps","description":"Newest first. status is active, pending_auth, error or disabled.\n\nRequired API key scope: `read:marketplace`.","tags":["App Marketplace"],"responses":{"200":{"description":"Installed apps","content":{"application/json":{"example":{"success":true,"data":[{"id":42,"status":"active","scopes_granted":["read:contacts"],"last_sync_at":"2026-09-29T09:00:00.000Z","installed_at":"2026-09-01T12:00:00.000Z","app":{"slug":"google-sheets","name":"Google Sheets","vendor":"Google","category":"productivity","icon_url":"https://..."}}]}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:marketplace, or the plan has no App Marketplace"}},"x-required-scope":"read:marketplace"}},"/gbp/reviews":{"get":{"operationId":"gbp_reviews","summary":"List Reviews","description":"Newest first. Filter by minimum star rating or by whether you replied.\n\nRequired API key scope: `read:reviews`.","tags":["Google Business Profile"],"parameters":[{"name":"min_rating","in":"query","description":"Only reviews with at least this many stars (1-5)","schema":{"type":"integer","example":4}},{"name":"replied","in":"query","description":"true = only replied, false = only unanswered","schema":{"type":"string","enum":["true","false"]}},{"name":"limit","in":"query","description":"Rows (1-100)","schema":{"type":"integer","default":20}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Reviews","content":{"application/json":{"example":{"success":true,"data":[{"id":311,"location_id":4,"reviewer_name":"Noa","reviewer_photo_url":null,"star_rating":5,"review_text":"Great service","review_create_time":"2026-09-20T10:00:00.000Z","reply_text":"Thank you!","reply_time":"2026-09-20T12:00:00.000Z"}],"limit":20,"offset":0}}}},"400":{"description":"Bad request - MIN_RATING_INVALID or REPLIED_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:reviews, or the plan has no Google Business Profile"}},"x-required-scope":"read:reviews"}},"/ads/campaigns":{"get":{"operationId":"ads_campaigns","summary":"List Ad Campaigns","description":"Campaigns across your ad platforms, most recently updated first (up to 200 per platform). Facebook and TikTok stats are summed from daily data over the last `days` days; Google stats are the campaign totals as last synced - each row says which in stats_window. ctr is a percentage and cpc is spend per click, computed the same way for every platform. platforms lists which ones your plan includes.\n\nRequired API key scope: `read:ads`.","tags":["Ads"],"parameters":[{"name":"platform","in":"query","description":"Only this platform","schema":{"type":"string","enum":["facebook","tiktok","google"]}},{"name":"days","in":"query","description":"Window for Facebook/TikTok stats, 1-365","schema":{"type":"integer","default":30}}],"responses":{"200":{"description":"Campaigns","content":{"application/json":{"example":{"success":true,"data":[{"platform":"facebook","id":"a46a4843-f3d0-459b-9c9b-2e4681aafaec","external_id":"120253406104490612","name":"Autumn sale | Conversions","status":"ACTIVE","objective":"OUTCOME_SALES","daily_budget":150,"lifetime_budget":null,"spend":1240.5,"impressions":84211,"clicks":1320,"conversions":41,"currency":"ILS","synced_at":"2026-09-28T00:00:00.000Z","ctr":1.57,"cpc":0.94,"stats_window":"last_30_days"}],"platforms":["facebook","tiktok","google"],"days":30}}}},"400":{"description":"Bad request - PLATFORM_INVALID or DAYS_INVALID"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:ads, or MODULE_LOCKED: none of the requested platforms is on your plan"}},"x-required-scope":"read:ads"}},"/business-domains":{"get":{"operationId":"business_domains","summary":"List Domains","description":"Your domains, newest first. Watch expires_at and cert_status to catch a domain or certificate about to lapse.\n\nRequired API key scope: `read:domains`.","tags":["Custom Domains"],"responses":{"200":{"description":"Domains","content":{"application/json":{"example":{"success":true,"data":[{"id":"57228f77-31d2-4603-8321-cba664d182b6","domain":"my-shop.co.il","tld":"co.il","registration_status":"active","registered_at":"2026-06-01T09:00:00.000Z","expires_at":"2027-06-01T09:00:00.000Z","auto_renew":true,"cert_status":"active","source":"purchase","created_at":"2026-06-01T08:55:00.000Z","updated_at":"2026-09-20T03:00:00.000Z"}]}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:domains"}},"x-required-scope":"read:domains"}},"/app-clips/links":{"get":{"operationId":"app_clip_links","summary":"List App Clip Links","description":"Your App Clip links, newest first, with session_count and conversion_count (sessions that completed). The link target token is never returned.\n\nRequired API key scope: `read:app_clips`.","tags":["App Clips"],"parameters":[{"name":"experience_type","in":"query","description":"Only links of this experience","schema":{"type":"string","enum":["booking","payment","contract","proposal","storefront","crm_form"]}},{"name":"is_active","in":"query","description":"Only active (true) or inactive (false) links","schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Page size (1-200)","schema":{"type":"integer","default":50,"example":50}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0,"example":0}}],"responses":{"200":{"description":"App Clip links","content":{"application/json":{"example":{"success":true,"data":[{"id":"0b7c2f1e-5a4d-4c8e-9f21-6d3a8e1b4c70","experience_type":"booking","slug":"spring-booking","title":"Spring booking QR","campaign":"spring-2026","is_active":true,"expires_at":null,"max_uses":null,"current_uses":15,"created_at":"2026-09-01T08:00:00.000Z","updated_at":"2026-09-10T12:30:00.000Z","session_count":42,"conversion_count":11}],"total":1,"limit":50,"offset":0}}}},"400":{"description":"Invalid experience_type, is_active, limit or offset"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:app_clips, or the App Clips module is not in your plan (code MODULE_LOCKED)"}},"x-required-scope":"read:app_clips"}},"/legal/matters":{"get":{"operationId":"legal_matters","summary":"List Matters","description":"Your matters, newest first, with open-deadline counts and the next due date. The client national id is never returned.\n\nRequired API key scope: `read:legal`.","tags":["Legal"],"parameters":[{"name":"status","in":"query","description":"Only matters in this status","schema":{"type":"string","enum":["intake","open","on_hold","closed","archived"]}},{"name":"limit","in":"query","description":"Page size (1-100)","schema":{"type":"integer","default":25,"example":25}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0,"example":0}}],"responses":{"200":{"description":"Matters","content":{"application/json":{"example":{"success":true,"data":[{"id":"5f1e2d3c-4b5a-4968-8776-a5b4c3d2e1f0","reference":"2026-014","title":"Cohen v. Levi - breach of contract","practice_area":"civil","court_case_no":"12345-06-26","court":"Tel Aviv Magistrates Court","status":"open","opened_at":"2026-06-10T08:00:00.000Z","privileged":true,"limitation_date":"2032-05-31","contact_id":812,"client_name":"Dana Cohen","open_deadlines":2,"next_deadline_at":"2026-10-05T21:00:00.000Z","unconfirmed_deadlines":1}],"total":1,"limit":25,"offset":0}}}},"400":{"description":"Invalid status, limit or offset"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:legal, or the legal module is not enabled (code MODULE_LOCKED)"}},"x-required-scope":"read:legal"}},"/legal/deadlines":{"get":{"operationId":"legal_deadlines","summary":"Upcoming Deadlines","description":"Open deadlines due within the window, overdue first. A deadline with source \"extracted\" and no confirmed_at is a proposal (is_operative false) until someone confirms it in the app.\n\nRequired API key scope: `read:legal`.","tags":["Legal"],"parameters":[{"name":"within_days","in":"query","description":"Days ahead to include (0-365); overdue deadlines are always included","schema":{"type":"integer","default":14,"example":30}},{"name":"include_unconfirmed","in":"query","description":"Include extracted deadlines nobody confirmed yet","schema":{"type":"boolean","default":true}}],"responses":{"200":{"description":"Deadlines","content":{"application/json":{"example":{"success":true,"data":[{"id":"9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d","matter_id":"5f1e2d3c-4b5a-4968-8776-a5b4c3d2e1f0","matter_reference":"2026-014","matter_title":"Cohen v. Levi - breach of contract","title":"Statement of defence due","due_at":"2026-10-05T21:00:00.000Z","source":"extracted","statutory_ref":"Civil Procedure Regulations 2018, reg. 13","confirmed_at":null,"is_operative":false,"overdue":false}],"within_days":14}}}},"400":{"description":"Invalid within_days or include_unconfirmed"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:legal, or the legal module is not enabled (code MODULE_LOCKED)"}},"x-required-scope":"read:legal"}},"/legal/matters/{id}":{"get":{"operationId":"legal_matter_detail","summary":"Get Matter","description":"One matter of your firm with its parties and every open deadline (unconfirmed proposals included, is_operative false). The client national id is never returned. Another firm's matter answers 404.\n\nRequired API key scope: `read:legal`.","tags":["Legal"],"parameters":[{"name":"id","in":"path","required":true,"description":"Matter id (UUID)","schema":{"type":"string","example":"5f1e2d3c-4b5a-4968-8776-a5b4c3d2e1f0"}}],"responses":{"200":{"description":"The matter","content":{"application/json":{"example":{"success":true,"data":{"id":"5f1e2d3c-4b5a-4968-8776-a5b4c3d2e1f0","reference":"2026-014","title":"Cohen v. Levi - breach of contract","status":"open","client_name":"Dana Cohen","parties":[{"id":"c1d2...","display_name":"Levi Ltd","role":"defendant","represented_by":null,"contact_id":null}],"deadlines":[{"id":"9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d","title":"Statement of defence due","due_at":"2026-10-05T21:00:00.000Z","source":"extracted","confirmed_at":null,"is_operative":false,"overdue":false}]}}}}},"400":{"description":"id is not a UUID"},"401":{"description":"Unauthorized - Invalid or missing API key or app session"},"403":{"description":"Forbidden - lacks read:legal, or the legal module is not enabled (code MODULE_LOCKED)"},"404":{"description":"No such matter in your firm"}},"x-required-scope":"read:legal"}},"/legal/deadlines/awaiting-confirmation":{"get":{"operationId":"legal_deadlines_awaiting","summary":"Deadlines Awaiting Confirmation","description":"Dates the system extracted from documents that nobody has confirmed yet, soonest first. Each one is a proposal until a person confirms it.\n\nRequired API key scope: `read:legal`.","tags":["Legal"],"responses":{"200":{"description":"Proposals","content":{"application/json":{"example":{"success":true,"data":[{"id":"9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d","matter_id":"5f1e2d3c-4b5a-4968-8776-a5b4c3d2e1f0","matter_reference":"2026-014","matter_title":"Cohen v. Levi - breach of contract","title":"Statement of defence due","due_at":"2026-10-05T21:00:00.000Z","source":"extracted","confirmed_at":null,"is_operative":false,"overdue":false}]}}}},"401":{"description":"Unauthorized - Invalid or missing API key or app session"},"403":{"description":"Forbidden - lacks read:legal, or the legal module is not enabled (code MODULE_LOCKED)"}},"x-required-scope":"read:legal"}},"/legal/deadlines/{id}/confirm":{"post":{"operationId":"legal_deadline_confirm","summary":"Confirm Deadline","description":"Confirms an extracted deadline: the date a right expires on becomes operative. A professional decision, so only a signed-in app user (an app session, not an API key alone) who holds the firm's deadline-confirm permission may do it. Audited.\n\nRequired API key scope: `write:legal`.","tags":["Legal"],"parameters":[{"name":"id","in":"path","required":true,"description":"Deadline id (UUID)","schema":{"type":"string","example":"9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d"}}],"responses":{"200":{"description":"Confirmed","content":{"application/json":{"example":{"success":true,"data":{"id":"9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d","title":"Statement of defence due","due_at":"2026-10-05T21:00:00.000Z","source":"extracted","confirmed_at":"2026-10-14T09:12:00.000Z","is_operative":true,"overdue":false}}}}},"400":{"description":"id is not a UUID"},"401":{"description":"Unauthorized - no valid app session"},"403":{"description":"Lacks write:legal; an API key without a signed-in user (USER_SESSION_REQUIRED); the user lacks the confirm permission (PERMISSION_DENIED); or the legal module is not enabled (MODULE_LOCKED)"},"404":{"description":"No such deadline in your firm, or it is already confirmed"}},"x-required-scope":"write:legal"}},"/vending/machines":{"get":{"operationId":"vending_machines","summary":"List Machines","description":"Your machines from Nayax: name, number, serial, status bit and location. When no Nayax token is connected the answer is connected=false with an empty list. status_bit is Nayax's raw MachineStatusBit.\n\nRequired API key scope: `read:vending`.","tags":["Vending"],"parameters":[{"name":"name","in":"query","description":"Filter by machine name (as Nayax matches it)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size (1-200)","schema":{"type":"integer","default":100,"example":100}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0,"example":0}}],"responses":{"200":{"description":"Machines","content":{"application/json":{"example":{"success":true,"data":{"connected":true,"machines":[{"machine_id":948201,"machine_name":"Lobby snacks","machine_number":"TLV-012","serial_number":"0434331234567","status_bit":1,"city":"Tel Aviv","address":"Yigal Alon 94","last_updated":"2026-09-28T17:42:00Z"}]}}}}},"400":{"description":"Invalid name, limit or offset"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:vending, or the Vending module is not in your plan (code MODULE_LOCKED)"},"409":{"description":"The stored Nayax connection must be re-established in the app (code VENDING_TOKEN_CORRUPTED)"},"502":{"description":"Nayax refused or did not answer (codes VENDING_TOKEN_INVALID, VENDING_TOKEN_FORBIDDEN, VENDING_LYNX_ERROR)"}},"x-required-scope":"read:vending"}},"/carpentry/projects":{"get":{"operationId":"carpentry_projects","summary":"List Projects","description":"Your carpentry projects, newest first. Ids are returned as strings (64-bit).\n\nRequired API key scope: `read:carpentry`.","tags":["Carpentry"],"parameters":[{"name":"status","in":"query","description":"Only projects in this status (for example approved, in_production, ready_for_install)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size (1-200)","schema":{"type":"integer","default":50,"example":50}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0,"example":0}}],"responses":{"200":{"description":"Projects","content":{"application/json":{"example":{"success":true,"data":[{"id":"12","name":"Levi kitchen","project_type":"kitchen","status":"approved","party_id":"3f2b1c7e-9d4a-4e8b-a1c2-7d6e5f4a3b21","crm_item_id":null,"deal_id":null,"address_street":"HaAtzmaut 45","address_city":"Haifa","address_country":"IL","target_start_date":"2026-10-12","target_end_date":"2026-11-02","tags":["kitchen"],"created_at":"2026-09-20T07:10:00.000Z","updated_at":"2026-09-25T11:00:00.000Z"}],"limit":50,"offset":0}}}},"400":{"description":"Invalid status, limit or offset"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:carpentry, or the Carpentry module is not enabled (code MODULE_LOCKED)"}},"x-required-scope":"read:carpentry"}},"/carpentry/work-orders":{"get":{"operationId":"carpentry_work_orders","summary":"List Work Orders","description":"Workshop work orders by priority, then scheduled start. Filter by project to follow one job through the shop.\n\nRequired API key scope: `read:carpentry`.","tags":["Carpentry"],"parameters":[{"name":"project_id","in":"query","description":"Only work orders of this project","schema":{"type":"integer"}},{"name":"status","in":"query","description":"Only work orders in this status","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size (1-200)","schema":{"type":"integer","default":50,"example":50}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0,"example":0}}],"responses":{"200":{"description":"Work orders","content":{"application/json":{"example":{"success":true,"data":[{"id":"31","project_id":"12","number":"WO-2026-0031","status":"in_progress","priority":1,"scheduled_start":"2026-10-12T05:00:00.000Z","scheduled_end":"2026-10-16T14:00:00.000Z","actual_start":"2026-10-12T05:20:00.000Z","actual_end":null,"total_labor_hours_est":32,"total_labor_hours_actual":9.5,"created_at":"2026-10-01T09:00:00.000Z","updated_at":"2026-10-12T05:20:00.000Z"}],"limit":50,"offset":0}}}},"400":{"description":"Invalid project_id, status, limit or offset"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:carpentry, or the Carpentry module is not enabled (code MODULE_LOCKED)"}},"x-required-scope":"read:carpentry"}},"/sensing/sensors":{"get":{"operationId":"sensing_sensors","summary":"List Sensors","description":"Your sensors, newest first, with status and when each was last seen. Network details (IP, MAC, config) are never returned.\n\nRequired API key scope: `read:sensing`.","tags":["Sensing"],"parameters":[{"name":"status","in":"query","description":"Only sensors in this status","schema":{"type":"string","enum":["active","inactive","error"]}},{"name":"limit","in":"query","description":"Page size (1-200)","schema":{"type":"integer","default":50,"example":50}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0,"example":0}}],"responses":{"200":{"description":"Sensors","content":{"application/json":{"example":{"success":true,"data":[{"id":"6b1f0c2e-8a3d-4f5b-9c7e-2d1a0b9c8e7f","name":"Front office router","type":"wifi_router","location":"Reception","status":"active","last_seen_at":"2026-09-29T13:58:12.000Z","created_at":"2026-08-01T09:00:00.000Z","updated_at":"2026-09-29T13:58:12.000Z"}],"limit":50,"offset":0}}}},"400":{"description":"Invalid status, limit or offset"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:sensing, or the Sensing module is not enabled (code MODULE_LOCKED)"}},"x-required-scope":"read:sensing"}},"/sensing/anomalies":{"get":{"operationId":"sensing_anomalies","summary":"List Anomalies","description":"Anomalies newest first, with the zone they happened in. Filter status=open to poll for what still needs attention.\n\nRequired API key scope: `read:sensing`.","tags":["Sensing"],"parameters":[{"name":"status","in":"query","description":"Only anomalies in this status","schema":{"type":"string","enum":["open","acknowledged","resolved"]}},{"name":"severity","in":"query","description":"Only anomalies of this severity","schema":{"type":"string","enum":["low","medium","high","critical"]}},{"name":"zone_id","in":"query","description":"Only anomalies in this zone (uuid)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size (1-200)","schema":{"type":"integer","default":50,"example":50}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0,"example":0}}],"responses":{"200":{"description":"Anomalies","content":{"application/json":{"example":{"success":true,"data":[{"id":"0c9d8e7f-6a5b-4c3d-8e2f-1a0b9c8d7e6f","zone_id":"4e3d2c1b-0a9f-4e8d-9c7b-6a5f4e3d2c1b","zone_name":"Warehouse","anomaly_type":"after_hours_motion","description":"Motion detected at 02:14 outside opening hours","severity":"high","status":"open","resolved_at":null,"created_at":"2026-09-29T23:14:00.000Z","updated_at":"2026-09-29T23:14:00.000Z"}],"limit":50,"offset":0}}}},"400":{"description":"Invalid status, severity, zone_id, limit or offset"},"401":{"description":"Unauthorized - Invalid or missing API key"},"403":{"description":"Forbidden - key lacks read:sensing, or the Sensing module is not enabled (code MODULE_LOCKED)"}},"x-required-scope":"read:sensing"}},"/ai/chat":{"post":{"operationId":"ai_chat","summary":"Chat","description":"OpenAI-style chat: send messages[] (or system_prompt + user_message) and get the answer as text and as choices[]. A skill your business attached to API chat is applied automatically.\n\nRequired API key scope: `use:ai`.","tags":["AI Chat"],"requestBody":{"description":"Chat request","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{},"description":"OpenAI-style history: {role: system|user|assistant, content}. Required unless user_message is sent."},"system_prompt":{"type":"string","description":"Single-shot alternative to a system message"},"user_message":{"type":"string","description":"Single-shot alternative to messages[]"},"max_tokens":{"type":"integer","description":"Answer length cap, 1-4096 (default 2048)"},"temperature":{"type":"number","description":"0-1 (default 0.5)"}}},"example":{"messages":[{"role":"system","content":"You answer customers of a dental clinic in Hebrew."},{"role":"user","content":"מתי אתם פתוחים ביום שישי?"}],"max_tokens":512,"temperature":0.3}}}},"responses":{"200":{"description":"The answer","content":{"application/json":{"example":{"success":true,"data":{"text":"ביום שישי אנחנו פתוחים 08:00-13:00.","model":"claude-haiku-4-5","provider":"anthropic","usage":null,"choices":[{"message":{"role":"assistant","content":"ביום שישי אנחנו פתוחים 08:00-13:00."},"finish_reason":"stop"}]}}}}},"400":{"description":"No user_message or messages[]"},"401":{"description":"Unauthorized - Invalid or missing API key"},"402":{"description":"No AI access or credits for this business (code INSUFFICIENT_CREDITS)"},"403":{"description":"Forbidden - key lacks use:ai"},"502":{"description":"The AI request failed"},"503":{"description":"The AI service is not available"}},"x-required-scope":"use:ai"}},"/ai/persona-chat":{"post":{"operationId":"ai_persona_chat","summary":"Persona Chat","description":"Chat with a Lynxbe AI persona available to your business. The persona owns its system prompt: any system message you send is dropped. Unknown personas and personas not available to your business both answer 404.\n\nRequired API key scope: `use:ai`.","tags":["AI Chat"],"requestBody":{"description":"Chat request","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"agent_key":{"type":"string","description":"The persona key"},"locale":{"type":"string","description":"he | en | ar (default: the persona default)"},"messages":{"type":"array","items":{},"description":"OpenAI-style history (system entries dropped, recent turns kept). Required unless user_message is sent."},"user_message":{"type":"string","description":"Single-shot alternative to messages[]"},"patient":{"type":"object","additionalProperties":true,"description":"Optional structured context for personas that accept it; only the fields the persona allows are used, the rest are dropped"},"max_tokens":{"type":"integer","description":"Answer length cap, 1-4096 (default 2048)"},"temperature":{"type":"number","description":"0-1 (default 0.5)"}},"required":["agent_key"]},"example":{"agent_key":"clinic_assistant","locale":"he","user_message":"איך להתכונן לבדיקה מחר?","max_tokens":800}}}},"responses":{"200":{"description":"The answer","content":{"application/json":{"example":{"success":true,"data":{"text":"...","agent_key":"clinic_assistant","locale":"he","model":"claude-haiku-4-5","provider":"anthropic","usage":{"tokens_in":812,"tokens_out":164},"choices":[{"message":{"role":"assistant","content":"..."},"finish_reason":"stop"}]}}}}},"400":{"description":"No user_message or messages[]"},"401":{"description":"Unauthorized - Invalid or missing API key"},"402":{"description":"No AI access or credits for this business"},"403":{"description":"Forbidden - key lacks use:ai"},"404":{"description":"Missing or unknown agent_key, or a persona not available to your business"},"502":{"description":"The AI request failed"},"503":{"description":"The AI service is not available"}},"x-required-scope":"use:ai"}},"/apps/{app}/me":{"get":{"operationId":"app_me","summary":"Who Am I","description":"The signed-in user, the firm and the session scopes. Authorization: Bearer <access token of THIS app>.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}}],"responses":{"200":{"description":"The session","content":{"application/json":{"example":{"success":true,"data":{"user":{"id":"4401","email":"dana@cohenlaw.co.il","name":"Dana Cohen","role":"company_admin"},"firm":{"business_id":"901","name":"Cohen & Co.","status":"active"},"scopes":["app:assistant","read:legal","write:legal"]}}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"An API key without a signed-in user (code USER_SESSION_REQUIRED)"},"404":{"description":"Unknown or inactive app (code APP_NOT_FOUND)"}}}},"/apps/{app}/assistant/sessions":{"get":{"operationId":"app_assistant_sessions","summary":"Lynxbe AI - List Chats","description":"The signed-in user's Lynxbe AI chats in the firm, newest first. Same answer as the Lynxbe AI screen.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"limit","in":"query","description":"Page size","schema":{"type":"integer","example":20}}],"responses":{"200":{"description":"Chats","content":{"application/json":{"example":{"success":true,"data":[{"id":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c","title":"Statement of defence","pinned":false,"updated_at":"2026-10-14T09:00:00.000Z"}]}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"},"post":{"operationId":"app_assistant_session_create","summary":"Lynxbe AI - New Chat","description":"Opens a chat in the firm. Optional metadata.recordContext pins it to one record.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}}],"requestBody":{"description":"JSON body","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Optional title"},"goal":{"type":"string","description":"Optional goal"},"metadata":{"type":"object","additionalProperties":true,"description":"Optional, e.g. { recordContext: { type, id } }"}}},"example":{"title":"Statement of defence"}}}},"responses":{"201":{"description":"The chat","content":{"application/json":{"example":{"success":true,"data":{"id":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c","title":"Statement of defence"}}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"}},"/apps/{app}/assistant/sessions/{id}":{"get":{"operationId":"app_assistant_session_get","summary":"Lynxbe AI - Get Chat","description":"One chat with its messages (content blocks, citations, process steps), as the Lynxbe AI screen shows them.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Session id (UUID)","schema":{"type":"string","example":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"}}],"responses":{"200":{"description":"The chat and its messages","content":{"application/json":{"example":{"success":true,"data":{"session":{"id":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"},"messages":[]}}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"},"patch":{"operationId":"app_assistant_session_update","summary":"Lynxbe AI - Rename Chat","description":"Changes the chat title or goal. Needs the Lynxbe AI manage permission.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Session id (UUID)","schema":{"type":"string","example":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"}}],"requestBody":{"description":"JSON body","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"New title"}}},"example":{"title":"Defence - Cohen v. Levi"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"example":{"success":true,"data":{"id":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"}}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"},"delete":{"operationId":"app_assistant_session_delete","summary":"Lynxbe AI - Delete Chat","description":"Deletes the chat. Needs the Lynxbe AI manage permission.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Session id (UUID)","schema":{"type":"string","example":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"example":{"success":true}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"}},"/apps/{app}/assistant/sessions/{id}/pin":{"patch":{"operationId":"app_assistant_session_pin","summary":"Lynxbe AI - Pin Chat","description":"Pins or unpins the chat.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Session id (UUID)","schema":{"type":"string","example":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"}}],"requestBody":{"description":"JSON body","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"pinned":{"type":"boolean","description":"true to pin"}}},"example":{"pinned":true}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"example":{"success":true}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"}},"/apps/{app}/assistant/sessions/{id}/messages":{"post":{"operationId":"app_assistant_message","summary":"Lynxbe AI - Send Message","description":"One turn, answered as JSON: the same answer, content blocks, citations (through the citation gate) and action proposals as the Lynxbe AI screen. agent_key must be one of the app's assistants; missing = the app's first assistant. Uses the firm's AI credits.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Session id (UUID)","schema":{"type":"string","example":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"}}],"requestBody":{"description":"JSON body","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"The user's message"},"agent_key":{"type":"string","description":"One of the app's assistants (default: the first)"},"locale":{"type":"string","description":"he | en"}},"required":["message"]},"example":{"message":"What is the deadline to file a statement of defence?","agent_key":"lexil_counsel","locale":"he"}}}},"responses":{"200":{"description":"The answer","content":{"application/json":{"example":{"success":true,"data":{"message":{"role":"assistant","content":"..."},"content_blocks":[],"action_proposals":[]}}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"402":{"description":"No AI credits for the firm"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"},"502":{"description":"The AI request failed"}},"x-required-scope":"app:assistant"}},"/apps/{app}/assistant/sessions/{id}/messages/stream":{"post":{"operationId":"app_assistant_message_stream","summary":"Lynxbe AI - Send Message (stream)","description":"The same turn as Server-Sent Events, exactly the Lynxbe AI stream: content_blocks, process_step, tool events, action_proposals, navigation, stream_end, then done. A \": ping\" comment every 15 seconds keeps the connection open.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Session id (UUID)","schema":{"type":"string","example":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"}}],"requestBody":{"description":"JSON body","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"The user's message"},"agent_key":{"type":"string","description":"One of the app's assistants (default: the first)"}},"required":["message"]},"example":{"message":"Summarise the matter","agent_key":"lexil_counsel"}}}},"responses":{"200":{"description":"text/event-stream"},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"402":{"description":"No AI credits for the firm"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"}},"/apps/{app}/assistant/sessions/{id}/messages/{messageId}/feedback":{"post":{"operationId":"app_assistant_message_feedback","summary":"Lynxbe AI - Rate Answer","description":"Thumbs up / down on one answer.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Session id (UUID)","schema":{"type":"string","example":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"}},{"name":"messageId","in":"path","required":true,"description":"Message id","schema":{"type":"string","example":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"}}],"requestBody":{"description":"JSON body","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"rating":{"type":"string","description":"up | down"},"comment":{"type":"string","description":"Optional"}}},"example":{"rating":"up"}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"example":{"success":true}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"}},"/apps/{app}/assistant/sessions/{id}/messages/{messageId}":{"delete":{"operationId":"app_assistant_message_delete","summary":"Lynxbe AI - Delete Message","description":"Deletes one message from the chat. Needs the Lynxbe AI manage permission.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Session id (UUID)","schema":{"type":"string","example":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"}},{"name":"messageId","in":"path","required":true,"description":"Message id","schema":{"type":"string","example":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"example":{"success":true}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"}},"/apps/{app}/assistant/actions":{"get":{"operationId":"app_assistant_actions","summary":"Lynxbe AI - Proposed Actions","description":"Actions Lynxbe AI proposed in the firm, with risk, rollback and evidence for the person deciding. Needs the Lynxbe AI manage permission.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"session_id","in":"query","description":"Only this chat (UUID)","schema":{"type":"string"}}],"responses":{"200":{"description":"Actions","content":{"application/json":{"example":{"success":true,"data":[]}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"}},"/apps/{app}/assistant/actions/{id}/approve":{"post":{"operationId":"app_assistant_action_approve","summary":"Lynxbe AI - Approve Action","description":"Approves a proposed action. Needs the Lynxbe AI manage permission. Audited.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Action id (UUID)","schema":{"type":"string","example":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"}}],"responses":{"200":{"description":"Approved","content":{"application/json":{"example":{"success":true}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"}},"/apps/{app}/assistant/actions/{id}/reject":{"post":{"operationId":"app_assistant_action_reject","summary":"Lynxbe AI - Reject Action","description":"Rejects a proposed action. Needs the Lynxbe AI manage permission. Audited.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Action id (UUID)","schema":{"type":"string","example":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"}}],"responses":{"200":{"description":"Rejected","content":{"application/json":{"example":{"success":true}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"}},"/apps/{app}/assistant/actions/{id}/execute":{"post":{"operationId":"app_assistant_action_execute","summary":"Lynxbe AI - Execute Action","description":"Runs an approved action. Needs the Lynxbe AI manage permission. Audited.\n\nRequired API key scope: `app:assistant`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Action id (UUID)","schema":{"type":"string","example":"6f1c2b8a-0d4e-4f5a-9b3c-2e1d0f9a8b7c"}}],"responses":{"200":{"description":"Executed","content":{"application/json":{"example":{"success":true}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks app:assistant; the user lacks Lynxbe AI permission (PERMISSION_DENIED); an agent_key the app may not use (AGENT_NOT_ALLOWED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no assistant feature, not found in this firm, or a path outside the app surface"}},"x-required-scope":"app:assistant"}},"/apps/{app}/legal/matters":{"get":{"operationId":"app_legal_matters","summary":"Legal - List Matters","description":"The firm's matters (the same answer as GET /api/v1/legal/matters). Needs read:legal and the user's legal:matter:view permission.\n\nRequired API key scope: `read:legal`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"status","in":"query","description":"Only matters in this status","schema":{"type":"string","enum":["intake","open","on_hold","closed","archived"]}},{"name":"limit","in":"query","description":"Page size (1-100)","schema":{"type":"integer","default":25,"example":25}},{"name":"offset","in":"query","description":"Rows to skip","schema":{"type":"integer","default":0,"example":0}}],"responses":{"200":{"description":"Matters","content":{"application/json":{"example":{"success":true,"data":[],"total":0,"limit":25,"offset":0}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks the scope, the user lacks the legal permission (PERMISSION_DENIED), or the legal module is not enabled for the firm (MODULE_LOCKED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no legal feature, or not found in this firm"}},"x-required-scope":"read:legal"}},"/apps/{app}/legal/matters/{id}":{"get":{"operationId":"app_legal_matter_detail","summary":"Legal - Get Matter","description":"One matter with its parties and open deadlines (GET /api/v1/legal/matters/{id}). Never the client national id.\n\nRequired API key scope: `read:legal`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Matter id (UUID)","schema":{"type":"string","example":"5f1e2d3c-4b5a-4968-8776-a5b4c3d2e1f0"}}],"responses":{"200":{"description":"The matter","content":{"application/json":{"example":{"success":true,"data":{"id":"5f1e2d3c-4b5a-4968-8776-a5b4c3d2e1f0","parties":[],"deadlines":[]}}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks the scope, the user lacks the legal permission (PERMISSION_DENIED), or the legal module is not enabled for the firm (MODULE_LOCKED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no legal feature, or not found in this firm"}},"x-required-scope":"read:legal"}},"/apps/{app}/legal/deadlines":{"get":{"operationId":"app_legal_deadlines","summary":"Legal - Upcoming Deadlines","description":"Open deadlines due within the window (GET /api/v1/legal/deadlines). Needs the user's legal:deadline:view permission.\n\nRequired API key scope: `read:legal`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"within_days","in":"query","description":"Days ahead (0-365)","schema":{"type":"integer","default":14,"example":30}},{"name":"include_unconfirmed","in":"query","description":"Include extracted proposals","schema":{"type":"boolean","default":true}}],"responses":{"200":{"description":"Deadlines","content":{"application/json":{"example":{"success":true,"data":[],"within_days":14}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks the scope, the user lacks the legal permission (PERMISSION_DENIED), or the legal module is not enabled for the firm (MODULE_LOCKED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no legal feature, or not found in this firm"}},"x-required-scope":"read:legal"}},"/apps/{app}/legal/deadlines/awaiting-confirmation":{"get":{"operationId":"app_legal_deadlines_awaiting","summary":"Legal - Awaiting Confirmation","description":"Extracted dates nobody confirmed yet, soonest first (GET /api/v1/legal/deadlines/awaiting-confirmation).\n\nRequired API key scope: `read:legal`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}}],"responses":{"200":{"description":"Proposals","content":{"application/json":{"example":{"success":true,"data":[]}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks the scope, the user lacks the legal permission (PERMISSION_DENIED), or the legal module is not enabled for the firm (MODULE_LOCKED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no legal feature, or not found in this firm"}},"x-required-scope":"read:legal"}},"/apps/{app}/legal/deadlines/{id}/confirm":{"post":{"operationId":"app_legal_deadline_confirm","summary":"Legal - Confirm Deadline","description":"Confirms an extracted deadline (POST /api/v1/legal/deadlines/{id}/confirm): write:legal and the user's legal:deadline:confirm permission. Audited.\n\nRequired API key scope: `write:legal`.","tags":["Lynxbe Apps"],"parameters":[{"name":"app","in":"path","required":true,"description":"The app slug (= the partner slug), e.g. openlex","schema":{"type":"string","example":"openlex"}},{"name":"id","in":"path","required":true,"description":"Deadline id (UUID)","schema":{"type":"string","example":"9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d"}}],"responses":{"200":{"description":"Confirmed","content":{"application/json":{"example":{"success":true,"data":{"id":"9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d","is_operative":true}}}}},"401":{"description":"No valid app session, or a session of another app (code APP_SESSION_INVALID; APP_SESSION_EXPIRED: call /auth/refresh)"},"403":{"description":"Session lacks the scope, the user lacks the legal permission (PERMISSION_DENIED), or the legal module is not enabled for the firm (MODULE_LOCKED)"},"404":{"description":"Unknown app (APP_NOT_FOUND), the app has no legal feature, or not found in this firm"}},"x-required-scope":"write:legal"}}},"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"},"BearerAuth":{"type":"http","scheme":"bearer"}}}}