{
  "openapi": "3.1.0",
  "info": {
    "title": "LeadlyIVR Public API",
    "version": "1.0.0",
    "description": "Tenant-scoped REST API for integrating external applications with LeadlyIVR — read call data, live agent presence, numbers and IVR flows, place click-to-call calls, and transfer live calls. Authenticate with an API key created in the LeadlyIVR dashboard (API Keys page). Subscribe to outbound webhooks (dashboard → Webhooks) for real-time call events.",
    "contact": {
      "url": "https://leadlyivr.com/contact"
    }
  },
  "servers": [
    {
      "url": "https://api.leadlyivr.com/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "apiKeyHeader": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Authorization: Bearer lk_live_… (API key from the dashboard; shown once at creation)"
      },
      "apiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      }
    },
    "schemas": {
      "Call": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "call_uuid": {
            "type": "string",
            "description": "Carrier call UUID — use as :callUuid in API paths"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "from_number": {
            "type": "string",
            "example": "919008413315"
          },
          "to_number": {
            "type": "string",
            "example": "918065951644"
          },
          "status": {
            "type": "string",
            "enum": [
              "in_progress",
              "completed",
              "missed",
              "no_answer",
              "voicemail"
            ]
          },
          "digits_path": {
            "type": [
              "string",
              "null"
            ],
            "description": "IVR keys the caller pressed, e.g. \"1>2\"",
            "example": "1"
          },
          "duration_seconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "recording_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "ended_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "agent_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Agent who handled the call, if any"
          },
          "department_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_ai": {
            "type": "boolean",
            "description": "True when the AI voice agent handled the call"
          },
          "ai_summary": {
            "type": [
              "string",
              "null"
            ],
            "description": "AI-generated summary of the conversation (AI calls)"
          },
          "ai_transcript": {
            "type": [
              "string",
              "null"
            ],
            "description": "Caller/agent transcript (AI calls)"
          }
        }
      },
      "Agent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "pstn_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Agent's phone number for PSTN ringing"
          },
          "online": {
            "type": "boolean",
            "description": "Live softphone presence"
          }
        }
      },
      "NumberRow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "e164": {
            "type": "string",
            "example": "918065951644"
          },
          "flow_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "IVR flow answering this number"
          }
        }
      },
      "Flow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AnalyticsSummary": {
        "type": "object",
        "properties": {
          "totals": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer"
              },
              "inbound": {
                "type": "integer"
              },
              "outbound": {
                "type": "integer"
              },
              "answered": {
                "type": "integer"
              },
              "voicemail": {
                "type": "integer"
              },
              "missed": {
                "type": "integer"
              },
              "today": {
                "type": "integer"
              },
              "calls_this_month": {
                "type": "integer"
              },
              "avg_duration_seconds": {
                "type": "integer"
              },
              "minutes_this_month": {
                "type": "integer"
              }
            }
          },
          "daily": {
            "type": "array",
            "description": "Calls per day, last 7 days",
            "items": {
              "type": "object",
              "properties": {
                "day": {
                  "type": "string",
                  "format": "date"
                },
                "calls": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "API key lacks the required scope",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded (default 120 requests / 60 s per key)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/calls": {
      "get": {
        "summary": "List calls",
        "description": "Most-recent-first call log for your workspace. Requires scope calls:read.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "in_progress",
                "completed",
                "missed",
                "no_answer",
                "voicemail"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of calls",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Call"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "summary": "Place a call (click-to-call)",
        "description": "Rings the agent first, then bridges to the destination. Requires scope calls:write.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to",
                  "agentId"
                ],
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Destination number (E.164, no +)",
                    "example": "919812345678"
                  },
                  "agentId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Agent to place the call as (see GET /agents)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Call origination accepted — the agent’s phone rings first, then the customer is bridged. Track progress via the call.started and call.completed webhooks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "requestUuid": {
                      "type": "string",
                      "description": "The call UUID — matches call_uuid in webhook events and GET /calls"
                    },
                    "message": {
                      "type": "string",
                      "example": "call fired"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid to or agentId",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Plan limit reached or insufficient credits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/calls/{callUuid}": {
      "get": {
        "summary": "Get one call",
        "description": "Look up a call by its call UUID. Requires scope calls:read.",
        "parameters": [
          {
            "name": "callUuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The call",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "404": {
            "description": "Call not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/calls/{callUuid}/transfer": {
      "post": {
        "summary": "Transfer a live call",
        "description": "Moves the caller of an in-progress call to another number/agent. Requires scope calls:write.",
        "parameters": [
          {
            "name": "callUuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to"
                ],
                "properties": {
                  "to": {
                    "type": "string",
                    "example": "919876543210"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transfer initiated"
          },
          "400": {
            "description": "Call not transferable / bad destination",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Call not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/analytics/summary": {
      "get": {
        "summary": "Analytics summary",
        "description": "Aggregate call counts, monthly minutes and a 7-day daily series. Requires scope analytics:read.",
        "responses": {
          "200": {
            "description": "Summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsSummary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/agents": {
      "get": {
        "summary": "List agents (with live presence)",
        "description": "All agents in the workspace with online (softphone presence). Requires scope agents:read.",
        "responses": {
          "200": {
            "description": "Array of agents",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Agent"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/numbers": {
      "get": {
        "summary": "List numbers",
        "description": "Virtual numbers rented by the workspace. Requires scope numbers:read.",
        "responses": {
          "200": {
            "description": "Array of numbers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NumberRow"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/flows": {
      "get": {
        "summary": "List IVR flows",
        "description": "IVR flows defined in the workspace. Requires scope flows:read.",
        "responses": {
          "200": {
            "description": "Array of flows",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Flow"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    }
  },
  "webhooks": {
    "call.started": {
      "post": {
        "summary": "Inbound call started",
        "description": "POSTed to your subscribed endpoint (dashboard → Webhooks) when a call begins. All webhook events share the same envelope; verify X-LeadlyIVR-Signature before trusting. Other events: call.answered, call.completed, call.no_answer (data.voicemail=true when a voicemail was left), recording.ready, webhook.test.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "example": "evt_9f2c…"
                  },
                  "event": {
                    "type": "string",
                    "example": "call.started"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "tenant_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "data": {
                    "$ref": "#/components/schemas/Call"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx to acknowledge. Non-2xx retries with backoff (1m/5m/30m/2h/6h, 5 attempts)."
          }
        }
      }
    }
  }
}