{
  "openapi": "3.1.0",
  "info": {
    "title": "AirFone API",
    "version": "2026-10-04",
    "description": "Fallback copy committed with the portal. The build replaces it with the live document when AIRFONE_OPENAPI_URL is set."
  },
  "servers": [
    {
      "url": "https://api.airfone.app"
    }
  ],
  "paths": {
    "/v1/calls": {
      "post": {
        "summary": "Start an outbound AI call",
        "tags": [
          "Calls"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "calls:write",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Makes a retried POST safe. Any unique string, kept 24 hours."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "E.164 number to call"
                  },
                  "agent_id": {
                    "type": "string",
                    "description": "Assistant that handles the call"
                  },
                  "from": {
                    "type": "string",
                    "description": "Your AirFone number, E.164"
                  },
                  "context": {
                    "type": "object",
                    "description": "Facts the assistant may use on this call"
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Your own key-value pairs, returned in webhooks"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "List calls",
        "tags": [
          "Calls"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "calls:read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "1 to 100, default 20"
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Cursor from the previous page"
          }
        ]
      }
    },
    "/v1/calls/{id}": {
      "get": {
        "summary": "Get a call",
        "tags": [
          "Calls"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "calls:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Call id, call_..."
          }
        ]
      }
    },
    "/v1/calls/{id}/transfer": {
      "post": {
        "summary": "Transfer a live call",
        "tags": [
          "Calls"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "calls:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Call id"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Makes a retried POST safe. Any unique string, kept 24 hours."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "type": "string",
                    "description": "Number or user to transfer to"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/calls/{id}/end": {
      "post": {
        "summary": "End a live call",
        "tags": [
          "Calls"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "calls:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Call id"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Makes a retried POST safe. Any unique string, kept 24 hours."
          }
        ]
      }
    },
    "/v1/calls/{id}/transcript": {
      "get": {
        "summary": "Get a call transcript",
        "tags": [
          "Calls"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "calls:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Call id"
          }
        ]
      }
    },
    "/v1/messages": {
      "post": {
        "summary": "Send a message",
        "tags": [
          "Messages"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "messages:write",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Makes a retried POST safe. Any unique string, kept 24 hours."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "channel": {
                    "type": "string",
                    "description": "whatsapp or sms"
                  },
                  "to": {
                    "type": "string",
                    "description": "E.164 number"
                  },
                  "text": {
                    "type": "string",
                    "description": "Message text"
                  },
                  "template": {
                    "type": "object",
                    "description": "WhatsApp template name, language and parameters, required outside the 24 hour window"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/conversations": {
      "get": {
        "summary": "List conversations",
        "tags": [
          "Messages"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "messages:read"
      }
    },
    "/v1/conversations/{id}/messages": {
      "get": {
        "summary": "List messages in a conversation",
        "tags": [
          "Messages"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "messages:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Conversation id"
          }
        ]
      }
    },
    "/v1/agents": {
      "get": {
        "summary": "List assistants",
        "tags": [
          "Agents"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "agents:read"
      }
    },
    "/v1/knowledge": {
      "post": {
        "summary": "Add a knowledge document",
        "tags": [
          "Agents"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "agents:write",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Makes a retried POST safe. Any unique string, kept 24 hours."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "agent_id": {
                    "type": "string",
                    "description": "Assistant"
                  },
                  "title": {
                    "type": "string",
                    "description": "Title"
                  },
                  "text": {
                    "type": "string",
                    "description": "Content"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/contacts": {
      "get": {
        "summary": "List contacts",
        "tags": [
          "Contacts"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "contacts:read"
      },
      "post": {
        "summary": "Create or update a contact",
        "tags": [
          "Contacts"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "contacts:write",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Makes a retried POST safe. Any unique string, kept 24 hours."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "phone": {
                    "type": "string",
                    "description": "E.164"
                  },
                  "name": {
                    "type": "string",
                    "description": "Name"
                  },
                  "external_id": {
                    "type": "string",
                    "description": "Your id for this person"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/records": {
      "post": {
        "summary": "Push records about a contact",
        "tags": [
          "Contacts"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "records:write",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Makes a retried POST safe. Any unique string, kept 24 hours."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "contact": {
                    "type": "object",
                    "description": "phone or external_id"
                  },
                  "records": {
                    "type": "array",
                    "description": "Records shown to staff and given to the assistant"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/bulk-calls": {
      "post": {
        "summary": "Start a bulk call campaign",
        "tags": [
          "Calls"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "calls:write",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Makes a retried POST safe. Any unique string, kept 24 hours."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "agent_id": {
                    "type": "string",
                    "description": "Assistant"
                  },
                  "form_id": {
                    "type": "string",
                    "description": "Form to fill on each call"
                  },
                  "contacts": {
                    "type": "array",
                    "description": "Numbers to call"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints": {
      "get": {
        "summary": "List webhook endpoints",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "webhooks:read"
      },
      "post": {
        "summary": "Create a webhook endpoint",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "webhooks:write",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Makes a retried POST safe. Any unique string, kept 24 hours."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "HTTPS URL"
                  },
                  "events": {
                    "type": "array",
                    "description": "Event types, or [\"*\"]"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/events": {
      "get": {
        "summary": "List events",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "events:read"
      }
    },
    "/v1/usage": {
      "get": {
        "summary": "Get usage for a period",
        "tags": [
          "Usage"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "usage:read",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "ISO date"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "ISO date"
          }
        ]
      }
    },
    "/v1/connectors": {
      "get": {
        "summary": "List connectors you publish",
        "tags": [
          "Connectors"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "connectors:read"
      }
    },
    "/v1/connectors/{id}/versions": {
      "post": {
        "summary": "Upload a connector version",
        "tags": [
          "Connectors"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "x-airfone-scope": "connectors:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Connector id, reverse DNS"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Makes a retried POST safe. Any unique string, kept 24 hours."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "bundle": {
                    "type": "string",
                    "description": "Signed tar bundle, base64"
                  },
                  "signature": {
                    "type": "string",
                    "description": "Ed25519 signature of the bundle's sha256"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/session": {
      "post": {
        "summary": "Open a widget session",
        "tags": [
          "Widget"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "description": "Called by widget.js with a publishable key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "visitor_id": {
                    "type": "string",
                    "description": "Stable id for this browser"
                  },
                  "identity": {
                    "type": "object",
                    "description": "user_id and user_hash for a verified visitor"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/messages": {
      "post": {
        "summary": "Send a widget message",
        "tags": [
          "Widget"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Makes a retried POST safe. Any unique string, kept 24 hours."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "Visitor's message"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/widget/stream": {
      "get": {
        "summary": "Stream widget events (SSE)",
        "tags": [
          "Widget"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Session token"
          }
        ]
      }
    },
    "/v1/widget/voice": {
      "post": {
        "summary": "Start a browser voice call",
        "tags": [
          "Widget"
        ],
        "responses": {
          "200": {
            "description": "Object"
          }
        },
        "description": "Returns a LiveKit URL, room and token. The assistant joins the room."
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Secret (af_sk_...) or restricted (af_rk_...) key"
      }
    }
  },
  "security": [
    {
      "bearer": []
    }
  ]
}