# ZupiChat API 1.0.0 — generated from the live contract
# Human documentation: https://www.zupichat.com/developers
openapi: 3.0.3
info:
  title: "ZupiChat API"
  version: 1.0.0
  description: "Send WhatsApp template messages, manage contacts and receive delivery events from your own application. ZupiChat is operated by Codelith Lab Private Limited and runs on the WhatsApp Cloud API."
  termsOfService: "https://www.zupichat.com/policy/terms-of-service"
  contact:
    name: "ZupiChat support"
    url: "https://www.zupichat.com/developers"
    email: "support@zupichat.com"
externalDocs:
  description: "Developer documentation"
  url: "https://www.zupichat.com/developers"
servers:
  -
    url: "https://www.zupichat.com/api/v1"
    description: Production
tags:
  -
    name: Account
    description: "Check a key and the number it is bound to"
  -
    name: Templates
    description: "Templates Meta has approved for this number"
  -
    name: Messages
    description: "Send a template and follow its delivery"
  -
    name: Contacts
    description: "Read the contact book, or push a lead into it"
security:
  -
    bearerAuth: []
  -
    apiKeyHeader: []
  -
    apiKeyQuery: []
paths:
  /me:
    get:
      tags:
        - Account
      summary: "Who this key belongs to"
      description: "Confirms the key works and tells you which WhatsApp number it is bound to. Useful as a health check right after a customer pastes their key into your product."
      operationId: getMe
      responses:
        "200":
          description: "The account behind the key."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Envelope"
              example:
                success: true
                message: OK
                data:
                  account:
                    id: 13
                    name: "Aditya Sonawane"
                    email: "owner@example.com"
                  whatsapp:
                    connected: true
                    display_name: "Zupi Chat"
                    phone_number: "918329608019"
                    phone_number_id: "1164348036759819"
        "401":
          description: "No key was sent, or the key is invalid or revoked."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
        "403":
          description: "The account behind the key is inactive."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
  /templates:
    get:
      tags:
        - Templates
      summary: "List approved templates"
      description: "Lists the message templates on the connected WhatsApp Business Account that Meta has approved, so you can show your users only what can actually be sent. Newest first, up to 200."
      operationId: listTemplates
      responses:
        "200":
          description: "Approved templates."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Envelope"
              example:
                success: true
                message: OK
                data:
                  templates:
                    -
                      id: 18
                      name: zupichat_new_offer
                      language: en_US
                      category:
                        id: 1
                        name: MARKETING
                        label: Marketing
                      status: APPROVED
        "401":
          description: "No key was sent, or the key is invalid or revoked."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
  /messages/template:
    post:
      tags:
        - Messages
      summary: "Send a template message"
      description: "Template messages are the only way to start a conversation outside WhatsApp's 24-hour window, which is why this is the endpoint most integrations need. The contact and the conversation are created on the first send, exactly as an inbound message would create them."
      operationId: sendTemplateMessage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/SendTemplateRequest"
            example:
              destination: "919876543210"
              template_name: order_confirmation_update
              name: Aditya
              body_variables:
                - Aditya
                - "#1042"
      responses:
        "200":
          description: "Handed to WhatsApp."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Envelope"
              example:
                success: true
                message: "Template message queued with WhatsApp."
                data:
                  message_id: 2340
                  whatsapp_message_id: wamid.HBgMOTE4MzI5NjA4MDE5FQIAERgSMEE4...
                  campaign_id: 61
                  conversation_id: 74
                  contact_id: 187
                  contact_created: false
                  status: sent
        "401":
          description: "No key was sent, or the key is invalid or revoked."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
        "404":
          description: "No approved template matches that name or id."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
        "409":
          description: "No WhatsApp number is connected, or the contact is blocked."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
        "422":
          description: "A field is missing or malformed."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
        "502":
          description: "WhatsApp rejected the send; the message repeats what Meta said."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
  "/messages/{id}":
    get:
      tags:
        - Messages
      summary: "Delivery status of one message"
      description: "Returns what WhatsApp last reported for that message — sent, delivered, read or failed. Poll it if you are not using webhooks."
      operationId: getMessageStatus
      parameters:
        -
          name: id
          in: path
          required: true
          description: "The message_id returned when you sent it."
          schema:
            type: integer
          example: 2340
      responses:
        "200":
          description: "Last known status."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Envelope"
              example:
                success: true
                message: OK
                data:
                  message_id: 2340
                  whatsapp_message_id: wamid.HBgMOTE4MzI5NjA4MDE5FQIAERgSMEE4...
                  status: delivered
                  sent_at: "2026-08-22T11:08:04+05:30"
        "401":
          description: "No key was sent, or the key is invalid or revoked."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
        "404":
          description: "The message does not belong to this account."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
  /contacts:
    get:
      tags:
        - Contacts
      summary: "List contacts"
      description: "Reads the contact book, newest first."
      operationId: listContacts
      parameters:
        -
          name: limit
          in: query
          description: "Rows per page, 200 maximum."
          schema:
            type: integer
            default: 50
            maximum: 200
          example: 50
        -
          name: page
          in: query
          description: "Page number, starting at 1."
          schema:
            type: integer
            default: 1
          example: 1
        -
          name: search
          in: query
          description: "Matches name or mobile number."
          schema:
            type: string
      responses:
        "200":
          description: "One page of contacts."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Envelope"
              example:
                success: true
                message: OK
                data:
                  contacts:
                    -
                      id: 187
                      name: "Aditya Sonawane"
                      mobile: "919876543210"
                      blocked: false
                      created: "2026-08-22T00:24:35+05:30"
                  pagination:
                    page: 1
                    pages: 2
                    total: 90
        "401":
          description: "No key was sent, or the key is invalid or revoked."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
    post:
      tags:
        - Contacts
      summary: "Create or update a contact"
      description: "Pushes a lead into the contact book the moment it appears in your own system. Matching is by mobile number, so calling this twice updates rather than duplicates."
      operationId: storeContact
      requestBody:
        required: true
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/StoreContactRequest"
            example:
              mobile: "919876543210"
              firstname: Aditya
              note: "Came from the pricing page"
      responses:
        "200":
          description: "Contact saved."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Envelope"
              example:
                success: true
                message: "Contact created."
                data:
                  contact_id: 189
                  created: true
                  name: "Aditya Sonawane"
                  mobile: "919876543210"
        "401":
          description: "No key was sent, or the key is invalid or revoked."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
        "422":
          description: "A field is missing or malformed."
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Error"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: "Authorization: Bearer zc_live_xxxxxxxxxxxxxxxx. A zc_test_ key runs the same endpoints in test mode: fixtures instead of live data, nothing sent to Meta, nothing billed."
    apiKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
      description: "Same key, for clients that cannot set an Authorization header."
    apiKeyQuery:
      type: apiKey
      in: query
      name: apiKey
      description: "Last resort for no-code tools. Prefer a header — query strings end up in logs."
  schemas:
    Envelope:
      type: object
      description: "Every response uses this shape, so a client can branch on one field."
      properties:
        success:
          type: boolean
        message:
          type: string
        data:
          type: object
      required:
        - success
        - message
    Error:
      type: object
      properties:
        success:
          type: boolean
          example: false
        message:
          type: string
          example: "Invalid or revoked API key."
        errors:
          type: object
          description: "Field-by-field detail, on a 422 only."
    SendTemplateRequest:
      type: object
      required:
        - destination
      properties:
        destination:
          type: string
          maxLength: 20
          description: "Recipient in international format, digits only, country code included."
          example: "919876543210"
        template_name:
          type: string
          maxLength: 120
          description: "Name of an approved template. Send this or template_id."
          example: order_confirmation_update
        template_id:
          type: integer
          description: "ZupiChat's own id for the template. Send this or template_name."
        name:
          type: string
          maxLength: 80
          description: "Contact name to save when this number is new to the account."
        campaign_name:
          type: string
          maxLength: 120
          description: "Groups the send under a standing API campaign in reports."
        body_variables:
          type: array
          items:
            type: string
          description: "Values for {{1}}, {{2}} … in body order."
        header_variables:
          type: array
          items:
            type: string
          description: "Values for a variable in the header, if the template has one."
    StoreContactRequest:
      type: object
      required:
        - mobile
      properties:
        mobile:
          type: string
          maxLength: 20
          description: "International format, country code included."
          example: "919876543210"
        firstname:
          type: string
          maxLength: 60
        lastname:
          type: string
          maxLength: 60
        note:
          type: string
          maxLength: 500
