{"openapi":"3.1.0","info":{"title":"WhatsApp Service API","version":"0.1.0","description":"Internal WhatsApp microservice API for CRM messaging. The service creates teacher + student/parent WhatsApp groups, ingests group messages into Supabase, and queues admin messages back to WhatsApp."},"servers":[{"url":"http://127.0.0.1:3100","description":"Local service"}],"security":[{"ApiKeyAuth":[]}],"tags":[{"name":"Health"},{"name":"Session"},{"name":"Groups"},{"name":"Messages"},{"name":"Participants"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"Health":{"type":"object","properties":{"ok":{"type":"boolean"},"db":{"type":"object","properties":{"ok":{"type":"boolean"},"missing":{"type":"array","items":{"type":"string"}},"error":{"type":"string"}}},"status":{"type":"string"},"hasQr":{"type":"boolean"},"agentJid":{"type":["string","null"]}},"required":["ok","db","status","hasQr"]},"SessionStatus":{"type":"object","properties":{"status":{"type":"string","examples":["disconnected","connecting","qr","open","reconnecting","logged_out"]},"hasQr":{"type":"boolean"},"agentJid":{"type":["string","null"],"examples":["380996119454@s.whatsapp.net"]}},"required":["status","hasQr"]},"GroupParticipantInput":{"type":"object","properties":{"phone":{"type":"string","description":"E.164 phone number","examples":["+380991112233"]},"role":{"type":"string","enum":["teacher","student","parent"]}},"required":["phone","role"]},"CreateGroupRequest":{"type":"object","properties":{"clientId":{"type":"string","description":"Platform booking/client/thread key"},"subject":{"type":"string","description":"WhatsApp group subject"},"participants":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/GroupParticipantInput"}}},"required":["clientId","subject","participants"]},"CreateGroupResponse":{"type":"object","properties":{"jid":{"type":"string","examples":["120363427045641776@g.us"]},"inviteLink":{"type":["string","null"]},"pendingInvites":{"type":"array","items":{"type":"string"}},"alreadyExisted":{"type":"boolean"}},"required":["jid","pendingInvites","alreadyExisted"]},"Group":{"type":"object","properties":{"jid":{"type":"string"},"client_id":{"type":"string"},"subject":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"last_message":{"type":["string","null"]},"last_message_type":{"type":["string","null"]},"last_sender_jid":{"type":["string","null"]},"last_sender_name":{"type":["string","null"]},"last_sender_avatar_url":{"type":["string","null"]},"last_sender_role":{"type":["string","null"]},"last_activity":{"type":["string","null"],"format":"date-time"}}},"Message":{"type":"object","properties":{"id":{"type":"integer"},"wa_message_id":{"type":"string"},"sender_jid":{"type":"string"},"sender_name":{"type":["string","null"]},"sender_avatar_url":{"type":["string","null"]},"sender_role":{"type":["string","null"]},"sender_participant_status":{"type":["string","null"]},"from_me":{"type":"boolean"},"type":{"type":"string","examples":["text","image","video","audio","document","system"]},"text":{"type":["string","null"]},"media_url":{"type":["string","null"]},"media_mime":{"type":["string","null"]},"status":{"type":"string"},"sent_at":{"type":"number","description":"Unix epoch seconds"}}},"Participant":{"type":"object","properties":{"jid":{"type":"string"},"lid_jid":{"type":["string","null"]},"role":{"type":"string","examples":["agent","teacher","student","parent","member"]},"status":{"type":"string","examples":["added","invited","left","removed"]},"display_name":{"type":["string","null"]},"profile_picture_url":{"type":["string","null"]},"updated_at":{"type":"string","format":"date-time"}}},"SendMessageRequest":{"type":"object","properties":{"text":{"type":"string"},"clientMessageId":{"type":"string","description":"Optional idempotency key alternative"}},"required":["text"]},"SendMessageResponse":{"type":"object","properties":{"outboxId":{"type":"integer"},"status":{"type":"string"},"attempts":{"type":"integer"},"waMessageId":{"type":["string","null"]}},"required":["outboxId","status","attempts"]}}},"paths":{"/health":{"get":{"tags":["Health"],"security":[],"summary":"Service and Supabase schema health","responses":{"200":{"description":"Healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"503":{"description":"Database/schema unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/session/status":{"get":{"tags":["Session"],"summary":"Get WhatsApp session status","responses":{"200":{"description":"Session status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionStatus"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/session/qr":{"get":{"tags":["Session"],"summary":"Get pairing QR PNG","responses":{"200":{"description":"PNG QR code","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Missing/invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No QR pending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/groups":{"get":{"tags":["Groups"],"summary":"List CRM groups","parameters":[{"name":"search","in":"query","schema":{"type":"string"},"description":"Matches subject by ILIKE or exact client_id"}],"responses":{"200":{"description":"Groups","content":{"application/json":{"schema":{"type":"object","properties":{"groups":{"type":"array","items":{"$ref":"#/components/schemas/Group"}}},"required":["groups"]}}}}}},"post":{"tags":["Groups"],"summary":"Create WhatsApp group as service account","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGroupRequest"}}}},"responses":{"201":{"description":"Created or existing active group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGroupResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"WhatsApp session not connected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/groups/{jid}/messages":{"get":{"tags":["Messages"],"summary":"Get chronological CRM thread","parameters":[{"name":"jid","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":200}},{"name":"before","in":"query","schema":{"type":"integer"},"description":"Return messages with local id lower than this cursor"}],"responses":{"200":{"description":"Messages","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"}}},"required":["messages"]}}}}}},"post":{"tags":["Messages"],"summary":"Queue admin message to WhatsApp","parameters":[{"name":"jid","in":"path","required":true,"schema":{"type":"string"}},{"name":"idempotency-key","in":"header","schema":{"type":"string","maxLength":120}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"}}}},"responses":{"202":{"description":"Queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/groups/{jid}/participants":{"get":{"tags":["Participants"],"summary":"List group participants","parameters":[{"name":"jid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Participants","content":{"application/json":{"schema":{"type":"object","properties":{"participants":{"type":"array","items":{"$ref":"#/components/schemas/Participant"}}},"required":["participants"]}}}},"404":{"description":"Unknown group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}