openapi: 3.0.3
info:
  title: Relay — WhatsApp Gateway API
  version: "1.0.0"
  description: >
    Self-hostable WhatsApp API gateway. All /v1 endpoints require a Bearer API key.
    Not affiliated with, endorsed by, or connected to WhatsApp or Meta.
servers:
  - url: https://your-gateway.onrender.com
    description: Your Relay deployment
security:
  - bearerAuth: []
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
  schemas:
    SendResult:
      type: object
      properties:
        id: { type: string }
        to: { type: string }
        session: { type: string }
        status: { type: string, example: sent }
    Error:
      type: object
      properties:
        error: { type: string }
        message: { type: string }
        detail: { type: object, nullable: true }
paths:
  /health:
    get:
      summary: Health check (no auth)
      security: []
      responses: { "200": { description: OK } }
  /v1/sessions:
    get:
      summary: List sessions
      responses: { "200": { description: Session list } }
    post:
      summary: Create/resume a session (returns QR or pairing code)
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string }
                phoneNumber: { type: string, description: switches to pairing code }
                webhookUrl: { type: string }
      responses: { "201": { description: Session state } }
  /v1/sessions/{name}:
    get:
      summary: Session status (includes warmup block)
      parameters: [{ name: name, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Session state } }
    delete:
      summary: Logout + wipe credentials
      parameters: [{ name: name, in: path, required: true, schema: { type: string } }]
      responses: { "200": { description: Deleted } }
  /v1/messages/text:
    post:
      summary: Send text (supports quotedId to reply)
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [session, to, text]
              properties:
                session: { type: string }
                to: { type: string }
                text: { type: string }
                quotedId: { type: string }
      responses:
        "200": { description: Sent, content: { application/json: { schema: { $ref: "#/components/schemas/SendResult" } } } }
  /v1/messages/media:
    post:
      summary: Send image/video/document/sticker (url or base64; stickers auto-convert)
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [session, to, type]
              properties:
                session: { type: string }
                to: { type: string }
                type: { type: string, enum: [image, video, document, sticker] }
                url: { type: string }
                base64: { type: string }
                caption: { type: string }
                fileName: { type: string }
      responses: { "200": { description: Sent } }
  /v1/messages/audio:
    post:
      summary: Send a voice note (auto-converts to ogg/opus)
      responses: { "200": { description: Sent } }
  /v1/messages/buttons:
    post:
      summary: Interactive buttons (reply/url/call/copy)
      responses: { "200": { description: Sent } }
  /v1/messages/list:
    post:
      summary: Native list menu (selectable rows)
      responses: { "200": { description: Sent } }
  /v1/messages/poll:
    post: { summary: Send a poll, responses: { "200": { description: Sent } } }
  /v1/messages/menu:
    post: { summary: Numbered text menu, responses: { "200": { description: Sent } } }
  /v1/messages/location:
    post: { summary: Send a location pin, responses: { "200": { description: Sent } } }
  /v1/messages/contact:
    post: { summary: Send a contact card, responses: { "200": { description: Sent } } }
  /v1/messages/reaction:
    post: { summary: React to a message (emoji, or "" to remove), responses: { "200": { description: OK } } }
  /v1/messages/edit:
    post: { summary: Edit a sent message, responses: { "200": { description: OK } } }
  /v1/messages/delete:
    post: { summary: Delete/revoke a message, responses: { "200": { description: OK } } }
  /v1/messages/read:
    post: { summary: Mark a chat's inbound messages read, responses: { "200": { description: OK } } }
  /v1/presence:
    post: { summary: "Set presence (available/composing/recording/paused)", responses: { "200": { description: OK } } }
  /v1/media/{session}/{messageId}:
    get:
      summary: Download inbound media bytes
      parameters:
        - { name: session, in: path, required: true, schema: { type: string } }
        - { name: messageId, in: path, required: true, schema: { type: string } }
      responses: { "200": { description: Media bytes } }
  /v1/chats/{session}:
    get: { summary: Recent chats, responses: { "200": { description: Chat list } } }
  /v1/chats/{session}/{number}:
    get: { summary: Conversation history, responses: { "200": { description: Messages } } }
  /v1/groups/{session}:
    get: { summary: List groups, responses: { "200": { description: Groups } } }
    post: { summary: Create a group, responses: { "201": { description: Group } } }
  /v1/groups/{session}/{groupId}:
    get: { summary: Group metadata + participants, responses: { "200": { description: Group } } }
    delete: { summary: Leave group, responses: { "200": { description: OK } } }
  /v1/groups/{session}/{groupId}/participants:
    post: { summary: "add/remove/promote/demote participants", responses: { "200": { description: OK } } }
  /v1/profile/{session}:
    post: { summary: "Update own name/status/picture", responses: { "200": { description: OK } } }
  /v1/status/{session}:
    post: { summary: Post a status/story, responses: { "200": { description: OK } } }
  /v1/check/{number}:
    get: { summary: Is a number on WhatsApp?, responses: { "200": { description: Result } } }
  /v1/optout:
    get: { summary: List opted-out numbers, responses: { "200": { description: List } } }
    post: { summary: Add an opt-out, responses: { "201": { description: Added } } }
  /v1/campaigns:
    get: { summary: List drip campaigns, responses: { "200": { description: Campaigns } } }
    post: { summary: Create a paced campaign, responses: { "201": { description: Campaign } } }
  /v1/events:
    get: { summary: Recent gateway events (observability), responses: { "200": { description: Events } } }