x-sandbox:
  description: "Test mode needs no WhatsApp Business Account. The public demo key below only simulates; send \"join zupi-demo\" on WhatsApp to the sandbox number and your test sends are delivered to that phone for real for 24 hours."
  demoKey: zc_demo_zupichat_public_sandbox
  joinNumber: "918329608019"
  docs: "https://www.zupichat.com/developers#testmode"
x-webhooks:
  message.received:
    post:
      summary: message.received
      description: "A customer sent a message to your WhatsApp number."
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/Envelope"
            example:
              event: message.received
              created_at: "2026-08-22T11:08:04+05:30"
              data:
                message_id: 2341
                conversation_id: 74
                contact_id: 187
                from: "919876543210"
                name: "Aditya Sonawane"
                type: text
                text: hi
      responses:
        "200":
          description: "Answer 2xx within 3 seconds or the delivery is logged as failed."
  message.sent:
    post:
      summary: message.sent
      description: "You sent a message, from the panel or through this API."
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/Envelope"
            example:
              event: message.sent
              created_at: "2026-08-22T11:08:04+05:30"
              data:
                message_id: 2340
                whatsapp_message_id: wamid.HBgMOTE4...
                conversation_id: 74
                contact_id: 187
                destination: "919876543210"
                template: order_confirmation_update
                source: api
      responses:
        "200":
          description: "Answer 2xx within 3 seconds or the delivery is logged as failed."
  message.status:
    post:
      summary: message.status
      description: "WhatsApp reported a message as sent, delivered, read or failed."
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/Envelope"
            example:
              event: message.status
              created_at: "2026-08-22T11:08:04+05:30"
              data:
                message_id: 2340
                whatsapp_message_id: wamid.HBgMOTE4...
                conversation_id: 74
                status: delivered
      responses:
        "200":
          description: "Answer 2xx within 3 seconds or the delivery is logged as failed."
  contact.created:
    post:
      summary: contact.created
      description: "A number reached the account for the first time."
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/Envelope"
            example:
              event: contact.created
              created_at: "2026-08-22T11:08:04+05:30"
              data:
                contact_id: 189
                mobile: "919876543210"
                name: "Aditya Sonawane"
      responses:
        "200":
          description: "Answer 2xx within 3 seconds or the delivery is logged as failed."
