openapi: 3.0.0
info:
  title: MyOTPApp API
  version: 1.2.0
  description: >
    Send and verify one-time passcodes over SMS, WhatsApp and Telegram from a
    single API key. Phone numbers are digits only, in E.164 order, with no
    leading '+' and no trailing national prefix.


    Delivery webhooks: register a callback URL against an API key in your
    dashboard and MyOTP will POST to it when a message reaches a terminal state,
    so you do not have to poll /check_otp_status. Events are otp.delivered,
    otp.sent, otp.read and otp.failed, on all three channels. The body is the
    DeliveryEvent schema below. Every request is signed with Standard Webhooks
    v1 (standardwebhooks.com), so you can verify it with any off-the-shelf
    library: the signed content is "{webhook-id}.{webhook-timestamp}.{body}",
    HMAC-SHA256 with your base64-decoded signing secret, sent in the
    webhook-signature header as "v1,<base64>". Non-2xx responses are retried
    5 times at 5, 10, 20, 40 and 60 seconds. Endpoints must be https.
servers:
  - url: https://api.myotp.app
    description: Production
paths:
  /generate_otp:
    post:
      summary: Generate OTP
      description: Generate a One Time Password (OTP) and send it to the specified phone number, with optional branding.
      operationId: generate_otp_endpoint
      parameters:
        - name: X-API-Key
          in: header
          required: true
          schema:
            type: string
            pattern: "^[a-zA-Z0-9_-]{32}$"
          description: API key for authentication.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                phone_number:
                  type: string
                  default: 19876543210
                  pattern: "^[1-9][0-9]{6,14}$"
                  description: Phone number to send the OTP to. E.164 format. Include country code without any prefixes.
                otp_length:
                  type: integer
                  default: 6
                  minimum: 4
                  maximum: 8
                  description: Length of the OTP, defaults to 6.
                otp_validity:
                  type: integer
                  default: 300
                  minimum: 60
                  maximum: 86400
                  description: Validity of the OTP in seconds, defaults to 300. SMS only.
                channel:
                  type: string
                  enum:
                    - sms
                    - whatsapp
                    - telegram
                  default: sms
                  description: >
                    Delivery channel for the OTP. Defaults to 'sms'.
                    For WhatsApp, ensure the destination supports WhatsApp Business messaging.
                    Telegram is delivered through the Telegram Gateway API and is charged
                    at a flat rate in every country.
                template_order:
                  type: integer
                  default: 1
                  minimum: 1
                  maximum: 5
                  description: Order of the template to be used for the OTP message, defaults to 1 (12 for WhatsApp).
                force_send:
                  type: boolean
                  default: false
                  description: If true, forces the sending of OTP even if there is an existing, unexpired OTP for the given phone number.
                return_otp:
                  type: boolean
                  default: false
                  description: >
                    If true, includes the OTP value in the response.
                    CAUTION: this is not compliant with regulations in many markets.
                brand:
                  type: string
                  default: MyOTP.App
                  pattern: "^[a-zA-Z0-9.]{3,16}$"
                  description: >
                    Optional brand name for personalizing the OTP message. 3 to 16 characters,
                    letters, digits and dots. Brand names are screened against a denylist to
                    prevent impersonation.
                    CAUTION: This overrides the account and app defaults.
              required:
                - phone_number
      responses:
        '200':
          description: OTP successfully generated and sent.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message_id:
                    type: string
                    description: ID of the message sent.
                  status:
                    type: string
                    description: Status of the request.
                  message:
                    type: string
                    description: Message describing the status of the request.
                  date_sent:
                    type: string
                    format: date-time
                    description: Date and time the OTP was sent.
                  expires_at:
                    type: string
                    format: date-time
                    description: Date and time the OTP expires.
                  cost:
                    type: number
                    format: float
                    description: Cost of sending the OTP.
                  otp:
                    type: string
                    description: The OTP value, included if return_otp is true.
        '400':
          description: Bad request due to invalid input parameters.
        '401':
          description: Unauthorized due to missing or invalid API key.
        '402':
          description: Low balance or payment is required.
        '409':
          description: Conflict, OTP already active.
        '500':
          description: Internal Server Error

  /verify_otp:
    post:
      summary: Verify OTP
      description: Validate the OTP for successful verification
      operationId: verify_otp_endpoint
      parameters:
        - name: X-API-Key
          in: header
          required: true
          schema:
            type: string
            pattern: "^[a-zA-Z0-9_-]{32}$"
          description: API key for authentication
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                phone_number:
                  type: string
                  pattern: "^[1-9][0-9]{6,14}$"
                  description: Phone number to verify the OTP
                message_id:
                  type: string
                  description: ID of the message to verify
                otp:
                  type: string
                  pattern: "^[0-9]{3,8}$"
                  description: OTP to verify
              required:
                - otp
      responses:
        '200':
          description: OTP verification result
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - success
                      - failed
                      - expired
                    description: Status of the OTP verification
                  message:
                    type: string
                    description: Message describing the status of the OTP verification
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '500':
          description: Internal Server Error
  /extend_otp:
    post:
      summary: Extend OTP Expiry
      description: Extend an existing OTP expiry time
      operationId: extend_otp_endpoint
      parameters:
        - name: X-API-Key
          in: header
          required: true
          schema:
            type: string
            pattern: "^[a-zA-Z0-9_-]{32}$"
          description: API key for authentication
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                message_id:
                  type: string
                  description: Message ID of the OTP to be extended
                duration:
                  type: integer
                  minimum: 60
                  maximum: 14400
                  default: 300
                  description: Duration to extend the OTP expiry, in seconds
              required:
                - message_id
                - duration
      responses:
        '200':
          description: OTP expiry successfully extended
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    description: Status of the request
                  message:
                    type: string
                    description: Message describing the status of the request
                  expires_at:
                    type: string
                    format: date-time
                    description: New expiry time of the OTP
        '400':
          description: Bad request
        '401':
          description: Unauthorized
        '404':
          description: OTP message_id not found
  /report:
    post:
      summary: Get Transactions Report
      description: Returns transactions within a specified date range. If `start_date` and `end_date` are omitted, the report will include the last 7 days of history. Supports pagination.
      operationId: report_endpoint
      parameters:
        - name: X-API-Key
          in: header
          required: true
          schema:
            type: string
            pattern: "^[a-zA-Z0-9_-]{32}$"
          description: API key for authentication
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                start_date:
                  type: string
                  format: date
                  description: Start date of the report in YYYY-MM-DD format (optional)
                  default: "7 days ago"
                end_date:
                  type: string
                  format: date
                  description: End date of the report in YYYY-MM-DD format (optional)
                  default: "current day"
                page:
                  type: integer
                  format: int32
                  minimum: 1
                  description: The page number to fetch (optional)
                  default: 1
                per_page:
                  type: integer
                  format: int32
                  minimum: 1
                  maximum: 100
                  description: The number of items per page (optional)
                  default: 10
      responses:
        '200':
          description: Transactions report successfully retrieved
          content:
            application/json:
              schema:
                type: object
                properties:
                  transactions:
                    type: array
                    items:
                      type: object
                      properties:
                        message_id:
                          type: string
                        phone_number:
                          type: string
                        cost:
                          type: number
                        timestamp:
                          type: string
                          format: date-time
                        message_type:
                          type: integer
                          description: Indicates whether this was to send an OTP or to Validate one
                        description:
                          type: string
                        force_send:
                          type: boolean
                          description: Indicates whether the transaction was force sent or not
                        client_ip:
                          type: string
                        application:
                          type: string
                        created_at:
                          type: string
                          format: date-time
        '400':
          description: Bad request
        '401':
          description: Unauthorized
  /check_otp_status:
    post:
      summary: Check message_id Status
      description: Check delivery report and if a sent OTP is still active or expired based on its message ID.
      parameters:
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - message_id
              properties:
                message_id:
                  type: string
                  description: The message ID of the OTP.
      responses:
        200:
          description: Successful response with the validity and expiry time of the OTP.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  is_valid:
                    type: boolean
                  expires_at:
                    type: string
                    format: date-time
        400:
          description: Bad request
        401:
          description: Unauthorized
        404:
          description: OTP message_id not found

  /me:
    get:
      summary: Account Identity
      description: Returns the account the supplied API key belongs to. Useful as an integration smoke test, since it exercises both key validation and the IP allowlist.
      operationId: me_endpoint
      parameters:
        - name: X-API-Key
          in: header
          required: true
          schema:
            type: string
            pattern: "^[a-zA-Z0-9_-]{32}$"
          description: API key for authentication
      responses:
        '200':
          description: The account owning this API key.
          content:
            application/json:
              schema:
                type: object
                properties:
                  email:
                    type: string
                    format: email
        '401':
          description: Unauthorized due to missing or invalid API key.
        '403':
          description: Calling IP address is not on this key's allowlist.

