{
  "openapi": "3.1.0",
  "info": {
    "title": "Vomule SMS API",
    "summary": "Signed REST API for transactional and bulk SMS delivery.",
    "description": "Use the Vomule SMS API to queue a single JSON SMS request or upload a prepared CSV file. Every request is authenticated with a client ID, Unix timestamp, SHA-256 body hash, and HMAC-SHA256 signature.",
    "version": "1.0.0",
    "contact": {
      "name": "Vomule Support"
    }
  },
  "servers": [
    {
      "url": "https://sms.vomule.net",
      "description": "Production SMS API"
    },
    {
      "url": "http://backend.test",
      "description": "Local development"
    }
  ],
  "tags": [
    {
      "name": "Messaging",
      "description": "SMS queueing endpoints"
    }
  ],
  "paths": {
    "/api/v1/send-sms": {
      "post": {
        "tags": ["Messaging"],
        "operationId": "sendSms",
        "summary": "Send SMS",
        "description": "Queue one SMS with JSON or upload a CSV file for bulk sending. For JSON requests, hash the exact JSON bytes. For file uploads, hash the original file bytes, not the multipart envelope.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ClientIdHeader"
          },
          {
            "$ref": "#/components/parameters/TimestampHeader"
          },
          {
            "$ref": "#/components/parameters/BodyHashHeader"
          },
          {
            "$ref": "#/components/parameters/SignatureHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SingleSmsRequest"
              },
              "examples": {
                "singleMessage": {
                  "summary": "Single message",
                  "value": {
                    "msisdn": "+26650123456",
                    "message": "Your verification code is 482913.",
                    "request_id": "order-20260622-0001"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/BulkSmsUploadRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message or file accepted for processing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsAcceptedResponse"
                },
                "examples": {
                  "json": {
                    "summary": "JSON accepted",
                    "value": {
                      "message": "Message successfully queued",
                      "status": "queued",
                      "message_id": "019..."
                    }
                  },
                  "file": {
                    "summary": "File accepted",
                    "value": {
                      "message": "File successfully uploaded"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        },
        "security": [
          {
            "ClientId": [],
            "Timestamp": [],
            "BodyHash": [],
            "Signature": []
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ClientId": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Client-ID"
      },
      "Timestamp": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Timestamp"
      },
      "BodyHash": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Body-Hash"
      },
      "Signature": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Signature"
      }
    },
    "parameters": {
      "ClientIdHeader": {
        "name": "X-Client-ID",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Issued SMS API client ID."
      },
      "TimestampHeader": {
        "name": "X-Timestamp",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[0-9]{10}$"
        },
        "description": "Current Unix timestamp in seconds."
      },
      "BodyHashHeader": {
        "name": "X-Body-Hash",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[a-f0-9]{64}$"
        },
        "description": "SHA-256 hash of the exact JSON bytes or original uploaded file bytes."
      },
      "SignatureHeader": {
        "name": "X-Signature",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[a-f0-9]{64}$"
        },
        "description": "HMAC-SHA256 of `{timestamp}POST/api/v1/send-sms{bodyHash}` using the client secret."
      }
    },
    "schemas": {
      "SingleSmsRequest": {
        "type": "object",
        "required": ["msisdn", "message"],
        "properties": {
          "msisdn": {
            "type": "string",
            "description": "Recipient MSISDN in international format.",
            "examples": ["+26650123456"]
          },
          "message": {
            "type": "string",
            "description": "SMS message body."
          },
          "request_id": {
            "type": "string",
            "description": "Optional caller-generated trace ID."
          }
        },
        "additionalProperties": false
      },
      "BulkSmsUploadRequest": {
        "type": "object",
        "required": ["file"],
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "description": "CSV file with rows in `msisdn,message,request_id` format. The message column may be supplied per row."
          }
        },
        "additionalProperties": false
      },
      "SmsAcceptedResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "message_id": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        },
        "additionalProperties": true
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The supplied body hash does not match the received bytes.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Credentials, timestamp, hash, or signature are invalid.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "ValidationError": {
        "description": "Message fields or uploaded file failed validation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "ServerError": {
        "description": "Unexpected service error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    }
  }
}
