openapi: 3.1.0

info:
  title: OutreachAgent API
  version: "1.0"
  description: |
    OutreachAgent is a WhatsApp CRM and business communication platform for Indian businesses.
    
    ## Base URL
    All API requests go to: `https://api.crm.emacronai.com/v1`
    
    ## Authentication
    Use Bearer token authentication. Get your API key from **Settings → API Keys** in the dashboard.
    
    ```
    Authorization: Bearer YOUR_API_KEY
    ```
    
    ## Rate Limits
    - 100 requests/minute per API key
    - 1000 WhatsApp messages/minute per WABA number
    - OTP: 10 per phone number per hour
    
    ## Webhooks
    Configure webhooks at **Settings → Webhooks**. All events are sent as POST requests with HMAC-SHA256 signatures.
  contact:
    name: OutreachAgent Developer Support
    email: dev@emacronai.com
    url: https://crm.emacronai.com/developers
  license:
    name: Proprietary
    url: https://crm.emacronai.com/terms

servers:
  - url: https://api.crm.emacronai.com/v1
    description: Production

security:
  - BearerAuth: []

tags:
  - name: OTP
    description: WhatsApp OTP / verification API
  - name: WhatsApp
    description: WhatsApp message sending and broadcasts
  - name: Contacts
    description: Contact management
  - name: Conversations
    description: Conversation and inbox management
  - name: Campaigns
    description: Broadcast and email campaigns

paths:

  # ── OTP ──────────────────────────────────────────────────────────────────────

  /otp/send:
    post:
      tags: [OTP]
      summary: Send WhatsApp OTP
      description: |
        Sends a one-time password to a phone number via WhatsApp.
        The OTP is generated server-side and sent automatically.
        Returns a `request_id` needed to verify the OTP.
      operationId: sendOtp
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone]
              properties:
                phone:
                  type: string
                  description: Phone number in E.164 format
                  example: "+919876543210"
                expiry:
                  type: integer
                  description: OTP validity in seconds (default 300)
                  default: 300
                  minimum: 60
                  maximum: 900
                length:
                  type: integer
                  description: OTP length (4–8 digits)
                  default: 6
                  minimum: 4
                  maximum: 8
      responses:
        "200":
          description: OTP sent successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  request_id:
                    type: string
                    description: Use this ID to verify the OTP
                    example: "otp_abc123xyz"
                  expires_at:
                    type: string
                    format: date-time
                    example: "2026-09-01T10:15:00Z"
              example:
                request_id: "otp_abc123xyz"
                expires_at: "2026-09-01T10:15:00Z"
        "400":
          $ref: "#/components/responses/BadRequest"
        "429":
          $ref: "#/components/responses/RateLimited"

  /otp/verify:
    post:
      tags: [OTP]
      summary: Verify OTP
      description: Verifies the OTP entered by the user. Returns `verified: true` on success.
      operationId: verifyOtp
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [request_id, otp]
              properties:
                request_id:
                  type: string
                  description: The request_id returned by /otp/send
                  example: "otp_abc123xyz"
                otp:
                  type: string
                  description: The OTP entered by the user
                  example: "482916"
      responses:
        "200":
          description: Verification result
          content:
            application/json:
              schema:
                type: object
                properties:
                  verified:
                    type: boolean
                  message:
                    type: string
              examples:
                success:
                  value: { verified: true, message: "OTP verified successfully" }
                failure:
                  value: { verified: false, message: "Invalid or expired OTP" }
        "400":
          $ref: "#/components/responses/BadRequest"

  # ── WhatsApp ─────────────────────────────────────────────────────────────────

  /whatsapp/send:
    post:
      tags: [WhatsApp]
      summary: Send WhatsApp message
      description: Send a template or free-form WhatsApp message to a phone number.
      operationId: sendWhatsappMessage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [to, type]
              properties:
                to:
                  type: string
                  description: Recipient phone in E.164 format
                  example: "+919876543210"
                type:
                  type: string
                  enum: [template, text]
                  description: Message type
                template:
                  type: object
                  description: Required when type is 'template'
                  properties:
                    name:
                      type: string
                      example: "order_confirmation"
                    language:
                      type: string
                      example: "en"
                    components:
                      type: array
                      items:
                        type: object
                text:
                  type: string
                  description: Free-form text (only in 24h conversation window)
      responses:
        "200":
          description: Message sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  message_id:
                    type: string
                  status:
                    type: string
                    enum: [sent, queued, failed]
        "400":
          $ref: "#/components/responses/BadRequest"

  /whatsapp/broadcasts:
    post:
      tags: [WhatsApp]
      summary: Create broadcast campaign
      description: Send a WhatsApp template message to multiple contacts at once.
      operationId: createBroadcast
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, template_name, recipients]
              properties:
                name:
                  type: string
                  description: Internal broadcast name
                  example: "Diwali sale blast"
                template_name:
                  type: string
                  example: "diwali_offer_v2"
                language:
                  type: string
                  default: "en"
                recipients:
                  type: array
                  description: List of phone numbers in E.164 format
                  items:
                    type: string
                  example: ["+919876543210", "+918765432109"]
                schedule_at:
                  type: string
                  format: date-time
                  description: Schedule for later (omit for immediate send)
      responses:
        "200":
          description: Broadcast created
          content:
            application/json:
              schema:
                type: object
                properties:
                  broadcast_id:
                    type: string
                  status:
                    type: string
                    enum: [queued, scheduled, sending]
                  total_recipients:
                    type: integer

  # ── Contacts ─────────────────────────────────────────────────────────────────

  /contacts:
    get:
      tags: [Contacts]
      summary: List contacts
      operationId: listContacts
      parameters:
        - in: query
          name: page
          schema: { type: integer, default: 1 }
        - in: query
          name: limit
          schema: { type: integer, default: 50, maximum: 200 }
        - in: query
          name: search
          schema: { type: string }
          description: Search by name, phone, or email
      responses:
        "200":
          description: Paginated contact list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Contact"
                  total:
                    type: integer
                  page:
                    type: integer

    post:
      tags: [Contacts]
      summary: Create contact
      operationId: createContact
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ContactInput"
      responses:
        "201":
          description: Contact created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Contact"

  /contacts/{id}:
    get:
      tags: [Contacts]
      summary: Get contact
      operationId: getContact
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Contact details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Contact"
        "404":
          $ref: "#/components/responses/NotFound"

    patch:
      tags: [Contacts]
      summary: Update contact
      operationId: updateContact
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ContactInput"
      responses:
        "200":
          description: Updated contact
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Contact"

  # ── Conversations ─────────────────────────────────────────────────────────────

  /conversations:
    get:
      tags: [Conversations]
      summary: List conversations
      operationId: listConversations
      parameters:
        - in: query
          name: status
          schema:
            type: string
            enum: [open, resolved, pending]
        - in: query
          name: assigned_to
          schema: { type: string }
          description: Agent user ID
        - in: query
          name: page
          schema: { type: integer, default: 1 }
      responses:
        "200":
          description: Conversation list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Conversation"
                  total:
                    type: integer

