openapi: 3.0.3
info:
  title: Cascade API
  version: 1.0.0
  description: |
    WhatsApp and Telegram OTP API. Get a Bearer token in the company cabinet (`/cabinet` → API keys).
    Docs: https://docs.cascade.kz
    API `message` strings are always Russian regardless of site language — branch on `error_code` / HTTP status.
servers:
  - url: https://cascade.kz/api
security:
  - bearerAuth: []
paths:
  /otp/send:
    post:
      summary: Send OTP
      operationId: sendOtp
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone]
              properties:
                phone:
                  type: string
                  example: "77001234567"
                purpose:
                  type: string
                  default: verification
                  example: verification
                channel:
                  type: string
                  enum: [whatsapp, telegram, sms, auto]
                  description: "Delivery channel. If omitted — company default_channel from the cabinet. A disabled channel returns 422."
                link:
                  type: string
                  format: uri
                  maxLength: 2048
                  description: "Optional http/https URL — shortened and appended to the same message as the code. One send = 1 token."
                  example: "https://example.com/confirm/abc"
                locale:
                  type: string
                  enum: [ru, kk, en]
                  default: ru
                  description: "OTP text language. Default ru. Templates: GET /otp/messages."
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            type: string
            maxLength: 255
          description: Retry with the same key and body returns the first response without a second send.
      responses:
        "200":
          description: OTP sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "OTP отправлен через WhatsApp"
                  expires_in:
                    type: integer
                    example: 300
                  otp_id:
                    type: integer
                    example: 1241
                  channel:
                    type: string
                    example: whatsapp
                  locale:
                    type: string
                    example: ru
                  credits_charged:
                    type: integer
                    example: 1
                  short_url:
                    type: string
                    description: Present when link was passed
                    example: "https://cascade.kz/s/Ab3xK9"
        "401":
          description: Invalid API token
        "422":
          description: Validation / balance / delivery channel error
  /otp/verify:
    post:
      summary: Verify OTP
      operationId: verifyOtp
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone, code]
              properties:
                phone:
                  type: string
                  example: "77001234567"
                code:
                  type: string
                  example: "123456"
                purpose:
                  type: string
                  default: verification
      responses:
        "200":
          description: Code confirmed
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "Номер подтверждён"
        "401":
          description: Invalid API token
        "422":
          description: Invalid or expired code
        "429":
          description: Too many attempts
  /otp/messages:
    get:
      summary: OTP message templates
      operationId: getOtpMessages
      parameters:
        - name: locale
          in: query
          schema:
            type: string
            enum: [ru, kk, en]
            default: ru
      responses:
        "200":
          description: Templates for the selected language
  /otp/status:
    get:
      summary: Latest OTP send status by phone
      operationId: getOtpStatus
      parameters:
        - name: phone
          in: query
          required: true
          schema:
            type: string
        - name: purpose
          in: query
          schema:
            type: string
            default: verification
      responses:
        "200":
          description: Status
  /otp/{otp}:
    get:
      summary: OTP status by id
      operationId: getOtpById
      parameters:
        - name: otp
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Found
        "404":
          description: No such send for this company
  /group/send:
    post:
      summary: Send a notification to a WhatsApp group
      operationId: sendGroupNotify
      description: |
        Sends a text message to a group. Pass a group name or id (`120363…@g.us`) in `group`.
        Auth — Bearer API token.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [group, message]
              properties:
                group:
                  type: string
                  description: Group name or WhatsApp group id
                  example: "OTP Alerts"
                message:
                  type: string
                  example: "Новая заявка #123 оплачена"
      responses:
        "200":
          description: Message sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                    example: "Сообщение отправлено в группу"
                  group_id:
                    type: string
                    example: "120363012345678901@g.us"
                  group_name:
                    type: string
                    nullable: true
                  message_id:
                    type: string
                    nullable: true
        "401":
          description: Invalid API token
        "404":
          description: Group not found
        "409":
          description: Several groups with a similar name — pass the id
        "422":
          description: Validation / send error
        "503":
          description: WhatsApp not connected
  /links/shorten:
    post:
      summary: Shorten a URL without sending
      operationId: shortenLink
      description: |
        Creates a short link `/s/{slug}` without sending a message.
        Free (no tokens charged).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url:
                  type: string
                  format: uri
                  maxLength: 2048
                  example: "https://example.com/very/long/path"
                alias:
                  type: string
                  pattern: '^[A-Za-z0-9_-]{3,32}$'
                  description: Custom slug (otherwise random)
                  example: "promo-may"
                title:
                  type: string
                  maxLength: 250
                expires_in:
                  type: integer
                  minimum: 60
                  maximum: 2592000
      responses:
        "200":
          description: Link created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  short_url:
                    type: string
                    example: "https://cascade.kz/s/promo-may"
                  slug:
                    type: string
                    example: "promo-may"
                  expires_at:
                    type: string
                    format: date-time
                    nullable: true
        "401":
          description: Invalid API token
        "422":
          description: Validation error / alias taken
  /links/send:
    post:
      summary: Shorten and send a link
      operationId: sendLink
      description: |
        Shortens a URL and sends a message to the phone via WhatsApp or Telegram.
        Charges 1 token for a delivered send. No OTP code is generated.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phone, url]
              properties:
                phone:
                  type: string
                  example: "77001234567"
                url:
                  type: string
                  format: uri
                  maxLength: 2048
                  example: "https://example.com/promo"
                channel:
                  type: string
                  enum: [whatsapp, telegram, sms, auto]
                  description: "If omitted — company default channel"
                text:
                  type: string
                  maxLength: 500
                  description: "Text with :link placeholder"
                  example: "Ваша скидка: :link"
                expires_in:
                  type: integer
                  minimum: 60
                  maximum: 2592000
      responses:
        "200":
          description: Link sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  message:
                    type: string
                  short_url:
                    type: string
                    example: "https://cascade.kz/s/Ab3xK9"
                  slug:
                    type: string
                    example: "Ab3xK9"
        "401":
          description: Invalid API token
        "422":
          description: Validation / balance / delivery channel error
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API token from the company cabinet
