{"openapi":"3.1.0","info":{"title":"Retivo API","version":"1.0.0","description":"AI-powered customer success platform — event tracking, playbooks, interventions, and analytics."},"servers":[{"url":"https://retivo.ai","description":"Production"},{"url":"http://localhost:3000","description":"Local development"}],"security":[{"bearerAuth":[]}],"components":{"schemas":{},"parameters":{}},"paths":{"/api/health":{"get":{"tags":["System"],"summary":"Health check","description":"Returns system health status with database, Redis, LLM, and Inngest connectivity checks.","responses":{"200":{"description":"System is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded"]},"checks":{"type":"object","additionalProperties":{"type":"object","properties":{"status":{"type":"string"},"latency_ms":{"type":"number"},"error":{"type":"string"}},"required":["status"]}},"version":{"type":"string"},"uptime_seconds":{"type":"number"}},"required":["status","checks","version","uptime_seconds"]}}}},"503":{"description":"System is degraded","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["degraded"]},"checks":{"type":"object","additionalProperties":{"type":"object","properties":{"status":{"type":"string"},"latency_ms":{"type":"number"},"error":{"type":"string"}},"required":["status"]}},"version":{"type":"string"},"uptime_seconds":{"type":"number"}},"required":["status","checks","version","uptime_seconds"]}}}}}}},"/api/track":{"post":{"tags":["Events"],"summary":"Track events","description":"Ingests a batch of up to 100 events. Duplicates are silently dropped via idempotency keys.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"user_id":{"type":"string","minLength":1},"event":{"type":"string","minLength":1},"properties":{"type":"object","additionalProperties":{"nullable":true},"default":{}},"timestamp":{"type":"string","format":"date-time"},"idempotency_key":{"type":"string"}},"required":["user_id","event"]},"minItems":1,"maxItems":100}},"required":["events"]}}}},"responses":{"200":{"description":"Events accepted","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"number"},"duplicates":{"type":"number"}},"required":["accepted","duplicates"]}}}}}}},"/api/playbooks":{"get":{"tags":["Playbooks"],"summary":"List playbooks","description":"Returns all playbooks for the authenticated tenant with computed success rates.","responses":{"200":{"description":"List of playbooks","content":{"application/json":{"schema":{"type":"object","properties":{"playbooks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"lifecycle_area":{"type":"string","nullable":true},"entry_conditions":{"type":"object","nullable":true,"additionalProperties":{"nullable":true}},"exit_conditions":{"type":"object","nullable":true,"additionalProperties":{"nullable":true}},"constraints":{"type":"object","nullable":true,"additionalProperties":{"nullable":true}},"strategy_hints":{"type":"object","nullable":true,"additionalProperties":{"nullable":true}},"created_at":{"type":"string","nullable":true},"status_changed_at":{"type":"string","nullable":true},"success_rate":{"type":"number","nullable":true}},"required":["id","name","status","lifecycle_area","entry_conditions","exit_conditions","constraints","strategy_hints","created_at","status_changed_at","success_rate"]}}},"required":["playbooks"]}}}}}},"post":{"tags":["Playbooks"],"summary":"Create a playbook","description":"Creates a new playbook, subject to plan-based limits.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"entry_conditions":{"type":"object","additionalProperties":{"nullable":true},"default":{}},"exit_conditions":{"type":"object","additionalProperties":{"nullable":true},"default":{}},"constraints":{"type":"object","additionalProperties":{"nullable":true},"default":{}},"strategy_hints":{"type":"object","additionalProperties":{"nullable":true},"default":{}}},"required":["name"]}}}},"responses":{"201":{"description":"Playbook created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string","nullable":true}},"required":["id","name","status","created_at"]}}}},"403":{"description":"Playbook limit reached","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"current":{"type":"number"},"limit":{"type":"number"},"upgrade_to":{"type":"string","nullable":true}},"required":["error","current","limit","upgrade_to"]}}}}}}},"/api/playbooks/{id}":{"put":{"tags":["Playbooks"],"summary":"Update a playbook","description":"Updates fields on an existing playbook.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"entry_conditions":{"type":"object","additionalProperties":{"nullable":true},"default":{}},"exit_conditions":{"type":"object","additionalProperties":{"nullable":true},"default":{}},"constraints":{"type":"object","additionalProperties":{"nullable":true},"default":{}},"strategy_hints":{"type":"object","additionalProperties":{"nullable":true},"default":{}}}}}}},"responses":{"200":{"description":"Playbook updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"}},"required":["id","name","status"]}}}},"404":{"description":"Playbook not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"delete":{"tags":["Playbooks"],"summary":"Delete (archive) a playbook","description":"Archives a playbook by setting its status to archived.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Playbook archived","content":{"application/json":{"schema":{"type":"object","properties":{"archived":{"type":"boolean"}},"required":["archived"]}}}}}}},"/api/playbooks/{id}/status":{"put":{"tags":["Playbooks"],"summary":"Update playbook status","description":"Changes the status of a playbook (draft, active, paused, archived).","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["draft","active","paused","archived"]}},"required":["status"]}}}},"responses":{"200":{"description":"Status updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"404":{"description":"Playbook not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/playbooks/preview-count":{"post":{"tags":["Playbooks"],"summary":"Preview matching user count","description":"Returns the number of users matching the given entry conditions.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"entry_conditions":{"type":"object","additionalProperties":{"nullable":true}}},"required":["entry_conditions"]}}}},"responses":{"200":{"description":"Matching user count","content":{"application/json":{"schema":{"type":"object","properties":{"matching_users":{"type":"number"}},"required":["matching_users"]}}}}}}},"/api/interventions":{"get":{"tags":["Interventions"],"summary":"List interventions","description":"Returns a list of interventions for the current tenant, ordered by planned_at descending.","parameters":[{"schema":{"type":"string","example":"20"},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"List of interventions","content":{"application/json":{"schema":{"type":"object","properties":{"interventions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"user_id":{"type":"string"},"playbook_id":{"type":"string","nullable":true},"status":{"type":"string"},"channel":{"type":"string"},"message_body":{"type":"string","nullable":true},"reasoning":{"type":"string","nullable":true},"confidence":{"type":"number","nullable":true},"planned_at":{"type":"string","nullable":true},"approved_at":{"type":"string","nullable":true},"sent_at":{"type":"string","nullable":true},"delivered_at":{"type":"string","nullable":true},"flagged":{"type":"boolean","nullable":true},"flag_reason":{"type":"string","nullable":true},"quality_checks":{"nullable":true}},"required":["id","user_id","playbook_id","status","channel","message_body","reasoning","confidence","planned_at","approved_at","sent_at","delivered_at","flagged","flag_reason"]}}},"required":["interventions"]}}}}}},"post":{"tags":["Interventions"],"summary":"Create manual intervention","description":"Creates a manual intervention from the dashboard.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string","minLength":1},"playbook_id":{"type":"string","format":"uuid"},"channel":{"type":"string","enum":["email","in_app","webhook","crm"]},"message_body":{"type":"string"}},"required":["user_id","playbook_id","channel"]}}}},"responses":{"201":{"description":"Intervention created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"channel":{"type":"string"},"planned_at":{"type":"string","nullable":true}},"required":["id","status","channel","planned_at"]}}}}}}},"/api/interventions/{id}/approve":{"put":{"tags":["Interventions"],"summary":"Approve intervention","description":"Approves a planned intervention and triggers the execute pipeline for delivery.","parameters":[{"schema":{"type":"string","example":"int_abc123"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Intervention approved","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"404":{"description":"Intervention not found or not in planned status","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/interventions/{id}/pause":{"put":{"tags":["Interventions"],"summary":"Pause intervention","description":"Pauses an intervention by setting its status back to planned to halt the pipeline.","parameters":[{"schema":{"type":"string","example":"int_abc123"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Intervention paused","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"404":{"description":"Intervention not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/interventions/{id}/reject":{"put":{"tags":["Interventions"],"summary":"Reject intervention","description":"Rejects a planned intervention with a reason.","parameters":[{"schema":{"type":"string","example":"int_abc123"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string"}},"required":["reason"]}}}},"responses":{"200":{"description":"Intervention rejected","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"404":{"description":"Intervention not found or not in planned status","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/interventions/{id}/rollback":{"put":{"tags":["Interventions"],"summary":"Roll back a delivered intervention","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Reason for rollback"}}}}}},"responses":{"200":{"description":"Intervention rolled back","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"404":{"description":"Intervention not found or not in a rollback-eligible status","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/interventions/{id}/flag":{"put":{"tags":["Interventions"],"summary":"Flag intervention","description":"Flags an intervention as bad with a reason (post-delivery feedback).","parameters":[{"schema":{"type":"string","example":"int_abc123"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","minLength":1}},"required":["reason"]}}}},"responses":{"200":{"description":"Intervention flagged","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"flagged":{"type":"boolean"}},"required":["id","flagged"]}}}},"404":{"description":"Intervention not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/escalations":{"get":{"tags":["Escalations"],"summary":"List escalations","description":"Returns a list of escalations (interventions where channel = 'escalate') for the current tenant.","parameters":[{"schema":{"type":"string","example":"20"},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"List of escalations","content":{"application/json":{"schema":{"type":"object","properties":{"escalations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"user_id":{"type":"string"},"status":{"type":"string","nullable":true},"reasoning":{"type":"string","nullable":true},"suggested_action":{"type":"string","nullable":true},"handled_by":{"type":"string","nullable":true},"outcome":{"type":"string","nullable":true},"created_at":{"type":"string","nullable":true}},"required":["id","user_id","status","reasoning","suggested_action","handled_by","outcome","created_at"]}}},"required":["escalations"]}}}}}},"post":{"tags":["Escalations"],"summary":"Create manual escalation","description":"Creates a manual escalation from the dashboard.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string","minLength":1},"playbook_id":{"type":"string","format":"uuid"},"severity":{"type":"string","enum":["Critical","High","Medium","Low"]},"reason":{"type":"string","minLength":1}},"required":["user_id","playbook_id","severity","reason"]}}}},"responses":{"201":{"description":"Escalation created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}}}}},"/api/escalations/{id}/handle":{"put":{"tags":["Escalations"],"summary":"Handle escalation","description":"Marks an escalation as handled with an action taken and optional outcome.","parameters":[{"schema":{"type":"string","example":"esc_abc123"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"action_taken":{"type":"string"},"outcome":{"type":"string","enum":["positive","negative","neutral","pending"]}},"required":["action_taken"]}}}},"responses":{"200":{"description":"Escalation handled","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"404":{"description":"Escalation not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/escalations/{id}/delegate":{"put":{"tags":["Escalations"],"summary":"Delegate escalation","description":"Delegates an escalation by re-entering the execute pipeline with a specified channel.","parameters":[{"schema":{"type":"string","example":"esc_abc123"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"string","minLength":1,"default":"email"}}}}}},"responses":{"200":{"description":"Escalation delegated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"404":{"description":"Escalation not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/customers/stats":{"get":{"tags":["Customers"],"summary":"Get customer statistics","description":"Returns aggregate counts of total, active, at-risk, and churned customers for the tenant.","responses":{"200":{"description":"Customer statistics","content":{"application/json":{"schema":{"type":"object","properties":{"stats":{"type":"object","properties":{"total":{"type":"number"},"active":{"type":"number"},"at_risk":{"type":"number"},"churned":{"type":"number"}},"required":["total","active","at_risk","churned"]}},"required":["stats"]}}}}}}},"/api/customers":{"post":{"tags":["Customers"],"summary":"Create a customer","description":"Creates a new customer (user) record. If the user already exists, returns the existing record.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string","minLength":1},"email":{"type":"string","format":"email"},"name":{"type":"string"},"properties":{"type":"object","additionalProperties":{"nullable":true}}},"required":["user_id"]}}}},"responses":{"200":{"description":"Customer already exists — returning existing record","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{},"additionalProperties":{"nullable":true}}},"required":["user"]}}}},"201":{"description":"Customer created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{},"additionalProperties":{"nullable":true}}},"required":["user"]}}}}}},"get":{"tags":["Customers"],"summary":"List customers","description":"Returns all customers for the tenant with their state, scores, and next pending action.","responses":{"200":{"description":"List of customers","content":{"application/json":{"schema":{"type":"object","properties":{"customers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string","nullable":true},"company":{"type":"string","nullable":true},"role":{"type":"string","nullable":true},"team_size":{"type":"number","nullable":true},"locale":{"type":"string","nullable":true},"lifecycle_stage":{"type":"string"},"health":{"type":"number"},"engagement_score":{"type":"number"},"expansion_score":{"type":"number"},"risk_level":{"type":"string"},"last_activity_at":{"type":"string","nullable":true},"created_at":{"type":"string","nullable":true},"next_action":{"type":"object","nullable":true,"properties":{"channel":{"type":"string"},"status":{"type":"string"},"playbook":{"type":"string"}},"required":["channel","status","playbook"]}},"required":["id","name","email","company","role","team_size","locale","lifecycle_stage","health","engagement_score","expansion_score","risk_level","last_activity_at","created_at","next_action"]}}},"required":["customers"]}}}}}}},"/api/customers/{id}":{"get":{"tags":["Customers"],"summary":"Get customer detail","description":"Returns detailed customer information including recent events and interventions.","parameters":[{"schema":{"type":"string","minLength":1,"description":"Customer user ID"},"required":true,"description":"Customer user ID","name":"id","in":"path"}],"responses":{"200":{"description":"Customer detail with events and interventions","content":{"application/json":{"schema":{"type":"object","properties":{"customer":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string","nullable":true},"company":{"type":"string","nullable":true},"role":{"type":"string","nullable":true},"team_size":{"type":"number","nullable":true},"locale":{"type":"string","nullable":true},"lifecycle_stage":{"type":"string"},"health":{"type":"number"},"engagement_score":{"type":"number"},"expansion_score":{"type":"number"},"risk_level":{"type":"string"},"last_activity_at":{"type":"string","nullable":true},"created_at":{"type":"string","nullable":true},"milestone_progress":{"nullable":true},"segment_memberships":{"nullable":true}},"required":["id","name","email","company","role","team_size","locale","lifecycle_stage","health","engagement_score","expansion_score","risk_level","last_activity_at","created_at"]},"events":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":{"nullable":true}}},"interventions":{"type":"array","items":{"type":"object","properties":{},"additionalProperties":{"nullable":true}}}},"required":["customer","events","interventions"]}}}},"404":{"description":"Customer not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/settings":{"get":{"tags":["Settings"],"summary":"Get all settings","description":"Returns tenant configuration, brand context, and active API keys for the authenticated tenant.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Settings retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"tenant":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","nullable":true},"config":{"nullable":true},"plan_tier":{"type":"string"},"plan_status":{"type":"string"},"tracked_user_limit":{"type":"number","nullable":true},"kill_switch_active":{"type":"boolean"},"shadow_mode":{"type":"boolean"},"created_at":{"type":"string"}},"required":["id","name","slug","plan_tier","plan_status","tracked_user_limit","kill_switch_active","shadow_mode","created_at"]},"brand_context":{"type":"object","nullable":true,"properties":{"product_description":{"type":"string","nullable":true},"tone_guidelines":{"type":"string","nullable":true},"things_to_mention":{"type":"array","nullable":true,"items":{"type":"string"}},"things_to_avoid":{"type":"array","nullable":true,"items":{"type":"string"}}},"required":["product_description","tone_guidelines","things_to_mention","things_to_avoid"]},"api_keys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"prefix":{"type":"string"},"label":{"type":"string","nullable":true},"scope":{"nullable":true},"last_used_at":{"type":"string","nullable":true},"created_at":{"type":"string"}},"required":["id","prefix","label","last_used_at","created_at"]}}},"required":["tenant","brand_context","api_keys"]}}}},"404":{"description":"Tenant not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"patch":{"tags":["Settings"],"summary":"Update tenant settings","description":"Updates tenant name, config, approval mode, and/or onboarding status.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"config":{"type":"object","additionalProperties":{"nullable":true}},"approvalMode":{"type":"string","enum":["suggest_only","semi_auto","full_auto"]},"onboardingCompleted":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Settings updated","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"config":{"nullable":true}},"required":["name"]}}}},"404":{"description":"Tenant not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/settings/brand":{"patch":{"tags":["Settings"],"summary":"Update brand context","description":"Upserts brand context including product description, tone guidelines, and mention/avoid lists.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"product_description":{"type":"string"},"tone_guidelines":{"type":"string"},"things_to_mention":{"type":"array","items":{"type":"string"}},"things_to_avoid":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Brand context updated","content":{"application/json":{"schema":{"type":"object","properties":{"updated":{"type":"boolean"}},"required":["updated"]}}}}}}},"/api/settings/kill-switch":{"put":{"tags":["Settings"],"summary":"Toggle kill switch","description":"Activates or deactivates the kill switch. Admin only.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"active":{"type":"boolean"}},"required":["active"]}}}},"responses":{"200":{"description":"Kill switch toggled","content":{"application/json":{"schema":{"type":"object","properties":{"kill_switch_active":{"type":"boolean"}},"required":["kill_switch_active"]}}}},"404":{"description":"Tenant not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/settings/shadow-mode":{"put":{"tags":["Settings"],"summary":"Toggle shadow mode","description":"Activates or deactivates shadow mode (auto-expires after 72 hours). Admin only.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"active":{"type":"boolean"}},"required":["active"]}}}},"responses":{"200":{"description":"Shadow mode toggled","content":{"application/json":{"schema":{"type":"object","properties":{"shadow_mode":{"type":"boolean"}},"required":["shadow_mode"]}}}},"404":{"description":"Tenant not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/team/members":{"get":{"tags":["Team"],"summary":"List team members","description":"Returns all team members for the authenticated tenant.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Team members list","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"user_id":{"type":"string"},"role":{"type":"string"},"created_at":{"type":"string"},"name":{"type":"string","nullable":true},"email":{"type":"string"}},"required":["id","user_id","role","created_at","name","email"]}}},"required":["members"]}}}}}}},"/api/team/invitations":{"get":{"tags":["Team"],"summary":"List pending invitations","description":"Returns all pending (unaccepted) invitations for the tenant.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Pending invitations list","content":{"application/json":{"schema":{"type":"object","properties":{"invitations":{"type":"array","items":{"nullable":true}}},"required":["invitations"]}}}}}}},"/api/team/invite":{"post":{"tags":["Team"],"summary":"Invite a team member","description":"Sends an invitation email to add a new team member. Admin only. Enforces plan-based team member limits.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["admin","editor","viewer"],"default":"viewer"}},"required":["email"]}}}},"responses":{"201":{"description":"Invitation created","content":{"application/json":{"schema":{"type":"object","properties":{"invitation":{"nullable":true}}}}}},"403":{"description":"Team member limit reached","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"current":{"type":"number"},"limit":{"type":"number"},"upgrade_to":{"type":"string"}},"required":["error"]}}}},"409":{"description":"User is already a team member","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/team/accept":{"post":{"tags":["Team"],"summary":"Accept an invitation","description":"Called by the invited user to accept a pending invitation and join the team.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","format":"uuid"}},"required":["token"]}}}},"responses":{"200":{"description":"Invitation accepted","content":{"application/json":{"schema":{"type":"object","properties":{"tenant_id":{"type":"string"},"role":{"type":"string"}},"required":["tenant_id","role"]}}}},"404":{"description":"Invalid invitation","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Invitation already accepted","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"410":{"description":"Invitation expired","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/team/members/{memberId}/role":{"put":{"tags":["Team"],"summary":"Change a member's role","description":"Updates the role of an existing team member. Admin only.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"memberId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["admin","editor","viewer"]}},"required":["role"]}}}},"responses":{"200":{"description":"Role updated","content":{"application/json":{"schema":{"type":"object","properties":{"member":{"nullable":true}}}}}},"404":{"description":"Member not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/team/members/{memberId}":{"delete":{"tags":["Team"],"summary":"Remove a team member","description":"Removes a team member from the tenant. Admin only. Cannot remove yourself.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"memberId","in":"path"}],"responses":{"200":{"description":"Member removed","content":{"application/json":{"schema":{"type":"object","properties":{"removed":{"type":"boolean"}},"required":["removed"]}}}},"400":{"description":"Cannot remove yourself","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Member not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/team/invitations/{invitationId}":{"delete":{"tags":["Team"],"summary":"Revoke a pending invitation","description":"Deletes a pending invitation. Admin only.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"invitationId","in":"path"}],"responses":{"200":{"description":"Invitation revoked","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"boolean"}},"required":["revoked"]}}}}}}},"/api/interventions/dry-run":{"post":{"tags":["Interventions"],"summary":"Dry-run intervention decision","description":"Simulates the full evaluation pipeline for a user without creating an intervention. Returns the rules result and LLM decision.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string","minLength":1}},"required":["user_id"]}}}},"responses":{"200":{"description":"Dry-run result with decision and rules evaluation","content":{"application/json":{"schema":{"type":"object","properties":{"dry_run":{"type":"boolean","enum":[true]},"decision":{"type":"object","properties":{"action":{"type":"string"},"playbook_id":{"type":"string"},"channel":{"type":"string"},"confidence":{"type":"number"},"reasoning":{"type":"string"},"reason":{"type":"string"}},"required":["action"]},"rules":{"type":"object","properties":{},"additionalProperties":{"nullable":true}},"context":{"type":"object","properties":{"lifecycle_stage":{"type":"string"},"activation_score":{"type":"number"},"risk_level":{"type":"string"},"matched_playbooks":{"type":"number"}}}},"required":["dry_run","decision","rules"]}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/analytics":{"get":{"tags":["Analytics"],"summary":"Get analytics dashboard data","description":"Returns KPIs, ROI metrics, lifecycle/risk distributions, intervention stats, outcome stats, top playbooks, and lifecycle area breakdowns for the given time range.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["7d","30d","90d"],"default":"30d","description":"Time range for analytics data"},"required":false,"description":"Time range for analytics data","name":"range","in":"query"}],"responses":{"200":{"description":"Analytics data","content":{"application/json":{"schema":{"type":"object","properties":{"range":{"type":"string"},"kpis":{"type":"object","properties":{"total_users":{"type":"number"},"total_interventions":{"type":"number"},"positive_outcome_rate":{"type":"number"},"churn_rate":{"type":"number"},"retention_rate":{"type":"number"}},"required":["total_users","total_interventions","positive_outcome_rate","churn_rate","retention_rate"]},"roi":{"type":"object","properties":{"recovered_users":{"type":"number"},"intervention_success_rate":{"type":"number"},"avg_time_to_outcome_days":{"type":"number"},"outcomes_by_lifecycle_area":{"type":"array","items":{"nullable":true}}},"required":["recovered_users","intervention_success_rate","avg_time_to_outcome_days","outcomes_by_lifecycle_area"]},"lifecycle_distribution":{"type":"array","items":{"nullable":true}},"risk_distribution":{"type":"array","items":{"nullable":true}},"intervention_stats":{"type":"array","items":{"nullable":true}},"outcome_stats":{"type":"array","items":{"nullable":true}},"top_playbooks":{"type":"array","items":{"nullable":true}},"lifecycle_area_breakdown":{"type":"array","items":{"nullable":true}}},"required":["range","kpis","roi","lifecycle_distribution","risk_distribution","intervention_stats","outcome_stats","top_playbooks","lifecycle_area_breakdown"]}}}}}}},"/api/connectors/segment":{"post":{"tags":["Connectors"],"summary":"Ingest Segment events","description":"Receives a Segment webhook payload, normalizes it, and ingests users and events.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{"nullable":true}}}}},"responses":{"200":{"description":"Ingestion result","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"number"},"users_upserted":{"type":"number"}},"required":["accepted","users_upserted"]}}}}}}},"/api/connectors/posthog":{"post":{"tags":["Connectors"],"summary":"Ingest PostHog events","description":"Receives a PostHog webhook payload, normalizes it, and ingests users and events.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{"nullable":true}}}}},"responses":{"200":{"description":"Ingestion result","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"number"},"users_upserted":{"type":"number"}},"required":["accepted","users_upserted"]}}}}}}},"/api/connectors/mixpanel":{"post":{"tags":["Connectors"],"summary":"Ingest Mixpanel events","description":"Receives a Mixpanel webhook payload, normalizes it, and ingests users and events.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":{"nullable":true}}}}},"responses":{"200":{"description":"Ingestion result","content":{"application/json":{"schema":{"type":"object","properties":{"accepted":{"type":"number"},"users_upserted":{"type":"number"}},"required":["accepted","users_upserted"]}}}}}}},"/api/billing/checkout":{"post":{"tags":["Billing"],"summary":"Create checkout session","description":"Creates a Stripe Checkout session for upgrading to a paid plan.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","enum":["starter","growth","scale"]},"interval":{"type":"string","enum":["monthly","annual"],"default":"monthly"}},"required":["plan"]}}}},"responses":{"200":{"description":"Checkout session URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"400":{"description":"Invalid plan","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Tenant not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Billing not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/billing/portal":{"get":{"tags":["Billing"],"summary":"Create portal session","description":"Creates a Stripe Customer Portal session for managing the subscription.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Portal session URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"404":{"description":"No billing account","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Billing not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/webhooks/endpoints":{"get":{"tags":["Webhooks"],"summary":"List webhook endpoints","description":"Returns all active webhook endpoints for the current tenant.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"List of webhook endpoints","content":{"application/json":{"schema":{"type":"object","properties":{"endpoints":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"description":{"type":"string","nullable":true},"subscribed_events":{"type":"array","nullable":true,"items":{"type":"string"}},"active":{"type":"boolean"},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","url","description","subscribed_events","active","created_at","updated_at"]}}},"required":["endpoints"]}}}}}},"post":{"tags":["Webhooks"],"summary":"Register webhook endpoint","description":"Creates a new webhook endpoint. The secret is returned only once.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"description":{"type":"string"},"events":{"type":"array","items":{"type":"string","enum":["intervention.delivered","user.stage_changed","user.risk_changed","decision.recommended","outcome.recorded","webhook.test"]}}},"required":["url"]}}}},"responses":{"201":{"description":"Webhook endpoint created","content":{"application/json":{"schema":{"type":"object","properties":{"endpoint":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"secret":{"type":"string"},"description":{"type":"string","nullable":true},"subscribed_events":{"type":"array","nullable":true,"items":{"type":"string"}},"active":{"type":"boolean"},"created_at":{"type":"string"}},"required":["id","url","secret","description","subscribed_events","active","created_at"]}},"required":["endpoint"]}}}},"403":{"description":"Feature not available on current plan","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"upgrade_to":{"type":"string"}},"required":["error","upgrade_to"]}}}}}}},"/api/webhooks/endpoints/{id}":{"put":{"tags":["Webhooks"],"summary":"Update webhook endpoint","description":"Updates an existing webhook endpoint's URL, description, or subscribed events.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"description":{"type":"string"},"events":{"type":"array","nullable":true,"items":{"type":"string","enum":["intervention.delivered","user.stage_changed","user.risk_changed","decision.recommended","outcome.recorded","webhook.test"]}}}}}}},"responses":{"200":{"description":"Webhook endpoint updated","content":{"application/json":{"schema":{"type":"object","properties":{"endpoint":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"description":{"type":"string","nullable":true},"subscribed_events":{"type":"array","nullable":true,"items":{"type":"string"}},"active":{"type":"boolean"},"updated_at":{"type":"string"}},"required":["id","url","description","subscribed_events","active","updated_at"]}},"required":["endpoint"]}}}},"404":{"description":"Webhook endpoint not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"delete":{"tags":["Webhooks"],"summary":"Delete webhook endpoint","description":"Soft-deletes a webhook endpoint by setting active to false.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Webhook endpoint deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"description":"Webhook endpoint not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/webhooks/endpoints/{id}/test":{"post":{"tags":["Webhooks"],"summary":"Test webhook endpoint","description":"Sends a test payload to the webhook endpoint and returns the result.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Test result","content":{"application/json":{"schema":{"type":"object","properties":{"test":{"type":"object","properties":{"success":{"type":"boolean"},"status":{"type":"number"},"status_text":{"type":"string"},"error":{"type":"string"}},"required":["success"]}},"required":["test"]}}}},"404":{"description":"Webhook endpoint not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}}}}