openapi: 3.0.3
info:
  title: SIMcloud API
  version: 1.0.0
  description: |
    Token-authenticated SIMcloud API for user registration, SMS, airtime, data,
    VAS, electricity, transaction history, wallet balance, and network lookup.
servers:
  - url: https://simcloud.co.za
security:
  - bearerAuth: []
tags:
  - name: Registration
  - name: SMS
  - name: Airtime
  - name: Data
  - name: VAS
  - name: Electricity
  - name: Transaction History
  - name: Balance
  - name: Network Lookup
paths:
  /api/register.php:
    post:
      tags: [Registration]
      summary: Register a normal SIMcloud child account
      description: Creates a normal SIMcloud account under the authenticated parent account and returns the new child's API token. The child has its own wallet, orders and API access. Retrying with the same parent token and cell number returns the existing child token.
      operationId: registerSimcloudUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [first_name, last_name, cell, email, password]
              properties:
                first_name:
                  type: string
                  example: Jane
                last_name:
                  type: string
                  example: Doe
                cell:
                  type: string
                  description: 10-digit South African mobile number beginning with 0.
                  example: "0821234567"
                email:
                  type: string
                  format: email
                  example: jane@example.com
                password:
                  type: string
                  format: password
                id_number:
                  type: string
                  description: Optional valid South African ID number.
                business_name:
                  type: string
                business_reg:
                  type: string
                vatno:
                  type: string
                address:
                  type: string
      responses:
        '201':
          description: Child account created; response includes its API token.
        '200':
          description: Existing child account returned for the same authenticated parent and cell number.
        '400':
          description: Validation or JSON request error.
        '401':
          description: Unauthorized parent token.
        '403':
          description: Registration blocked for the supplied identity.
        '409':
          description: Cell number belongs to an account outside this authenticated parent's downline.
        '422':
          description: Upstream registration rejected the request.
        '502':
          description: Upstream registration service error.
  /api/sms.php:
    post:
      tags: [SMS]
      summary: Queue an SMS
      operationId: sendSms
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [recipient, message]
              properties:
                recipient:
                  type: string
                  example: "+27821234567"
                message:
                  type: string
                  example: "Hello World!"
      responses:
        '200':
          description: SMS queued
        '400':
          description: Validation error
        '401':
          description: Unauthorized
    get:
      tags: [SMS]
      summary: Query SMS status
      operationId: getSmsStatus
      parameters:
        - in: query
          name: sms_id
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: SMS status
        '404':
          description: SMS not found
  /api/airtime.php:
    post:
      tags: [Airtime]
      summary: Queue an airtime order
      description: The same MSISDN and amount cannot be ordered again within 5 minutes. Duplicate requests inside that window return HTTP 409.
      operationId: createAirtimeOrder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [msisdn, network, amount, reference]
              properties:
                msisdn:
                  type: string
                  example: "0821234567"
                network:
                  type: string
                  description: Accepts friendly names or internal codes.
                  example: "mtn"
                amount:
                  type: number
                  format: float
                  example: 10.00
                reference:
                  type: string
                  example: "Airtime for John"
      responses:
        '201':
          description: Airtime order queued
        '400':
          description: Validation or balance error
        '401':
          description: Unauthorized
        '409':
          description: Duplicate order within the 5-minute rule window
    get:
      tags: [Airtime]
      summary: Query an airtime order
      operationId: getAirtimeOrder
      parameters:
        - in: query
          name: request_id
          required: false
          schema:
            type: integer
        - in: query
          name: orderno
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Airtime order status
        '404':
          description: Order not found
  /api/data.php:
    post:
      tags: [Data]
      summary: Queue a data order
      description: The same MSISDN and amount cannot be ordered again within 5 minutes. Duplicate requests inside that window return HTTP 409.
      operationId: createDataOrder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [msisdn, network, amount, reference]
              properties:
                msisdn:
                  type: string
                  example: "0821234567"
                network:
                  type: string
                  description: Accepts friendly names or internal codes.
                  example: "vodacom"
                amount:
                  type: number
                  format: float
                  example: 25.00
                sellvalue:
                  type: number
                  format: float
                  description: Alias for amount.
                reference:
                  type: string
                  example: "Data for John"
      responses:
        '201':
          description: Data order queued
        '400':
          description: Validation or balance error
        '401':
          description: Unauthorized
        '409':
          description: Duplicate order within the 5-minute rule window
    get:
      tags: [Data]
      summary: List data products or query a data order
      description: Without request_id or orderno, returns the current data bundle product catalog. Add network to filter the catalog. With request_id or orderno, returns the authenticated account's data order status.
      operationId: getDataOrder
      parameters:
        - in: query
          name: products
          required: false
          description: Optional flag for product catalog reads. Bare GET also returns the catalog when request_id and orderno are not supplied.
          schema:
            type: boolean
        - in: query
          name: network
          required: false
          description: Optional network filter for product catalog reads. Accepts friendly names or internal codes.
          schema:
            type: string
        - in: query
          name: request_id
          required: false
          schema:
            type: integer
        - in: query
          name: orderno
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Data product catalog or data order status
        '400':
          description: Invalid catalog filter or order query
        '404':
          description: Order not found
  /api/vas.php:
    get:
      tags: [VAS]
      summary: List VAS products or query a VAS order
      description: Without query parameters, returns the active VAS product catalog. With order_id or transaction_id, returns an API-created VAS order owned by the authenticated account.
      operationId: getVas
      parameters:
        - in: query
          name: order_id
          required: false
          schema:
            type: integer
        - in: query
          name: transaction_id
          required: false
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: VAS catalog or VAS order status
        '401':
          description: Unauthorized
        '404':
          description: Order not found
    post:
      tags: [VAS]
      summary: Queue a VAS voucher order
      operationId: createVasOrder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [amount, reference]
              properties:
                product_id:
                  type: integer
                  description: SIMcloud VAS product ID returned by the catalog endpoint. Required if voucher_type is not supplied.
                  example: 12
                voucher_type:
                  type: string
                  description: Exact voucher type or slug returned by the catalog endpoint. Required if product_id is not supplied.
                  example: "checkers"
                amount:
                  type: number
                  format: float
                  example: 100.00
                reference:
                  type: string
                  maxLength: 200
                  example: "INV-10045"
      responses:
        '201':
          description: VAS order queued
        '400':
          description: Validation, denomination, or balance error
        '401':
          description: Unauthorized
  /api/electricity.php:
    post:
      tags: [Electricity]
      summary: Submit an electricity order
      operationId: createElectricityOrder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [meter_number, amount, client_reference, recipient]
              properties:
                meter_number:
                  type: string
                  example: "01234567890"
                amount:
                  type: number
                  format: float
                  example: 150.00
                client_reference:
                  type: string
                  example: "INV-10045"
                recipient:
                  type: string
                  example: "0821234567"
                send_sms:
                  type: boolean
                  example: true
                sms_company_name:
                  type: string
                  example: "acme power"
      responses:
        '201':
          description: Electricity order accepted
        '400':
          description: Validation or balance error
        '401':
          description: Unauthorized
        '503':
          description: Electricity unavailable
    get:
      tags: [Electricity]
      summary: Check an electricity meter or query an electricity order
      operationId: getElectricityOrder
      parameters:
        - in: query
          name: meter_number
          required: false
          schema:
            type: string
          description: Meter number to validate before placing an order. When supplied, the endpoint returns meter support details instead of order status. A supplier internal error returns status unknown and supported null; retry later rather than treating the meter as unsupported.
        - in: query
          name: order_id
          required: false
          schema:
            type: integer
        - in: query
          name: client_reference
          required: false
          schema:
            type: string
        - in: query
          name: order_reference_id
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Meter support details, pending order status, or completed electricity voucher details
        '400':
          description: Missing or invalid query parameters
        '404':
          description: Order not found
        '502':
          description: Electricity meter validation request failed
        '503':
          description: Electricity meter validation unavailable
  /api/transactions.php:
    get:
      tags: [Transaction History]
      summary: List account transaction history
      description: Returns account-owned web, CSV, recurring and API transactions for airtime, data, SMS, VAS and electricity. Prices, fulfilment details, recipients and message content are not returned.
      operationId: getTransactionHistory
      parameters:
        - in: query
          name: date_from
          required: true
          schema:
            type: string
            format: date
          description: Inclusive start date in Africa/Johannesburg time.
          example: "2026-07-01"
        - in: query
          name: date_to
          required: true
          schema:
            type: string
            format: date
          description: Inclusive end date in Africa/Johannesburg time. The maximum range is 90 days.
          example: "2026-07-31"
        - in: query
          name: type
          required: false
          schema:
            type: string
          description: "Optional comma-separated service filter: airtime, data, electricity, vas and sms."
          example: "airtime,data"
        - in: query
          name: limit
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Opaque cursor returned by the previous response. Reuse it with the same date range and type filter.
      responses:
        '200':
          description: Transaction history page
        '400':
          description: Invalid date range, type filter, limit or cursor
        '401':
          description: Unauthorized
  /api/balance.php:
    get:
      tags: [Balance]
      summary: Get wallet balance
      operationId: getWalletBalance
      responses:
        '200':
          description: Last known wallet balance
        '401':
          description: Unauthorized
  /api/network.php:
    get:
      tags: [Network Lookup]
      summary: Look up a mobile network
      operationId: lookupNetwork
      parameters:
        - in: query
          name: msisdn
          required: true
          schema:
            type: string
          example: "0821234567"
      responses:
        '200':
          description: Network found
        '400':
          description: Invalid msisdn
        '401':
          description: Unauthorized
        '404':
          description: Number does not match any network
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API Token
