{
  "openapi": "3.1.0",
  "info": {
    "title": "Telvera API",
    "version": "1.0.0",
    "summary": "Phone number verification by an incoming call.",
    "description": "Send the user's number, show them `call_to`, they call it from their phone, you get `verification.verified` by webhook. Docs: https://telvera.io/doc-api/",
    "contact": { "name": "Telvera", "url": "https://telvera.io/doc-api/#support" }
  },
  "servers": [{ "url": "https://api.telvera.io" }],
  "security": [{ "bearerAuth": [] }],
  "paths": {
    "/v1/verify": {
      "post": {
        "operationId": "createVerification",
        "summary": "Create a verification",
        "description": "Returns the number the user should call; they have one minute. Every request creates a new verification; a previous pending one for this phone becomes `expired`. Send `Idempotency-Key` (one per user session/attempt) so that a retry returns the same verification with `200`.",
        "parameters": [
          { "name": "Idempotency-Key", "in": "header", "required": false, "description": "A repeat with the same key (per API key, 24 h) returns the same verification with `200` and `Idempotent-Replayed: true`. 1–255 printable ASCII characters.", "schema": { "type": "string", "minLength": 1, "maxLength": 255 }, "example": "signup-7f3c2a" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateVerificationRequest" },
              "example": { "phone": "+375290000001", "metadata": { "user_id": "42" } }
            }
          }
        },
        "responses": {
          "201": { "description": "New verification", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Verification" } } } },
          "200": { "description": "A repeat with the same `Idempotency-Key` — the same verification is returned", "headers": { "Idempotent-Replayed": { "schema": { "type": "string", "enum": ["true"] } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Verification" } } } },
          "401": { "$ref": "#/components/responses/Error" },
          "402": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RetryableError" },
          "503": { "$ref": "#/components/responses/RetryableError" }
        }
      }
    },
    "/v1/verify/{id}": {
      "get": {
        "operationId": "getVerification",
        "summary": "Get status",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^vrf_[0-9a-f]{32}$" }, "example": "vrf_0d9a3d2b5ca2439b9af861470d5986bc" }
        ],
        "responses": {
          "200": { "description": "The verification", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Verification" } } } },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RetryableError" }
        }
      }
    }
  },
  "webhooks": {
    "verification": {
      "post": {
        "summary": "Verification finished",
        "description": "Sent to the key's webhook URL. Signed: `X-Telvera-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, \"{t}.{body}\")>`. Reply 2xx within 5 seconds; otherwise retried after 1, 3 and 10 seconds.",
        "parameters": [
          { "name": "X-Telvera-Signature", "in": "header", "required": true, "schema": { "type": "string" } },
          { "name": "X-Telvera-Event", "in": "header", "required": true, "schema": { "type": "string", "enum": ["verification.verified", "verification.expired"] } }
        ],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Event" } } } },
        "responses": { "200": { "description": "Any 2xx — delivered" } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "description": "API key: `sk_live_…` or `sk_test_…` (Settings → API)." }
    },
    "schemas": {
      "CreateVerificationRequest": {
        "type": "object",
        "required": ["phone"],
        "additionalProperties": false,
        "properties": {
          "phone": { "type": "string", "maxLength": 32, "description": "International format; spaces, brackets and dashes are fine." },
          "metadata": {
            "type": "object",
            "maxProperties": 20,
            "propertyNames": { "maxLength": 64 },
            "additionalProperties": { "type": ["string", "number", "boolean", "null"], "maxLength": 500 },
            "description": "Flat object, returned unchanged in responses and the webhook."
          }
        }
      },
      "Verification": {
        "type": "object",
        "required": ["id", "status", "phone", "call_to", "expires_at", "verified_at", "metadata", "created_at"],
        "additionalProperties": false,
        "properties": {
          "id": { "type": "string", "pattern": "^vrf_[0-9a-f]{32}$" },
          "status": { "type": "string", "enum": ["pending", "verified", "expired"] },
          "phone": { "type": "string", "description": "E.164" },
          "call_to": { "type": "string", "description": "E.164 — the number the user should call" },
          "expires_at": { "type": "string", "format": "date-time" },
          "verified_at": { "type": ["string", "null"], "format": "date-time" },
          "metadata": { "type": ["object", "null"] },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "Event": {
        "type": "object",
        "required": ["id", "type", "livemode", "created_at", "data"],
        "additionalProperties": false,
        "properties": {
          "id": { "type": "string", "pattern": "^evt_[0-9a-f]{32}$", "description": "Same for every retry — use it to skip duplicates." },
          "type": { "type": "string", "enum": ["verification.verified", "verification.expired"] },
          "livemode": { "type": "boolean", "description": "false for a test key" },
          "created_at": { "type": "string", "format": "date-time" },
          "data": { "$ref": "#/components/schemas/Verification" }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "additionalProperties": false,
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "additionalProperties": false,
            "properties": {
              "code": { "type": "string", "enum": ["unauthorized", "insufficient_checks", "account_disabled", "test_phone_only", "not_found", "invalid_phone", "phone_not_supported", "idempotency_key_reused", "invalid_request", "rate_limited", "unavailable", "internal_error"] },
              "message": { "type": "string" }
            }
          }
        }
      }
    },
    "responses": {
      "Error": { "description": "Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RetryableError": {
        "description": "Retry after `Retry-After` seconds",
        "headers": { "Retry-After": { "schema": { "type": "integer" } } },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  }
}
