{
  "openapi": "3.0.3",
  "info": {
    "title": "OTP Sanly API",
    "description": "OTP (one-time password) authentication API. SMS delivery is Turkmenistan-only (+993 numbers, via an Android SIM-card gateway); email delivery works worldwide. Full human-readable docs: https://otp.sanly.dev/developers",
    "version": "1.0.0",
    "contact": { "url": "https://otp.sanly.dev/developers" }
  },
  "servers": [{ "url": "https://otp.sanly.dev" }],
  "paths": {
    "/api/send-otp": {
      "post": {
        "summary": "Send an OTP code via SMS or email",
        "operationId": "sendOtp",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["apiKey"],
                "properties": {
                  "apiKey": { "type": "string", "description": "Starts with otpsanly_" },
                  "phone":  { "type": "string", "description": "Turkmenistan number, e.g. +99361234567. Send phone OR email, not both." },
                  "email":  { "type": "string", "format": "email", "description": "Any email address, any country. Send phone OR email, not both." },
                  "project": { "type": "string", "description": "Optional free-text label shown in dashboard/webhooks." },
                  "lang":    { "type": "string", "enum": ["tk","ru","en"], "description": "Which language to send the OTP in (selects the 3-language custom template/project label configured for the API key). Defaults to 'tk' if omitted and Accept-Language header is absent or unrecognized." }
                }
              },
              "examples": {
                "phone": { "value": { "apiKey": "otpsanly_xxxxxxxx", "phone": "+99361234567", "project": "My Site", "lang": "tk" } },
                "email": { "value": { "apiKey": "otpsanly_xxxxxxxx", "email": "user@example.com", "project": "My Site", "lang": "ru" } }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OTP sent (or sandbox response, or a handled error such as rate-limit — check `success`)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success":      { "type": "boolean" },
                    "otpId":        { "type": "integer", "description": "Required for the verify-otp call" },
                    "expiresIn":    { "type": "integer", "description": "Seconds until the code expires" },
                    "expiresAtTM":  { "type": "string", "description": "Expiry time, Ashgabat timezone" },
                    "attempt":      { "type": "integer" },
                    "maxAttempts":  { "type": "integer", "example": 3 },
                    "remainingOtp": { "type": "integer", "description": "OTPs left on the plan" },
                    "channel":      { "type": "string", "enum": ["email", "sms_gateway", "sms_sandbox"] },
                    "sandbox":      { "type": "boolean", "description": "Present and true only for sandbox keys" },
                    "code":         { "type": "string", "description": "Present ONLY for sandbox keys — the actual OTP code, for testing without a real SMS/email" },
                    "target":       { "type": "string" },
                    "retryAfter":   { "type": "integer", "description": "Present only when rate-limited: seconds to wait" },
                    "error":        { "type": "string", "description": "Present only when success is false" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/verify-otp": {
      "post": {
        "summary": "Verify a previously sent OTP code",
        "operationId": "verifyOtp",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["apiKey", "otpId", "code"],
                "properties": {
                  "apiKey": { "type": "string" },
                  "otpId":  { "type": "integer", "description": "From the send-otp response" },
                  "code":   { "type": "string", "description": "The code the user entered" }
                }
              },
              "example": { "apiKey": "otpsanly_xxxxxxxx", "otpId": 1234, "code": "482913" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification result — check `success`",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success":      { "type": "boolean" },
                    "otpId":        { "type": "integer" },
                    "target":       { "type": "string", "description": "Phone or email that was verified" },
                    "verifiedAt":   { "type": "string", "description": "UTC ISO timestamp" },
                    "verifiedAtTM": { "type": "string", "description": "Ashgabat local time" },
                    "channel":      { "type": "string" },
                    "project":      { "type": "string" },
                    "error":        { "type": "string", "description": "Present only when success is false" }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
