{
  "openapi": "3.1.0",
  "info": {
    "title": "CallAnswer API",
    "version": "1.0.0",
    "summary": "List calls and appointments, book or cancel an appointment, and read or update business hours — for your own scripts and AI agents.",
    "description": "Every endpoint authenticates with a per-customer API key (`Authorization: Bearer ca_sk_...`) created in Settings at https://callanswer.net/settings#agent-access. A key acts AS the customer: it only ever reaches the one business that account owns, never another customer's data. Keys have a `read` or `write` scope. Rate limit: 300 calls per key per hour. The same jobs are available as MCP tools at https://callanswer.net/mcp and from the `callanswer` CLI (`npx callanswer --help`). Setup guide: https://callanswer.net/docs/agents. Nothing in this API sends an SMS, places a call, or touches billing.",
    "termsOfService": "https://callanswer.net/terms",
    "contact": { "name": "CallAnswer support", "email": "support@callanswer.net", "url": "https://callanswer.net/support" }
  },
  "servers": [{ "url": "https://callanswer.net" }],
  "security": [{ "apiKey": [] }],
  "tags": [
    { "name": "account" },
    { "name": "calls", "description": "Lead conversations captured from missed calls." },
    { "name": "appointments", "description": "The business's calendar." },
    { "name": "business-hours", "description": "Hours the AI uses to decide 'after hours'." }
  ],
  "paths": {
    "/api/v1/me": {
      "get": {
        "tags": ["account"], "operationId": "getMe", "summary": "Who this key belongs to, the business, plan, and recent agent calls",
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Me" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NoBusiness" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/api/v1/calls": {
      "get": {
        "tags": ["calls"], "operationId": "listCalls", "summary": "List calls (lead conversations)",
        "description": "A missed call becomes a lead with an AI SMS conversation; this lists those. Optional `q` matches lead name or phone; `status` filters by lead status.",
        "parameters": [
          { "name": "q", "in": "query", "schema": { "type": "string" } },
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["NEW", "CONTACTED", "QUALIFIED", "BOOKED", "WON", "LOST"] } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } }
        ],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "required": ["calls"], "properties": { "calls": { "type": "array", "items": { "$ref": "#/components/schemas/CallSummary" } } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/api/v1/calls/{id}": {
      "get": {
        "tags": ["calls"], "operationId": "getCall", "summary": "A call's summary and full SMS transcript",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "required": ["call"], "properties": { "call": { "$ref": "#/components/schemas/CallDetail" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/api/v1/appointments": {
      "get": {
        "tags": ["appointments"], "operationId": "listAppointments", "summary": "List appointments",
        "parameters": [
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["SCHEDULED", "CONFIRMED", "COMPLETED", "NO_SHOW", "CANCELLED"] } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } }
        ],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "required": ["appointments"], "properties": { "appointments": { "type": "array", "items": { "$ref": "#/components/schemas/Appointment" } } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      },
      "post": {
        "tags": ["appointments"], "operationId": "createAppointment", "summary": "Book an appointment for an existing lead",
        "description": "Real state change on the calendar. Call once without `confirm` to get back exactly what would be created; call again with `confirm: true` to actually create it. Requires a write-scope key. Does not send any SMS.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateAppointmentRequest" } } } },
        "responses": {
          "200": { "description": "Created, or a confirmation preview if `confirm` was not `true`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateAppointmentResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Scope" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/appointments/{id}/cancel": {
      "post": {
        "tags": ["appointments"], "operationId": "cancelAppointment", "summary": "Cancel an appointment (destructive, not undoable)",
        "description": "Sets status to CANCELLED. Call once without `confirm` to get back what would be cancelled; call again with `confirm: true` to actually cancel. Requires a write-scope key. Does not notify the lead.",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": { "required": false, "content": { "application/json": { "schema": { "type": "object", "properties": { "confirm": { "type": "boolean", "default": false } } } } } },
        "responses": {
          "200": { "description": "Cancelled, or a confirmation preview if `confirm` was not `true`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CancelAppointmentResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Scope" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/business-hours": {
      "get": {
        "tags": ["business-hours"], "operationId": "getBusinessHours", "summary": "The business's configured hours and timezone",
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BusinessHours" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      },
      "patch": {
        "tags": ["business-hours"], "operationId": "updateBusinessHours", "summary": "Replace the business's hours configuration",
        "description": "Changes how the AI answers 'are you open' in real time. Call once without `confirm` to preview; call again with `confirm: true` to apply. Requires a write-scope key.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["businessHours"], "properties": { "businessHours": { "type": "object", "description": "Keyed monday..sunday; each value an { open, close } object or null" }, "confirm": { "type": "boolean", "default": false } } } } } },
        "responses": {
          "200": { "description": "Updated, or a confirmation preview if `confirm` was not `true`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BusinessHoursUpdateResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Scope" }, "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": { "type": "http", "scheme": "bearer", "bearerFormat": "ca_sk_<40 hex>", "description": "Create and revoke keys at https://callanswer.net/settings#agent-access. Scopes: read, write." }
    },
    "responses": {
      "Unauthorized": { "description": "Missing, unknown or revoked key. Carries `WWW-Authenticate: Bearer realm=\"callanswer\"`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "BadRequest": { "description": "Invalid input", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound": { "description": "Not found, or not visible to this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NoBusiness": { "description": "The account has no business set up yet (onboarding incomplete)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Scope": { "description": "The key lacks the write scope for this action", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RateLimited": { "description": "300 calls per key per hour exceeded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "Error": { "type": "object", "required": ["error"], "properties": { "error": { "type": "string" }, "docs": { "type": "string" }, "createKeyAt": { "type": "string" } } },
      "Me": {
        "type": "object", "required": ["email", "businessName", "plan", "key"],
        "properties": {
          "email": { "type": "string" }, "businessName": { "type": "string" },
          "plan": { "type": "string", "enum": ["essentials", "starter", "pro"] },
          "subscriptionStatus": { "type": "string" },
          "key": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "scopes": { "type": "array", "items": { "type": "string", "enum": ["read", "write"] } } } },
          "recentCalls": { "type": "array", "items": { "type": "object", "properties": { "action": { "type": "string" }, "surface": { "type": "string", "enum": ["mcp", "rest"] }, "ok": { "type": "boolean" }, "durationMs": { "type": "integer" }, "createdAt": { "type": "string" } } } }
        }
      },
      "CallSummary": {
        "type": "object", "required": ["id", "leadId", "leadPhone", "status"],
        "properties": { "id": { "type": "string" }, "leadId": { "type": "string" }, "leadName": { "type": ["string", "null"] }, "leadPhone": { "type": "string" }, "leadStatus": { "type": "string" }, "isHot": { "type": "boolean" }, "channel": { "type": "string" }, "status": { "type": "string" }, "messageCount": { "type": "integer" }, "lastMessage": { "type": ["string", "null"] }, "updatedAt": { "type": "string" } }
      },
      "Message": { "type": "object", "required": ["id", "role", "content", "createdAt"], "properties": { "id": { "type": "string" }, "role": { "type": "string", "enum": ["USER", "ASSISTANT", "SYSTEM"] }, "content": { "type": "string" }, "createdAt": { "type": "string" } } },
      "CallDetail": {
        "type": "object", "required": ["id", "lead", "summary", "transcript"],
        "properties": {
          "id": { "type": "string" }, "channel": { "type": "string" }, "status": { "type": "string" }, "createdAt": { "type": "string" }, "updatedAt": { "type": "string" },
          "lead": { "type": "object" }, "summary": { "type": "object" },
          "transcript": { "type": "array", "items": { "$ref": "#/components/schemas/Message" } },
          "appointments": { "type": "array", "items": { "type": "object" } }
        }
      },
      "Appointment": {
        "type": "object", "required": ["id", "leadId", "date", "status"],
        "properties": { "id": { "type": "string" }, "leadId": { "type": "string" }, "leadName": { "type": ["string", "null"] }, "leadPhone": { "type": "string" }, "date": { "type": "string" }, "duration": { "type": "integer" }, "status": { "type": "string", "enum": ["SCHEDULED", "CONFIRMED", "COMPLETED", "NO_SHOW", "CANCELLED"] }, "serviceType": { "type": ["string", "null"] }, "notes": { "type": ["string", "null"] } }
      },
      "CreateAppointmentRequest": {
        "type": "object", "required": ["leadId", "date"],
        "properties": { "leadId": { "type": "string" }, "date": { "type": "string", "description": "ISO 8601 date-time" }, "duration": { "type": "integer", "minimum": 1, "default": 60 }, "serviceType": { "type": "string" }, "notes": { "type": "string" }, "confirm": { "type": "boolean", "default": false } }
      },
      "CreateAppointmentResponse": { "type": "object", "properties": { "needsConfirmation": { "type": "boolean" }, "wouldCreate": { "type": "object" }, "appointment": { "$ref": "#/components/schemas/Appointment" } } },
      "CancelAppointmentResponse": { "type": "object", "properties": { "needsConfirmation": { "type": "boolean" }, "wouldCancel": { "type": "object" }, "appointment": { "type": "object", "properties": { "id": { "type": "string" }, "status": { "type": "string" } } } } },
      "BusinessHours": { "type": "object", "required": ["businessHours", "timezone"], "properties": { "businessHours": { "type": ["object", "null"] }, "timezone": { "type": "string" } } },
      "BusinessHoursUpdateResponse": { "type": "object", "properties": { "needsConfirmation": { "type": "boolean" }, "wouldSet": { "type": "object" }, "businessHours": { "type": ["object", "null"] }, "timezone": { "type": "string" } } }
    }
  }
}