# ── Components ────────────────────────────────────────────────────────────────

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: Get your API key from Settings → API Keys

  schemas:
    Contact:
      type: object
      properties:
        id:
          type: string
          example: "cnt_abc123"
        name:
          type: string
          example: "Priya Sharma"
        phone:
          type: string
          example: "+919876543210"
        email:
          type: string
          example: "priya@example.com"
        tags:
          type: array
          items: { type: string }
          example: ["lead", "bangalore"]
        custom_fields:
          type: object
          additionalProperties: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    ContactInput:
      type: object
      required: [name]
      properties:
        name:
          type: string
          example: "Priya Sharma"
        phone:
          type: string
          example: "+919876543210"
        email:
          type: string
          example: "priya@example.com"
        tags:
          type: array
          items: { type: string }
        custom_fields:
          type: object
          additionalProperties: true

    Conversation:
      type: object
      properties:
        id:
          type: string
          example: "conv_xyz789"
        contact:
          $ref: "#/components/schemas/Contact"
        status:
          type: string
          enum: [open, resolved, pending]
        channel:
          type: string
          enum: [whatsapp, email]
        assigned_to:
          type: string
          description: Agent user ID
        last_message:
          type: string
        last_message_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time

    Error:
      type: object
      properties:
        error:
          type: string
        message:
          type: string
        code:
          type: string

  responses:
    BadRequest:
      description: Bad request — check your request body
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: "bad_request"
            message: "phone is required"
            code: "VALIDATION_ERROR"
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    RateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error: "rate_limited"
            message: "Too many OTP requests for this phone number. Try again in 3600 seconds."
            code: "RATE_LIMIT_EXCEEDED"
