{
  "openapi": "3.0.3",
  "info": {
    "title": "KwickPhone Developer API",
    "version": "1.0.0",
    "description": "Provision AI phone numbers and read their calls, transcripts, and recordings.\n\nAll requests are authenticated with a Bearer API key (`kp_live_…`) issued from your KwickPhone account. No cookies or sessions. Responses are JSON; errors use the envelope `{ \"error\": { \"code\", \"message\" } }`.\n\nRate limit: 60 requests / 60s per key. An API key may be restricted to an IP allowlist.",
    "contact": { "name": "KwickPhone", "url": "https://kwickphone.com/integrations/api/" }
  },
  "servers": [ { "url": "https://kwickphone.com/v1", "description": "Production" } ],
  "security": [ { "bearerAuth": [] } ],
  "tags": [
    { "name": "Numbers", "description": "Provision, list, and release AI phone numbers." },
    { "name": "Calls", "description": "Read calls, transcripts, and recordings for your numbers." }
  ],
  "paths": {
    "/numbers": {
      "post": {
        "tags": ["Numbers"],
        "summary": "Provision an AI phone number",
        "description": "Provisions a new AI phone number for your account and starts your AI agent answering it. The `webhook_secret` is returned ONCE on creation — store it to verify webhook signatures.",
        "operationId": "createNumber",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NumberCreate" },
            "example": { "business_phone": "+13125551234", "area_code": "312", "webhook_url": "https://your-app.com/webhooks/kwickphone" } } }
        },
        "responses": {
          "201": { "description": "Number provisioned", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/NumberCreated" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "402": { "description": "Insufficient API balance", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "insufficient_balance", "message": "Add funds to your API balance before provisioning a number." } } } } },
          "409": { "description": "Number or velocity limit reached", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "503": { "description": "Provider unavailable / no numbers in area code", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      },
      "get": {
        "tags": ["Numbers"],
        "summary": "List your numbers",
        "operationId": "listNumbers",
        "responses": {
          "200": { "description": "Your account's numbers", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Number" } } } } } } },
          "401": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/numbers/{id}": {
      "delete": {
        "tags": ["Numbers"],
        "summary": "Release a number",
        "operationId": "releaseNumber",
        "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" },
          "description": "The numeric number id — the digits from the `id` field (for `\"num_42\"`, pass `42`).", "example": 42 } ],
        "responses": {
          "200": { "description": "Released", "content": { "application/json": { "schema": { "type": "object", "properties": { "released": { "type": "boolean", "example": true } } } } } },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/calls": {
      "get": {
        "tags": ["Calls"],
        "summary": "List calls",
        "description": "Calls across all of your account's API numbers, newest first.",
        "operationId": "listCalls",
        "responses": {
          "200": { "description": "Calls", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Call" } } } } } } },
          "401": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/calls/{callSid}/transcript": {
      "get": {
        "tags": ["Calls"],
        "summary": "Get a call transcript",
        "operationId": "getCallTranscript",
        "parameters": [ { "name": "callSid", "in": "path", "required": true, "schema": { "type": "string" }, "description": "The call `id` from List calls.", "example": "CA0123456789abcdef" } ],
        "responses": {
          "200": { "description": "Transcript", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Transcript" } } } },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/calls/{callSid}/recording": {
      "get": {
        "tags": ["Calls"],
        "summary": "Get a call recording",
        "description": "Streams the call recording audio (`audio/mpeg`).",
        "operationId": "getCallRecording",
        "parameters": [ { "name": "callSid", "in": "path", "required": true, "schema": { "type": "string" }, "description": "The call `id` from List calls.", "example": "CA0123456789abcdef" } ],
        "responses": {
          "200": { "description": "MP3 audio", "content": { "audio/mpeg": { "schema": { "type": "string", "format": "binary" } } } },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "description": "No recording for this call", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "kp_live_…", "description": "Your KwickPhone API key, sent as `Authorization: Bearer kp_live_…`." }
    },
    "responses": {
      "Error": { "description": "Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RateLimited": { "description": "Too many requests (60 / 60s per key)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "rate_limited", "message": "Too many requests. Slow down and retry." } } } } }
    },
    "schemas": {
      "NumberCreate": {
        "type": "object",
        "properties": {
          "business_phone": { "type": "string", "description": "The merchant's real phone (E.164). Calls the AI can't handle can route here.", "example": "+13125551234" },
          "area_code": { "type": "string", "description": "Preferred area code for the new number.", "example": "312" },
          "webhook_url": { "type": "string", "format": "uri", "description": "HTTPS endpoint we POST call events to.", "example": "https://your-app.com/webhooks/kwickphone" }
        }
      },
      "Number": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "example": "num_42" },
          "phone_number": { "type": "string", "example": "+13125550000" },
          "business_phone": { "type": "string", "example": "+13125551234" },
          "status": { "type": "string", "example": "active" },
          "webhook_url": { "type": "string", "example": "https://your-app.com/webhooks/kwickphone" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "NumberCreated": {
        "allOf": [
          { "$ref": "#/components/schemas/Number" },
          { "type": "object", "properties": { "webhook_secret": { "type": "string", "description": "Returned ONCE — store it to verify webhook signatures.", "example": "whsec_a1b2c3…" } } }
        ]
      },
      "Call": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "description": "Call SID — use for transcript/recording.", "example": "CA0123456789abcdef" },
          "number_id": { "type": "string", "example": "num_42" },
          "phone_number": { "type": "string", "example": "+13125550000" },
          "caller": { "type": "string", "example": "+13125559999" },
          "direction": { "type": "string", "example": "inbound" },
          "status": { "type": "string", "example": "completed" },
          "outcome": { "type": "string", "example": "order_placed" },
          "duration_sec": { "type": "integer", "example": 92 },
          "started_at": { "type": "string", "format": "date-time" },
          "has_recording": { "type": "boolean" },
          "has_transcript": { "type": "boolean" }
        }
      },
      "Transcript": {
        "type": "object",
        "description": "Transcript payload for the call (turns + metadata).",
        "additionalProperties": true,
        "example": { "call_sid": "CA0123456789abcdef", "turns": [ { "role": "agent", "text": "Thanks for calling — how can I help?" }, { "role": "caller", "text": "I'd like a large pepperoni for pickup." } ] }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "object", "properties": {
            "code": { "type": "string", "example": "unauthorized" },
            "message": { "type": "string", "example": "Missing or invalid API key." }
          } }
        }
      }
    }
  }
}
