{
  "openapi": "3.1.0",
  "info": {
    "title": "Craft API",
    "description": "Public API for programmatic access to your Craft data.",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://data.craftflow.co/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/users/": {
      "get": {
        "operationId": "listUsers",
        "summary": "List Users",
        "description": "Retrieve a paginated list of users in your company.",
        "tags": ["Users"],
        "parameters": [
          {
            "name": "is_active",
            "in": "query",
            "description": "Filter by active status.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "ordering",
            "in": "query",
            "description": "Sort results by a field. Prefix with `-` for descending order.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": ["created_at", "-created_at"],
              "default": "-created_at"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Number of results per page.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 25,
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of users.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/User"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  },
                  "required": ["data", "pagination"]
                },
                "example": {
                  "data": [
                    {
                      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                      "email": "jane@example.com",
                      "first_name": "Jane",
                      "last_name": "Smith",
                      "full_name": "Jane Smith",
                      "is_active": true,
                      "permissions": ["call_center_ai_coach", "call_center_ai_record"],
                      "created_at": "2025-06-15T14:30:00Z"
                    },
                    {
                      "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
                      "email": "john@example.com",
                      "first_name": "John",
                      "last_name": "Doe",
                      "full_name": "John Doe",
                      "is_active": true,
                      "permissions": ["ridealong_record"],
                      "created_at": "2025-05-10T09:15:00Z"
                    }
                  ],
                  "pagination": {
                    "count": 2,
                    "page": 1,
                    "page_size": 25,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "detail": "Invalid API key."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "detail": "Request was throttled. Expected available in 60 seconds."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/teams/": {
      "get": {
        "operationId": "listTeams",
        "summary": "List Teams",
        "description": "Retrieve a paginated list of teams in your company.",
        "tags": ["Teams"],
        "parameters": [
          {
            "name": "ordering",
            "in": "query",
            "description": "Sort results by a field. Prefix with `-` for descending order.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": ["created_at", "-created_at"],
              "default": "-created_at"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Number of results per page.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 25,
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of teams.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Team"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  },
                  "required": ["data", "pagination"]
                },
                "example": {
                  "data": [
                    {
                      "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
                      "name": "Sales",
                      "description": "Field sales team",
                      "created_at": "2025-03-01T10:00:00Z"
                    },
                    {
                      "id": "d4e5f6a7-b8c9-0123-defa-234567890123",
                      "name": "Support",
                      "description": "Customer support team",
                      "created_at": "2025-04-15T12:00:00Z"
                    }
                  ],
                  "pagination": {
                    "count": 2,
                    "page": 1,
                    "page_size": 25,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/teams/{id}/": {
      "get": {
        "operationId": "getTeam",
        "summary": "Get Team",
        "description": "Retrieve a single team by its ID.",
        "tags": ["Teams"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier of the team.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A single team.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Team"
                    }
                  },
                  "required": ["data"]
                },
                "example": {
                  "data": {
                    "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
                    "name": "Sales",
                    "description": "Field sales team",
                    "created_at": "2025-03-01T10:00:00Z"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Team not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "detail": "Not found."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/recordings/": {
      "get": {
        "operationId": "listRecordings",
        "summary": "List Field Recordings",
        "description": "Retrieve a paginated list of field recordings in your company.",
        "tags": ["Field Recordings"],
        "parameters": [
          {
            "name": "ordering",
            "in": "query",
            "description": "Sort results by a field. Prefix with `-` for descending order.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": ["created_at", "-created_at"],
              "default": "-created_at"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Number of results per page.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 25,
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of field recordings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FieldRecording"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  },
                  "required": ["data", "pagination"]
                },
                "example": {
                  "data": [
                    {
                      "id": "e5f6a7b8-c9d0-1234-efab-567890123456",
                      "name": "HVAC Install - 123 Main St",
                      "start_time": "2025-07-01T09:00:00Z",
                      "end_time": "2025-07-01T09:45:00Z",
                      "audio_duration_seconds": 2700,
                      "score": 0.85,
                      "user": {
                        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                        "full_name": "John Doe"
                      },
                      "created_at": "2025-07-01T09:00:00Z"
                    }
                  ],
                  "pagination": {
                    "count": 1,
                    "page": 1,
                    "page_size": 25,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/recordings/{id}/": {
      "get": {
        "operationId": "getRecording",
        "summary": "Get Field Recording",
        "description": "Retrieve a single field recording by its ID.",
        "tags": ["Field Recordings"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier of the field recording.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A single field recording.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/FieldRecordingDetail"
                    }
                  },
                  "required": ["data"]
                },
                "example": {
                  "data": {
                    "id": "e5f6a7b8-c9d0-1234-efab-567890123456",
                    "name": "HVAC Install - 123 Main St",
                    "start_time": "2025-07-01T09:00:00Z",
                    "end_time": "2025-07-01T09:45:00Z",
                    "audio_duration_seconds": 2700,
                    "score": 0.85,
                    "user": {
                      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                      "full_name": "John Doe"
                    },
                    "created_at": "2025-07-01T09:00:00Z",
                    "custom_fields": [
                      {
                        "name": "Customer Interest",
                        "slug": "customer-interest",
                        "value_type": "single_select",
                        "value": "High",
                        "source": "ai"
                      },
                      {
                        "name": "Quoted Price",
                        "slug": "quoted-price",
                        "value_type": "number",
                        "value": 12500,
                        "source": "manual"
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Field recording not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "detail": "Not found."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/calls/": {
      "get": {
        "operationId": "listCalls",
        "summary": "List Calls",
        "description": "Retrieve a paginated list of Contact Center calls in your company.",
        "tags": ["Calls"],
        "parameters": [
          {
            "name": "source",
            "in": "query",
            "description": "Filter by call source.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": ["integration", "voice_agent"]
            }
          },
          {
            "name": "ordering",
            "in": "query",
            "description": "Sort results by a field. Prefix with `-` for descending order.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": ["created_at", "-created_at"],
              "default": "-created_at"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Number of results per page.",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 25,
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of calls.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Call"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  },
                  "required": ["data", "pagination"]
                },
                "example": {
                  "data": [
                    {
                      "id": "f6a7b8c9-d0e1-2345-fabc-678901234567",
                      "start_time": "2025-07-02T14:00:00Z",
                      "end_time": "2025-07-02T14:05:00Z",
                      "audio_duration_seconds": 300,
                      "direction": "inbound",
                      "source": "integration",
                      "sentiment": "positive",
                      "score": 0.9,
                      "summary": "Customer called about HVAC repair.",
                      "is_qualified": true,
                      "is_booked": true,
                      "is_excused": false,
                      "excused_at": null,
                      "excuse_reason": null,
                      "is_cancellation": false,
                      "has_cancellation": false,
                      "user": {
                        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                        "full_name": "Jane Smith"
                      },
                      "created_at": "2025-07-02T14:00:00Z"
                    }
                  ],
                  "pagination": {
                    "count": 1,
                    "page": 1,
                    "page_size": 25,
                    "total_pages": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/calls/{id}/": {
      "get": {
        "operationId": "getCall",
        "summary": "Get Call",
        "description": "Retrieve a single call by its ID.",
        "tags": ["Calls"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier of the call.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A single call.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Call"
                    }
                  },
                  "required": ["data"]
                },
                "example": {
                  "data": {
                    "id": "f6a7b8c9-d0e1-2345-fabc-678901234567",
                    "start_time": "2025-07-02T14:00:00Z",
                    "end_time": "2025-07-02T14:05:00Z",
                    "audio_duration_seconds": 300,
                    "direction": "inbound",
                    "source": "integration",
                    "sentiment": "positive",
                    "score": 0.9,
                    "summary": "Customer called about HVAC repair.",
                    "is_qualified": true,
                    "is_booked": true,
                    "is_excused": false,
                    "excused_at": null,
                    "excuse_reason": null,
                    "is_cancellation": false,
                    "has_cancellation": false,
                    "user": {
                      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                      "full_name": "Jane Smith"
                    },
                    "created_at": "2025-07-02T14:00:00Z"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Call not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "detail": "Not found."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/recordings/{id}/transcript/": {
      "get": {
        "operationId": "getRecordingTranscript",
        "summary": "Get Field Recording Transcript",
        "description": "Retrieve the full transcript for a field recording.",
        "tags": ["Field Recordings"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier of the field recording.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The transcript for the field recording.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/FieldRecordingTranscript"
                    }
                  },
                  "required": ["data"]
                },
                "example": {
                  "data": {
                    "id": "e5f6a7b8-c9d0-1234-efab-567890123456",
                    "transcript": [
                      {
                        "speaker": "A",
                        "text": "So let me walk you through what we found with the unit.",
                        "start": 1200,
                        "end": 4800,
                        "words": [
                          {"text": "So", "start": 1200, "end": 1400, "speaker": "A"},
                          {"text": "let", "start": 1450, "end": 1600, "speaker": "A"},
                          {"text": "me", "start": 1650, "end": 1800, "speaker": "A"},
                          {"text": "walk", "start": 1850, "end": 2100, "speaker": "A"},
                          {"text": "you", "start": 2150, "end": 2300, "speaker": "A"},
                          {"text": "through", "start": 2350, "end": 2600, "speaker": "A"},
                          {"text": "what", "start": 2650, "end": 2850, "speaker": "A"},
                          {"text": "we", "start": 2900, "end": 3050, "speaker": "A"},
                          {"text": "found", "start": 3100, "end": 3400, "speaker": "A"},
                          {"text": "with", "start": 3450, "end": 3650, "speaker": "A"},
                          {"text": "the", "start": 3700, "end": 3850, "speaker": "A"},
                          {"text": "unit.", "start": 3900, "end": 4800, "speaker": "A"}
                        ]
                      },
                      {
                        "speaker": "B",
                        "text": "Okay, sounds good.",
                        "start": 5200,
                        "end": 6500,
                        "words": [
                          {"text": "Okay,", "start": 5200, "end": 5600, "speaker": "B"},
                          {"text": "sounds", "start": 5700, "end": 6000, "speaker": "B"},
                          {"text": "good.", "start": 6050, "end": 6500, "speaker": "B"}
                        ]
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Field recording not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "detail": "Not found."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/calls/{id}/transcript/": {
      "get": {
        "operationId": "getCallTranscript",
        "summary": "Get Call Transcript",
        "description": "Retrieve the full transcript for a call.",
        "tags": ["Calls"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier of the call.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The transcript for the call.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallTranscript"
                    }
                  },
                  "required": ["data"]
                },
                "example": {
                  "data": {
                    "id": "f6a7b8c9-d0e1-2345-fabc-678901234567",
                    "transcript": [
                      {
                        "speaker": "CSR",
                        "text": "Thank you for calling, how can I help you today?",
                        "start": 0,
                        "end": 2800
                      },
                      {
                        "speaker": "Customer",
                        "text": "Hi, my AC stopped working and it's really hot in here.",
                        "start": 3200,
                        "end": 6500
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Call not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "detail": "Not found."
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/users/{id}/": {
      "get": {
        "operationId": "getUser",
        "summary": "Get User",
        "description": "Retrieve a single user by their ID.",
        "tags": ["Users"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The unique identifier of the user.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A single user.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/User"
                    }
                  },
                  "required": ["data"]
                },
                "example": {
                  "data": {
                    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                    "email": "jane@example.com",
                    "first_name": "Jane",
                    "last_name": "Smith",
                    "full_name": "Jane Smith",
                    "is_active": true,
                    "permissions": ["call_center_ai_coach", "call_center_ai_record"],
                    "created_at": "2025-06-15T14:30:00Z"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "User not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "detail": "Not found."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/outbound-campaigns/webhook/{webhook_token}/": {
      "post": {
        "operationId": "addAudienceMember",
        "summary": "Add Audience Member (Webhook)",
        "description": "Add a single contact to an outbound campaign audience via a public webhook. The contact is automatically enrolled in any active campaigns targeting this audience.\n\nNo API key or auth header is required — the webhook token in the URL is the authentication. You can find your webhook URL on the audience detail page in Craft.\n\nIdempotent: if a contact with the same phone number or email already exists in the audience, the existing contact is returned without creating a duplicate.",
        "tags": ["Outbound Campaigns"],
        "security": [],
        "parameters": [
          {
            "name": "webhook_token",
            "in": "path",
            "description": "The audience's unique webhook token. Found on the audience detail page in Craft.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "first_name": {
                    "type": "string",
                    "description": "Contact's first name. Available as `{{first_name}}` in campaign message templates.",
                    "maxLength": 255
                  },
                  "last_name": {
                    "type": "string",
                    "description": "Contact's last name. Available as `{{last_name}}` in campaign message templates.",
                    "maxLength": 255
                  },
                  "phone": {
                    "type": "string",
                    "description": "Phone number. Accepts most common formats (`+15551234567`, `555-123-4567`, `(555) 123-4567`). At least one of `phone` or `email` is required.",
                    "maxLength": 50
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Email address. At least one of `phone` or `email` is required."
                  },
                  "custom_fields": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Arbitrary key-value pairs. Each key becomes a template variable in campaign messages (e.g., `{\"service_type\": \"HVAC\"}` → `{{service_type}}`)."
                  }
                }
              },
              "example": {
                "first_name": "Jane",
                "last_name": "Doe",
                "phone": "5551234567",
                "email": "jane@example.com",
                "custom_fields": {
                  "service_type": "HVAC",
                  "source": "website"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Contact created and enrolled in active campaigns.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceMember"
                },
                "example": {
                  "id": "f1e2d3c4-b5a6-7890-cdef-123456789012",
                  "created_at": "2026-03-27T14:30:00Z",
                  "first_name": "Jane",
                  "last_name": "Doe",
                  "phone": "+15551234567",
                  "email": "jane@example.com",
                  "custom_fields": {
                    "service_type": "HVAC",
                    "source": "website"
                  },
                  "status": "active"
                }
              }
            }
          },
          "200": {
            "description": "Contact with this phone or email already exists. Existing contact returned (idempotent).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceMember"
                }
              }
            }
          },
          "400": {
            "description": "Validation error — missing phone and email, invalid email format, or audience is at capacity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "detail": "At least one of 'phone' or 'email' is required."
                  }
                }
              }
            }
          },
          "404": {
            "description": "Webhook token is invalid or the audience has been deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key, prefixed with `cf_live_`."
      }
    },
    "schemas": {
      "AudienceMember": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the audience member."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the member was added to the audience."
          },
          "first_name": {
            "type": "string",
            "description": "Contact's first name."
          },
          "last_name": {
            "type": "string",
            "description": "Contact's last name."
          },
          "phone": {
            "type": "string",
            "description": "Phone number in E.164 format."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email address."
          },
          "custom_fields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Custom key-value pairs attached to this contact."
          },
          "status": {
            "type": "string",
            "enum": ["active", "opted_out", "do_not_contact"],
            "description": "Current status of the audience member."
          }
        },
        "required": ["id", "created_at", "first_name", "last_name", "phone", "email", "custom_fields", "status"]
      },
      "User": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the user."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "The user's email address."
          },
          "first_name": {
            "type": "string",
            "description": "The user's first name."
          },
          "last_name": {
            "type": "string",
            "description": "The user's last name."
          },
          "full_name": {
            "type": "string",
            "description": "The user's full display name."
          },
          "is_active": {
            "type": "boolean",
            "description": "Whether the user is currently active."
          },
          "permissions": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "ridealong_admin",
                "ridealong_coach",
                "ridealong_record",
                "call_center_ai_admin",
                "call_center_ai_coach",
                "call_center_ai_record",
                "inside_sales_admin",
                "inside_sales_rep"
              ]
            },
            "description": "Permission codes assigned to the user."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the user was created (ISO 8601)."
          }
        },
        "required": ["id", "email", "first_name", "last_name", "full_name", "is_active", "permissions", "created_at"]
      },
      "Team": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the team."
          },
          "name": {
            "type": "string",
            "description": "The team's name."
          },
          "description": {
            "type": "string",
            "description": "A description of the team."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the team was created (ISO 8601)."
          }
        },
        "required": ["id", "name", "description", "created_at"]
      },
      "FieldRecordingWord": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "description": "The individual word."
          },
          "start": {
            "type": "integer",
            "description": "Word start time in milliseconds from the beginning of the recording."
          },
          "end": {
            "type": "integer",
            "description": "Word end time in milliseconds from the beginning of the recording."
          },
          "speaker": {
            "type": "string",
            "description": "Speaker label (e.g. \"A\", \"B\")."
          }
        },
        "required": ["text", "start", "end", "speaker"]
      },
      "FieldRecordingUtterance": {
        "type": "object",
        "properties": {
          "speaker": {
            "type": "string",
            "description": "Speaker label (e.g. \"A\", \"B\")."
          },
          "text": {
            "type": "string",
            "description": "The spoken text of the utterance."
          },
          "start": {
            "type": "integer",
            "description": "Start time in milliseconds from the beginning of the recording."
          },
          "end": {
            "type": "integer",
            "description": "End time in milliseconds from the beginning of the recording."
          },
          "words": {
            "type": "array",
            "description": "Word-level timing and speaker data.",
            "items": {
              "$ref": "#/components/schemas/FieldRecordingWord"
            }
          }
        },
        "required": ["speaker", "text", "start", "end", "words"]
      },
      "CallUtterance": {
        "type": "object",
        "properties": {
          "speaker": {
            "type": "string",
            "description": "Speaker role (e.g. \"CSR\", \"Customer\")."
          },
          "text": {
            "type": "string",
            "description": "The spoken text of the utterance."
          },
          "start": {
            "type": "integer",
            "description": "Start time in milliseconds from the beginning of the call."
          },
          "end": {
            "type": "integer",
            "description": "End time in milliseconds from the beginning of the call."
          }
        },
        "required": ["speaker", "text", "start"]
      },
      "NestedUser": {
        "type": "object",
        "description": "The user associated with this record, or null if none.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the user."
          },
          "full_name": {
            "type": "string",
            "description": "The user's full display name."
          }
        },
        "required": ["id", "full_name"]
      },
      "FieldRecording": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the field recording."
          },
          "name": {
            "type": ["string", "null"],
            "description": "Name or label for the field recording."
          },
          "start_time": {
            "type": "string",
            "format": "date-time",
            "description": "When the field recording started (ISO 8601)."
          },
          "end_time": {
            "type": "string",
            "format": "date-time",
            "description": "When the field recording ended (ISO 8601)."
          },
          "audio_duration_seconds": {
            "type": "integer",
            "description": "Duration of the audio in seconds."
          },
          "score": {
            "type": "number",
            "description": "Overall score for the field recording (0.0 - 1.0)."
          },
          "user": {
            "oneOf": [
              { "$ref": "#/components/schemas/NestedUser" },
              { "type": "null" }
            ],
            "description": "The technician who made the field recording, or null."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the field recording was created (ISO 8601)."
          }
        },
        "required": ["id", "name", "start_time", "end_time", "audio_duration_seconds", "score", "user", "created_at"]
      },
      "FieldRecordingCustomField": {
        "type": "object",
        "description": "A custom field value attached to a field recording. Custom fields are configured per company and can be AI-computed or manually entered.",
        "properties": {
          "name": {
            "type": "string",
            "description": "Human-readable name of the custom field."
          },
          "slug": {
            "type": "string",
            "description": "Stable machine-readable identifier for the custom field, unique within the company."
          },
          "value_type": {
            "type": "string",
            "enum": ["text", "number", "boolean", "single_select", "multi_select"],
            "description": "The data type of the field's value. Determines the JSON type returned in `value`."
          },
          "value": {
            "oneOf": [
              { "type": "string" },
              { "type": "number" },
              { "type": "boolean" },
              { "type": "array", "items": { "type": "string" } }
            ],
            "description": "The field's value. The JSON type depends on `value_type`: `text` → string, `number` → number, `boolean` → boolean, `single_select` → string, `multi_select` → array of strings."
          },
          "source": {
            "type": "string",
            "enum": ["ai", "manual"],
            "description": "Whether the value was produced by AI extraction from the transcript or manually entered."
          }
        },
        "required": ["name", "slug", "value_type", "value", "source"]
      },
      "FieldRecordingDetail": {
        "allOf": [
          { "$ref": "#/components/schemas/FieldRecording" },
          {
            "type": "object",
            "properties": {
              "custom_fields": {
                "type": "array",
                "description": "Custom field values attached to this recording. Returned only on the retrieve endpoint, not on list.",
                "items": {
                  "$ref": "#/components/schemas/FieldRecordingCustomField"
                }
              }
            },
            "required": ["custom_fields"]
          }
        ]
      },
      "Call": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the call."
          },
          "start_time": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "When the call started (ISO 8601)."
          },
          "end_time": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "When the call ended (ISO 8601)."
          },
          "audio_duration_seconds": {
            "type": "integer",
            "description": "Duration of the call audio in seconds."
          },
          "direction": {
            "type": "string",
            "enum": ["inbound", "outbound"],
            "description": "Whether the call was inbound or outbound."
          },
          "source": {
            "type": "string",
            "enum": ["integration", "voice_agent"],
            "description": "The source of the call."
          },
          "sentiment": {
            "type": "string",
            "enum": ["positive", "neutral", "negative"],
            "description": "Detected sentiment of the call."
          },
          "score": {
            "type": "number",
            "description": "Overall score for the call (0.0 - 1.0)."
          },
          "summary": {
            "type": "string",
            "description": "AI-generated summary of the call."
          },
          "is_qualified": {
            "type": "boolean",
            "description": "Whether the call was qualified as a lead."
          },
          "is_booked": {
            "type": "boolean",
            "description": "Whether an appointment was booked on the call."
          },
          "is_excused": {
            "type": "boolean",
            "description": "Whether the call has been excused."
          },
          "excused_at": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "When the call was excused (ISO 8601), or null."
          },
          "excuse_reason": {
            "type": ["string", "null"],
            "description": "Reason the call was excused, or null."
          },
          "is_cancellation": {
            "type": "boolean",
            "description": "Whether the call is a cancellation call."
          },
          "has_cancellation": {
            "type": "boolean",
            "description": "Whether a cancellation is associated with this call."
          },
          "user": {
            "oneOf": [
              { "$ref": "#/components/schemas/NestedUser" },
              { "type": "null" }
            ],
            "description": "The CSR who handled the call, or null (e.g. for voice agent calls)."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the call record was created (ISO 8601)."
          }
        },
        "required": ["id", "start_time", "end_time", "audio_duration_seconds", "direction", "source", "sentiment", "score", "summary", "is_qualified", "is_booked", "is_excused", "excused_at", "excuse_reason", "is_cancellation", "has_cancellation", "user", "created_at"]
      },
      "FieldRecordingTranscript": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the field recording."
          },
          "transcript": {
            "type": "array",
            "description": "List of transcript utterances with speaker labels and timing.",
            "items": {
              "$ref": "#/components/schemas/FieldRecordingUtterance"
            }
          }
        },
        "required": ["id", "transcript"]
      },
      "CallTranscript": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for the call."
          },
          "transcript": {
            "type": "array",
            "description": "List of transcript utterances with speaker labels and timing.",
            "items": {
              "$ref": "#/components/schemas/CallUtterance"
            }
          }
        },
        "required": ["id", "transcript"]
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer",
            "description": "Total number of results across all pages."
          },
          "page": {
            "type": "integer",
            "description": "Current page number."
          },
          "page_size": {
            "type": "integer",
            "description": "Number of results on this page."
          },
          "total_pages": {
            "type": "integer",
            "description": "Total number of pages."
          }
        },
        "required": ["count", "page", "page_size", "total_pages"]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "detail": {
                "type": "string",
                "description": "Human-readable error message."
              }
            },
            "required": ["detail"]
          }
        },
        "required": ["error"]
      }
    }
  }
}