components:
  schemas:
    DeliveryEvent:
      type: object
      description: >
        The JSON body POSTed to your registered webhook URL when a message reaches
        a terminal state. Sent with headers webhook-id, webhook-timestamp and
        webhook-signature.
      properties:
        event:
          type: string
          enum: [otp.delivered, otp.sent, otp.read, otp.failed]
          description: >
            All failures collapse to otp.failed; the specific cause is in `reason`,
            so subscribing to otp.failed keeps working as new failure codes appear.
        message_id:
          type: string
          description: The message_id returned by /generate_otp.
        status:
          type: string
          description: The underlying transaction status, e.g. delivered, read, failed.NoBalance.
        channel:
          type: string
          enum: [sms, whatsapp, telegram]
        phone_number:
          type: string
        country:
          type: string
        occurred_at:
          type: string
          format: date-time
        reason:
          type: string
          description: Present only on otp.failed, e.g. NoBalance, SMPP, mWebAPI.
      required: [event, message_id, status, occurred_at]
      example:
        event: otp.delivered
        message_id: 8f14e45f-ea0d-4d1b-9c6a-2b7c1f0a9e33
        status: delivered
        channel: sms
        phone_number: "19876543210"
        country: USA
        occurred_at: "2026-09-02T03:02:01Z"
