openapi: 3.0.3
info:
  title: EzNumber API
  version: "1.0.0"
  description: >
    Official public EzNumber API for temporary and permanent number orders.
    Public responses use only product/group/server codes and do not expose internal provider names or internal flow fields.
servers:
  - url: https://bot.eznumbersstore.com/api/v1
security:
  - bearerAuth: []
tags:
  - name: Public
  - name: Docs
  - name: Account
  - name: Servers
  - name: Services
  - name: Purchase
  - name: Orders
paths:
  /ping:
    get:
      tags: [Public]
      summary: Health check
      security: []
      responses:
        "200":
          description: Service is reachable.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PingResponse"
              examples:
                ok:
                  value:
                    ok: true
                    service: eznumber-api
                    version: v1
                    time: "2026-07-12T10:00:00.000Z"
  /docs:
    get:
      tags: [Docs]
      summary: Human-readable documentation site
      security: []
      responses:
        "200":
          description: HTML documentation.
          content:
            text/html:
              schema:
                type: string
  /docs/download:
    get:
      tags: [Docs]
      summary: Download Markdown user guide
      security: []
      parameters:
        - name: lang
          in: query
          schema:
            type: string
            enum: [ar, en]
            default: ar
      responses:
        "200":
          description: Markdown guide.
          content:
            text/markdown:
              schema:
                type: string
  /docs/openapi.yaml:
    get:
      tags: [Docs]
      summary: Download this OpenAPI document
      security: []
      responses:
        "200":
          description: OpenAPI YAML.
          content:
            application/yaml:
              schema:
                type: string
  /me:
    get:
      tags: [Account]
      summary: Read current API client
      description: Requires any valid Bearer API key.
      responses:
        "200":
          description: Current API client public details.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeResponse"
              examples:
                ok:
                  value:
                    ok: true
                    client:
                      id: "64f000000000000000000001"
                      name: Example Client
                      status: ACTIVE
                      allowed_product_types: [temporary, permanent]
                      allowed_servers:
                        - temporary.american.server1
                        - temporary.american.server2
                        - permanent.american.server1
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /balance:
    get:
      tags: [Account]
      summary: Read API wallet balance
      description: Requires scope `balance:read`. This is the API wallet, not the Telegram wallet.
      responses:
        "200":
          description: API wallet balance.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BalanceResponse"
              examples:
                ok:
                  value:
                    ok: true
                    balance: 25.5
                    currency: USD
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/ApiError"
  /temporary/servers:
    get:
      tags: [Servers]
      summary: List allowed temporary servers
      description: Requires scope `servers:read`. Returns only servers allowed for the current client.
      responses:
        "200":
          description: Allowed temporary server groups.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServerGroupsResponse"
              examples:
                ok:
                  value:
                    ok: true
                    type: temporary
                    groups:
                      - group_code: american
                        name_ar: السيرفر الأمريكي
                        name_en: American Server
                        servers:
                          - server_code: server1
                            name_ar: سيرفر 1
                            name_en: Server 1
                            available: true
                            country_mode: fixed
                            supported_countries: [US]
                            countries:
                              - code: US
                                name_ar: الولايات المتحدة
                                name_en: United States
                                available: true
                            auto_refund_on_no_code: true
                            no_code_timeout_seconds: 300
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /permanent/servers:
    get:
      tags: [Servers]
      summary: List allowed permanent servers
      description: Requires scope `servers:read`.
      responses:
        "200":
          description: Allowed permanent server groups.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServerGroupsResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /temporary/{group}/{server}/countries:
    get:
      tags: [Servers]
      summary: List countries for a temporary server
      description: Requires scope `servers:read`. Dynamic global country discovery requires a `service` query.
      parameters:
        - $ref: "#/components/parameters/Group"
        - $ref: "#/components/parameters/Server"
        - name: service
          in: query
          required: false
          schema:
            type: string
          description: Required for dynamic global country discovery.
        - $ref: "#/components/parameters/Search"
      responses:
        "200":
          description: Countries.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CountryListResponse"
              examples:
                dynamic:
                  value:
                    ok: true
                    type: temporary
                    group: global
                    server: global_server1
                    country_mode: dynamic
                    countries:
                      - code: GB
                        name_ar: المملكة المتحدة
                        name_en: United Kingdom
                        available: true
                        count: 12
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/ApiError"
        "503":
          $ref: "#/components/responses/ApiError"
  /permanent/{group}/{server}/countries:
    get:
      tags: [Servers]
      summary: List countries for a permanent server
      description: Requires scope `servers:read`.
      parameters:
        - $ref: "#/components/parameters/Group"
        - $ref: "#/components/parameters/Server"
        - $ref: "#/components/parameters/Search"
      responses:
        "200":
          description: Countries.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CountryListResponse"
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/ApiError"
  /temporary/{group}/{server}/services:
    get:
      tags: [Services]
      summary: List temporary services
      description: Requires scope `servers:read`. This endpoint does not reserve a number or debit balance.
      parameters:
        - $ref: "#/components/parameters/Group"
        - $ref: "#/components/parameters/Server"
        - $ref: "#/components/parameters/Search"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Country"
        - name: service
          in: query
          schema:
            type: string
          description: Optional helper used by dynamic validation.
      responses:
        "200":
          description: Public services.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceListResponse"
              examples:
                ok:
                  value:
                    ok: true
                    type: temporary
                    group: american
                    server: server2
                    services:
                      - service: example-mail
                        name: Example Mail
                        price: 0.62
                        country: US
                        supported_countries: [US]
                        auto_refund_on_no_code: true
                        no_code_timeout_seconds: 900
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/ApiError"
        "503":
          $ref: "#/components/responses/ApiError"
  /permanent/{group}/{server}/services:
    get:
      tags: [Services]
      summary: List permanent services
      description: Requires scope `servers:read`.
      parameters:
        - $ref: "#/components/parameters/Group"
        - $ref: "#/components/parameters/Server"
        - $ref: "#/components/parameters/Search"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Country"
        - name: duration
          in: query
          schema:
            type: string
          description: Server-dependent duration, such as 7d or thirtyDay.
        - name: auto_renew
          in: query
          schema:
            type: boolean
      responses:
        "200":
          description: Public permanent services.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceListResponse"
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/ApiError"
        "503":
          $ref: "#/components/responses/ApiError"
  /temporary/{group}/{server}/buy:
    post:
      tags: [Purchase]
      summary: Buy a temporary number
      description: Requires scope `orders:create` and `Idempotency-Key`. Can debit API wallet.
      parameters:
        - $ref: "#/components/parameters/Group"
        - $ref: "#/components/parameters/Server"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TemporaryPurchaseRequest"
            examples:
              usAreaCode:
                value:
                  service: example-mail
                  country: US
                  area_code: "213"
              usState:
                value:
                  service: example-mail
                  country: US
                  state: California
      responses:
        "200":
          $ref: "#/components/responses/OrderOk"
        "201":
          $ref: "#/components/responses/OrderOk"
        "202":
          $ref: "#/components/responses/ApiError"
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/ApiError"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/ApiError"
        "500":
          $ref: "#/components/responses/ApiError"
        "503":
          $ref: "#/components/responses/ApiError"
  /permanent/{group}/{server}/buy:
    post:
      tags: [Purchase]
      summary: Buy a permanent number
      description: Requires scope `orders:create` and `Idempotency-Key`. Can debit API wallet.
      parameters:
        - $ref: "#/components/parameters/Group"
        - $ref: "#/components/parameters/Server"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PermanentPurchaseRequest"
            examples:
              rental:
                value:
                  service: example-mail
                  country: US
                  duration: thirtyDay
                  auto_renew: true
      responses:
        "200":
          $ref: "#/components/responses/OrderOk"
        "201":
          $ref: "#/components/responses/OrderOk"
        "202":
          $ref: "#/components/responses/ApiError"
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/ApiError"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/ApiError"
        "500":
          $ref: "#/components/responses/ApiError"
        "503":
          $ref: "#/components/responses/ApiError"
  /orders/{orderId}:
    get:
      tags: [Orders]
      summary: Read an order
      description: Requires scope `orders:read`.
      parameters:
        - $ref: "#/components/parameters/OrderId"
      responses:
        "200":
          $ref: "#/components/responses/OrderOk"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/ApiError"
        "500":
          $ref: "#/components/responses/ApiError"
  /orders/{orderId}/sync:
    post:
      tags: [Orders]
      summary: Sync a permanent order
      description: Requires scope `orders:read`. Does not require Idempotency-Key and does not debit balance.
      parameters:
        - $ref: "#/components/parameters/OrderId"
      responses:
        "200":
          $ref: "#/components/responses/OrderOk"
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/ApiError"
        "409":
          $ref: "#/components/responses/ApiError"
        "500":
          $ref: "#/components/responses/ApiError"
  /orders/{orderId}/reuse:
    post:
      tags: [Orders]
      summary: Reuse an eligible temporary order
      description: Temporary-only. Requires scope `orders:create` and `Idempotency-Key`. May debit balance depending on server policy.
      parameters:
        - $ref: "#/components/parameters/OrderId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReuseRequest"
      responses:
        "200":
          $ref: "#/components/responses/OrderOk"
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/ApiError"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/ApiError"
        "409":
          $ref: "#/components/responses/ApiError"
        "500":
          $ref: "#/components/responses/ApiError"
        "503":
          $ref: "#/components/responses/ApiError"
  /orders/{orderId}/cancel:
    post:
      tags: [Orders]
      summary: Cancel or refund an eligible order
      description: Requires scope `orders:cancel` and `Idempotency-Key`. Does not charge; may refund when policy allows.
      parameters:
        - $ref: "#/components/parameters/OrderId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          $ref: "#/components/responses/OrderOk"
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/ApiError"
        "409":
          $ref: "#/components/responses/ApiError"
        "500":
          $ref: "#/components/responses/ApiError"
        "503":
          $ref: "#/components/responses/ApiError"
  /orders/{orderId}/renew:
    post:
      tags: [Orders]
      summary: Renew an eligible permanent order
      description: Requires scope `orders:renew` and `Idempotency-Key`. Can debit API wallet.
      parameters:
        - $ref: "#/components/parameters/OrderId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          $ref: "#/components/responses/OrderOk"
        "202":
          $ref: "#/components/responses/OrderOk"
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/ApiError"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/ApiError"
        "409":
          $ref: "#/components/responses/ApiError"
        "500":
          $ref: "#/components/responses/ApiError"
        "503":
          $ref: "#/components/responses/ApiError"
  /orders/{orderId}/extend:
    post:
      tags: [Orders]
      summary: Extend an eligible permanent order
      description: Requires scope `orders:extend` and `Idempotency-Key`. Can debit API wallet.
      parameters:
        - $ref: "#/components/parameters/OrderId"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ExtendRequest"
      responses:
        "200":
          $ref: "#/components/responses/OrderOk"
        "202":
          $ref: "#/components/responses/OrderOk"
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/ApiError"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/ApiError"
        "409":
          $ref: "#/components/responses/ApiError"
        "500":
          $ref: "#/components/responses/ApiError"
        "503":
          $ref: "#/components/responses/ApiError"
  /orders/{orderId}/auto-renew/enable:
    post:
      tags: [Orders]
      summary: Enable auto-renew
      description: Requires scope `orders:renew` and `Idempotency-Key`. Does not immediately renew the number.
      parameters:
        - $ref: "#/components/parameters/OrderId"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "200":
          $ref: "#/components/responses/OrderOk"
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/ApiError"
        "409":
          $ref: "#/components/responses/ApiError"
        "503":
          $ref: "#/components/responses/ApiError"
  /orders/{orderId}/auto-renew/disable:
    post:
      tags: [Orders]
      summary: Disable auto-renew
      description: Requires scope `orders:renew` and `Idempotency-Key`.
      parameters:
        - $ref: "#/components/parameters/OrderId"
        - $ref: "#/components/parameters/IdempotencyKey"
      responses:
        "200":
          $ref: "#/components/responses/OrderOk"
        "400":
          $ref: "#/components/responses/ApiError"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/ApiError"
        "409":
          $ref: "#/components/responses/ApiError"
        "503":
          $ref: "#/components/responses/ApiError"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
  parameters:
    Group:
      name: group
      in: path
      required: true
      schema:
        type: string
        enum: [american, global]
      description: Public server group code.
    Server:
      name: server
      in: path
      required: true
      schema:
        type: string
        enum: [server1, server2, server3, server4, global_server1]
      description: Public server code.
    Search:
      name: search
      in: query
      required: false
      schema:
        type: string
      description: Optional search filter. Some endpoints also accept q.
    Limit:
      name: limit
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 100
      description: Optional result limit.
    Country:
      name: country
      in: query
      required: false
      schema:
        type: string
        example: US
      description: Public country code.
    OrderId:
      name: orderId
      in: path
      required: true
      schema:
        type: string
        example: "64f000000000000000000010"
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        minLength: 8
        maxLength: 200
      description: Unique key for this user action. Reuse the same key only when retrying the exact same request.
  responses:
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            unauthorized:
              value:
                ok: false
                error:
                  code: UNAUTHORIZED
                  message: Authentication required.
    Forbidden:
      description: Key, client, or scope is forbidden.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            forbidden:
              value:
                ok: false
                error:
                  code: FORBIDDEN
                  message: Forbidden.
    ApiError:
      description: Public API error.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          examples:
            unsupportedCountry:
              value:
                ok: false
                error:
                  code: UNSUPPORTED_COUNTRY
                  message: This server supports US only.
    OrderOk:
      description: Public order response.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/OrderResponse"
          examples:
            temporaryActive:
              value:
                ok: true
                order:
                  id: "64f000000000000000000010"
                  type: temporary
                  group: american
                  server: server2
                  service: example-mail
                  country: US
                  status: ACTIVE
                  phone_number: "+12135550100"
                  price: 0.62
                  currency: USD
                  duration: null
                  expires_at: null
                  auto_renew_enabled: false
                  auto_refund_on_no_code: true
                  no_code_timeout_seconds: 900
                  no_code_deadline_at: "2026-07-12T10:15:00.000Z"
                  refund: null
                  sms_code: null
                  sms_code_received_at: null
                  sms_codes: []
                  reuse_allowed: false
                  reusable_until: null
                  renewal_status: null
                  extension_status: null
                  lifecycle_operations: []
            permanentMultiSms:
              value:
                ok: true
                order:
                  id: "64f000000000000000000020"
                  type: permanent
                  group: american
                  server: server2
                  service: example-mail
                  country: US
                  status: CODE_RECEIVED
                  phone_number: "+12025550199"
                  price: 6.25
                  currency: USD
                  duration: thirtyDay
                  expires_at: "2026-08-12T10:00:00.000Z"
                  auto_renew_enabled: true
                  auto_refund_on_no_code: false
                  no_code_timeout_seconds: null
                  no_code_deadline_at: null
                  refund: null
                  sms_code: "222333"
                  sms_code_received_at: "2026-07-12T10:15:00.000Z"
                  sms_codes:
                    - code: "111222"
                      received_at: "2026-07-12T10:02:00.000Z"
                    - code: "222333"
                      received_at: "2026-07-12T10:15:00.000Z"
                  reuse_allowed: false
                  reusable_until: null
                  renewal_status: INCLUDED_FOR_RENEWAL
                  extension_status: null
                  lifecycle_operations: []
  schemas:
    PingResponse:
      type: object
      required: [ok, service, version, time]
      properties:
        ok:
          type: boolean
        service:
          type: string
          example: eznumber-api
        version:
          type: string
          example: v1
        time:
          type: string
          format: date-time
    ErrorResponse:
      type: object
      required: [ok, error]
      properties:
        ok:
          type: boolean
          example: false
        error:
          type: object
          required: [code, message]
          properties:
            code:
              type: string
              example: UNSUPPORTED_COUNTRY
            message:
              type: string
              example: This server supports US only.
        order_id:
          type: string
          nullable: true
        status:
          type: string
          nullable: true
        details:
          type: object
          additionalProperties: true
          nullable: true
    MeResponse:
      type: object
      required: [ok, client]
      properties:
        ok:
          type: boolean
        client:
          type: object
          required: [id, name, status, allowed_product_types, allowed_servers]
          properties:
            id:
              type: string
            name:
              type: string
            status:
              type: string
              enum: [ACTIVE, DISABLED]
            allowed_product_types:
              type: array
              items:
                type: string
                enum: [temporary, permanent]
            allowed_servers:
              type: array
              items:
                type: string
                example: temporary.american.server1
    BalanceResponse:
      type: object
      required: [ok, balance, currency]
      properties:
        ok:
          type: boolean
        balance:
          type: number
          format: double
        currency:
          type: string
          example: USD
    ServerGroupsResponse:
      type: object
      required: [ok, type, groups]
      properties:
        ok:
          type: boolean
        type:
          type: string
          enum: [temporary, permanent]
        groups:
          type: array
          items:
            $ref: "#/components/schemas/ServerGroup"
    ServerGroup:
      type: object
      required: [group_code, name_ar, name_en, servers]
      properties:
        group_code:
          type: string
          example: american
        name_ar:
          type: string
          example: السيرفر الأمريكي
        name_en:
          type: string
          example: American Server
        servers:
          type: array
          items:
            $ref: "#/components/schemas/PublicServer"
    PublicServer:
      type: object
      required: [server_code, name_ar, name_en, available]
      properties:
        server_code:
          type: string
          example: server1
        name_ar:
          type: string
        name_en:
          type: string
        available:
          type: boolean
        country_mode:
          type: string
          nullable: true
          enum: [fixed, dynamic]
        supported_countries:
          type: array
          nullable: true
          items:
            type: string
        countries:
          type: array
          items:
            $ref: "#/components/schemas/Country"
        auto_refund_on_no_code:
          type: boolean
        no_code_timeout_seconds:
          type: integer
          nullable: true
    Country:
      type: object
      required: [code, name_ar, name_en, available]
      properties:
        code:
          type: string
          example: US
        name_ar:
          type: string
        name_en:
          type: string
        available:
          type: boolean
        count:
          type: integer
          nullable: true
    CountryListResponse:
      type: object
      required: [ok, type, group, server, country_mode, countries]
      properties:
        ok:
          type: boolean
        type:
          type: string
          enum: [temporary, permanent]
        group:
          type: string
        server:
          type: string
        country_mode:
          type: string
          enum: [fixed, dynamic]
        countries:
          type: array
          items:
            $ref: "#/components/schemas/Country"
    PublicService:
      type: object
      additionalProperties: true
      required: [service]
      properties:
        service:
          type: string
          example: example-mail
        name:
          type: string
          nullable: true
        price:
          type: number
          nullable: true
        country:
          type: string
          nullable: true
        duration:
          type: string
          nullable: true
        supported_countries:
          type: array
          nullable: true
          items:
            type: string
        auto_refund_on_no_code:
          type: boolean
        no_code_timeout_seconds:
          type: integer
          nullable: true
    ServiceListResponse:
      type: object
      required: [ok, type, group, server, services]
      properties:
        ok:
          type: boolean
        type:
          type: string
          enum: [temporary, permanent]
        group:
          type: string
        server:
          type: string
        services:
          type: array
          items:
            $ref: "#/components/schemas/PublicService"
    TemporaryPurchaseRequest:
      type: object
      required: [service]
      additionalProperties: true
      description: Send either state or area_code, not both. Sending both may return STATE_SELECTION_CONFLICT.
      properties:
        service:
          type: string
          description: Exact public service identifier returned by service discovery.
        country:
          type: string
          description: Required for dynamic servers and recommended for fixed servers.
          example: US
        state:
          type: string
          nullable: true
          description: Optional where supported. Use skip or omit when not needed. Mutually exclusive with area_code.
        area_code:
          type: string
          nullable: true
          description: Optional three-digit area code where supported. Mutually exclusive with state.
          example: "213"
        carrier:
          type: string
          nullable: true
          description: Optional where supported.
    PermanentPurchaseRequest:
      type: object
      required: [service]
      additionalProperties: true
      description: Send either state or area_code, not both, where those selectors are supported.
      properties:
        service:
          type: string
        country:
          type: string
          example: US
        duration:
          type: string
          nullable: true
          description: Server-dependent duration such as 7d, 14d, 30d, oneDay, sevenDay, or thirtyDay.
        auto_renew:
          type: boolean
          nullable: true
        state:
          type: string
          nullable: true
          description: Optional where supported. Mutually exclusive with area_code.
        area_code:
          type: string
          nullable: true
          description: Optional where supported. Mutually exclusive with state.
    ReuseRequest:
      type: object
      additionalProperties: true
      properties:
        service:
          type: string
          nullable: true
        area_code:
          type: string
          nullable: true
    ExtendRequest:
      type: object
      additionalProperties: true
      properties:
        duration:
          type: string
          nullable: true
          example: sevenDay
    OrderResponse:
      type: object
      required: [ok, order]
      properties:
        ok:
          type: boolean
        order:
          $ref: "#/components/schemas/Order"
        operation:
          $ref: "#/components/schemas/LifecycleOperationSummary"
          nullable: true
        refund:
          $ref: "#/components/schemas/Refund"
          nullable: true
    Order:
      type: object
      required:
        - id
        - type
        - group
        - server
        - service
        - country
        - status
        - price
        - currency
        - auto_renew_enabled
        - auto_refund_on_no_code
        - sms_codes
        - reuse_allowed
        - lifecycle_operations
      properties:
        id:
          type: string
        type:
          type: string
          enum: [temporary, permanent]
        group:
          type: string
        server:
          type: string
        service:
          type: string
        country:
          type: string
          nullable: true
        status:
          type: string
          enum: [CREATED, PROCESSING, ACTIVE, CANCEL_PENDING, CODE_RECEIVED, FAILED, REFUND_PENDING, REFUNDED, CANCELLED, EXPIRED]
        phone_number:
          type: string
          nullable: true
        price:
          type: number
        currency:
          type: string
          example: USD
        duration:
          type: string
          nullable: true
        expires_at:
          type: string
          format: date-time
          nullable: true
        auto_renew_enabled:
          type: boolean
        auto_refund_on_no_code:
          type: boolean
        no_code_timeout_seconds:
          type: integer
          nullable: true
        no_code_deadline_at:
          type: string
          format: date-time
          nullable: true
        refund:
          $ref: "#/components/schemas/Refund"
          nullable: true
        sms_code:
          type: string
          nullable: true
          description: Latest convenient SMS code value.
        sms_code_received_at:
          type: string
          format: date-time
          nullable: true
        sms_codes:
          type: array
          description: Complete public list of SMS codes/messages recorded for this order.
          items:
            $ref: "#/components/schemas/SmsCodeEntry"
        reuse_allowed:
          type: boolean
          description: Temporary reuse eligibility. Normally false for permanent orders.
        reusable_until:
          type: string
          format: date-time
          nullable: true
        renewal_status:
          type: string
          nullable: true
        extension_status:
          type: string
          nullable: true
        lifecycle_operations:
          type: array
          items:
            $ref: "#/components/schemas/LifecycleOperation"
    SmsCodeEntry:
      type: object
      required: [code]
      properties:
        code:
          type: string
        received_at:
          type: string
          format: date-time
          nullable: true
    Refund:
      type: object
      nullable: true
      properties:
        amount:
          type: number
        currency:
          type: string
          example: USD
        refunded_at:
          type: string
          format: date-time
          nullable: true
        reason:
          type: string
          nullable: true
    LifecycleOperationSummary:
      type: object
      nullable: true
      properties:
        operation_id:
          type: string
        status:
          type: string
    LifecycleOperation:
      type: object
      properties:
        operation_id:
          type: string
          nullable: true
        type:
          type: string
          nullable: true
          example: RENEWAL
        status:
          type: string
          nullable: true
          example: PROVIDER_REQUESTED
        amount:
          type: number
        currency:
          type: string
          example: USD
        previous_expires_at:
          type: string
          format: date-time
          nullable: true
        new_expires_at:
          type: string
          format: date-time
          nullable: true
        result_code:
          type: string
          nullable: true
        failure_code:
          type: string
          nullable: true
        started_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
