{"openapi":"3.1.0","info":{"title":"wa-bot API","version":"1.0.0","description":"Multi-tenant WhatsApp service. Two auth schemes:\n\n- **DeviceToken** (`wabot_…`) — used for sending and reading state of a single device.\n- **AdminApiKey** (`wabotk_…`) — used by external apps to manage devices programmatically.\n","contact":{"name":"wa-bot"}},"servers":[{"url":"https://whatsapp.super-coding.com"}],"tags":[{"name":"health","description":"Public uptime endpoints"},{"name":"public","description":"Public utilities (short-link redirects, unsubscribe) — no auth"},{"name":"send","description":"Send messages (text, media, polls, location, voice)"},{"name":"broadcast","description":"Bulk + scheduled sending"},{"name":"jobs","description":"Track and cancel async jobs"},{"name":"inbox","description":"Lookup, message ops (react/edit/delete), groups"},{"name":"admin","description":"Admin API for device CRUD"}],"components":{"securitySchemes":{"DeviceToken":{"type":"http","scheme":"bearer","bearerFormat":"wabot_…"},"AdminApiKey":{"type":"http","scheme":"bearer","bearerFormat":"wabotk_…"}},"schemas":{"Error":{"type":"object","properties":{"ok":{"type":"boolean","example":false},"error":{"type":"string"}}},"SendResult":{"type":"object","properties":{"ok":{"type":"boolean","example":true},"jid":{"type":"string","example":"201001234567@s.whatsapp.net"},"id":{"type":"string","example":"3EB0..."}}},"Device":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"phone":{"type":"string","nullable":true},"status":{"type":"string","enum":["disconnected","connecting","qr","connected"]},"embedUrl":{"type":"string","nullable":true}}}}},"paths":{"/api/health":{"get":{"tags":["health"],"summary":"System health (no auth)","responses":{"200":{"description":"OK"}}}},"/r/{slug}":{"get":{"tags":["public"],"summary":"Short-link redirect + click tracking (no auth)","description":"Redirects to the target URL stored for `slug` and records an entry in `short_link_clicks` (IP, User-Agent, referrer). Create slugs from the dashboard at `/dashboard/devices/:id/links` — no public API for managing them yet.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9]{7}$"},"description":"7-char base62 slug"}],"responses":{"302":{"description":"Redirect to the configured target URL"},"404":{"description":"Slug unknown or link disabled"}}}},"/unsubscribe":{"get":{"tags":["public"],"summary":"Contact unsubscribe page (no auth)","description":"Public page that lets a recipient confirm opt-out using a per-contact `unsubscribe_token`. Embed the link `/unsubscribe?token={token}` in broadcast footers for compliance.","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Confirmation page (or done page if already opted out)"}}}},"/api/status":{"get":{"tags":["inbox"],"summary":"Status of the device behind this token","security":[{"DeviceToken":[]}],"responses":{"200":{"description":"Device status + stats"},"401":{"description":"Invalid token"}}}},"/api/send-text":{"post":{"tags":["send"],"summary":"Send a text message (text or templateId)","security":[{"DeviceToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","example":"201001234567"},"text":{"type":"string"},"templateId":{"type":"string","format":"uuid"},"variables":{"type":"object","additionalProperties":{"type":"string"}}},"required":["to"]}}}},"responses":{"200":{"description":"Sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendResult"}}}},"400":{"description":"Bad input"},"401":{"description":"Invalid token"}}}},"/api/send-media":{"post":{"tags":["send"],"summary":"Send media (image / video / audio / document) — multipart","security":[{"DeviceToken":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"to":{"type":"string"},"file":{"type":"string","format":"binary"},"caption":{"type":"string"}},"required":["to","file"]}}}},"responses":{"200":{"description":"Sent"},"400":{"description":"Bad input"}}}},"/api/send-poll":{"post":{"tags":["send"],"summary":"Send an interactive poll","security":[{"DeviceToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string"},"name":{"type":"string","description":"Poll question"},"options":{"type":"array","items":{"type":"string"},"minItems":2},"selectableCount":{"type":"integer","default":1}},"required":["to","name","options"]}}}},"responses":{"200":{"description":"Sent"}}}},"/api/send-location":{"post":{"tags":["send"],"summary":"Send a location pin","security":[{"DeviceToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"},"name":{"type":"string"},"address":{"type":"string"}},"required":["to","latitude","longitude"]}}}},"responses":{"200":{"description":"Sent"}}}},"/api/send-voice":{"post":{"tags":["send"],"summary":"Send a voice note (PTT) — multipart audio","security":[{"DeviceToken":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"to":{"type":"string"},"file":{"type":"string","format":"binary"}},"required":["to","file"]}}}},"responses":{"200":{"description":"Sent"}}}},"/api/check-number":{"post":{"tags":["inbox"],"summary":"Check whether numbers have WhatsApp","security":[{"DeviceToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","description":"Single number"},"numbers":{"type":"array","items":{"type":"string"},"description":"Up to 50"}}}}}},"responses":{"200":{"description":"Results array"}}}},"/api/groups":{"get":{"tags":["inbox"],"summary":"List groups the device is a member of","security":[{"DeviceToken":[]}],"responses":{"200":{"description":"Groups list"}}}},"/api/broadcast":{"post":{"tags":["broadcast"],"summary":"Async broadcast (returns 202 jobId)","security":[{"DeviceToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"recipients":{"oneOf":[{"type":"array","items":{"type":"string"}},{"type":"array","items":{"type":"object","properties":{"to":{"type":"string"},"variables":{"type":"object"}}}}]},"text":{"type":"string"},"templateId":{"type":"string"},"delayMs":{"type":"integer"}},"required":["recipients"]}}}},"responses":{"202":{"description":"Accepted, returns jobId + statusUrl"}}}},"/api/schedule-text":{"post":{"tags":["broadcast"],"summary":"Schedule a text message for the future","security":[{"DeviceToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string"},"text":{"type":"string"},"templateId":{"type":"string"},"variables":{"type":"object"},"scheduledAt":{"type":"string","description":"ISO datetime or unix seconds"}},"required":["to","scheduledAt"]}}}},"responses":{"202":{"description":"Scheduled"}}}},"/api/jobs/{id}":{"get":{"tags":["jobs"],"summary":"Get job status (broadcast → includes progress)","security":[{"DeviceToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Job details"},"404":{"description":"Not found"}}},"delete":{"tags":["jobs"],"summary":"Cancel a job (and its children)","security":[{"DeviceToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Cancelled"}}}},"/api/jobs":{"get":{"tags":["jobs"],"summary":"List recent jobs for the device","security":[{"DeviceToken":[]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["pending","running","completed","dead","cancelled"]}},{"name":"limit","in":"query","schema":{"type":"integer","default":50}}],"responses":{"200":{"description":"Jobs array"}}}},"/api/react-message":{"post":{"tags":["inbox"],"summary":"React to a message with an emoji (\"\" to remove)","security":[{"DeviceToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messageId":{"type":"string"},"emoji":{"type":"string"}},"required":["messageId"]}}}},"responses":{"200":{"description":"OK"}}}},"/api/edit-message":{"post":{"tags":["inbox"],"summary":"Edit a message we sent (within ~15 minutes)","security":[{"DeviceToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messageId":{"type":"string"},"text":{"type":"string"}},"required":["messageId","text"]}}}},"responses":{"200":{"description":"OK"}}}},"/api/delete-message":{"post":{"tags":["inbox"],"summary":"Delete a message (revoke for everyone)","security":[{"DeviceToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messageId":{"type":"string"},"forEveryone":{"type":"boolean","default":true}},"required":["messageId"]}}}},"responses":{"200":{"description":"OK"}}}},"/api/admin/devices":{"post":{"tags":["admin"],"summary":"Create a device + receive its token + embedUrl","security":[{"AdminApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"notes":{"type":"string"},"webhookUrl":{"type":"string"},"events":{"type":"array","items":{"type":"string"}}},"required":["name"]}}}},"responses":{"201":{"description":"Created — returns device + token + embedSecret"}}},"get":{"tags":["admin"],"summary":"List all devices","security":[{"AdminApiKey":[]}],"responses":{"200":{"description":"Devices"}}}},"/api/admin/devices/{id}":{"get":{"tags":["admin"],"summary":"Get a device","security":[{"AdminApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Device"},"404":{"description":"Not found"}}},"patch":{"tags":["admin"],"summary":"Update name/notes/isEnabled","security":[{"AdminApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Updated"}}},"delete":{"tags":["admin"],"summary":"Delete the device","security":[{"AdminApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted"}}}},"/api/admin/devices/{id}/qr":{"get":{"tags":["admin"],"summary":"Get current QR (data URL)","security":[{"AdminApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"QR"},"404":{"description":"Not found"}}}},"/api/admin/devices/{id}/qr.png":{"get":{"tags":["admin"],"summary":"Get current QR as PNG bytes","security":[{"AdminApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"image/png"}}}},"/api/admin/devices/{id}/logout":{"post":{"tags":["admin"],"summary":"Force a fresh QR (logout)","security":[{"AdminApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/admin/devices/{id}/regenerate-token":{"post":{"tags":["admin"],"summary":"Issue a fresh device token","security":[{"AdminApiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"New token"}}}}}}