{
  "openapi": "3.1.0",
  "info": {
    "title": "echowin Public API",
    "version": "1.0.0",
    "summary": "REST API for managing echowin AI agents, knowledgebases, contacts, calls, and chats.",
    "description": "The echowin public API lets you manage AI agents, knowledgebase content, CRM contacts, and conversation history (calls and chats) for your team.\n\n**Authentication**: every request must carry an API key in the `X-API-Key` header. Create keys in the portal under Build > Integrations > API Keys (https://echo.win/portal/integrations?tab=api-keys).\n\n**Agency scoping**: API keys owned by a team that owns an agency may pass `?teamId=<subteam-team-id>` on any endpoint to operate on one of their client subteams. Rate limits always apply to the key-owning team, regardless of `teamId`.\n\n**Rate limits** (per minute, per API-key team): standard reads 100, writes 60, knowledgebase search 30, bulk operations 10. 429 responses include `Retry-After` and `X-RateLimit-*` headers. Webpage refreshes are additionally limited to once per webpage per 24 hours.\n\n**Errors**: all errors use the shape `{ \"error\": string, \"code\"?: string, \"hint\"?: string }`; 400 validation failures may add a `details` array of per-field issues.\n\n**Credits**: knowledgebase search endpoints consume 1 credit per query from the team's balance.",
    "contact": {
      "name": "echowin support",
      "url": "https://echo.win/contactus"
    }
  },
  "externalDocs": {
    "description": "Human-readable API documentation",
    "url": "https://echo.win/api-docs"
  },
  "servers": [
    {
      "url": "https://echo.win/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Identity",
      "description": "Who the current API key belongs to and what it may do."
    },
    {
      "name": "Agents",
      "description": "AI agents and their instructions."
    },
    {
      "name": "Agent knowledgebase",
      "description": "Knowledgebase content addressed through the agent that owns it."
    },
    {
      "name": "Knowledgebase",
      "description": "Knowledgebase content addressed by knowledgebase id."
    },
    {
      "name": "Calls",
      "description": "Phone call logs, transcripts, recordings."
    },
    {
      "name": "Chats",
      "description": "Web, web-agent, voice-widget, and WhatsApp conversations."
    },
    {
      "name": "Contacts",
      "description": "CRM contacts, notes, assignments."
    },
    {
      "name": "Boards",
      "description": "Kanban boards for organizing contacts."
    },
    {
      "name": "Tags",
      "description": "Contact tags."
    },
    {
      "name": "Agency",
      "description": "Agency-only endpoints for provisioning client subteams."
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "operationId": "getCurrentIdentity",
        "tags": [
          "Identity"
        ],
        "summary": "Describe the current API key",
        "description": "Returns the team the API key belongs to, whether that team owns an agency (so ?teamId is available), the team's credit balance and its subscription. The first call an integration should make. The key itself is never returned. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The identity behind the API key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Identity"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The team no longer exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agents": {
      "get": {
        "operationId": "listAgents",
        "tags": [
          "Agents"
        ],
        "summary": "List agents",
        "description": "Returns every AI agent belonging to the authenticated team, newest first, with the knowledgebase each agent is linked to. Use the returned agent ids with the other /agents endpoints. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The team's agents.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Agent"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agents/{agentId}/instructions": {
      "parameters": [
        {
          "name": "agentId",
          "in": "path",
          "required": true,
          "description": "Unique id of the agent.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getAgentInstructions",
        "tags": [
          "Agents"
        ],
        "summary": "Get agent instructions",
        "description": "Returns the current natural-language instructions (system prompt) for one agent. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The agent's instructions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AgentInstructions"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Agent not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "operationId": "updateAgentInstructions",
        "tags": [
          "Agents"
        ],
        "summary": "Update agent instructions",
        "description": "Replaces the agent's instructions. The previous version is saved to the agent's instruction history (last 20 versions kept) and a prompt-optimization job is queued so the change takes effect on upcoming conversations. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The new instructions.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "instructions"
                ],
                "properties": {
                  "instructions": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 100000,
                    "description": "Full replacement instruction text for the agent."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Instructions updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AgentInstructions"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Agent not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agents/{agentId}/knowledgebase": {
      "parameters": [
        {
          "name": "agentId",
          "in": "path",
          "required": true,
          "description": "Unique id of the agent.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getAgentKnowledgebase",
        "tags": [
          "Agent knowledgebase"
        ],
        "summary": "Get an agent's knowledgebase",
        "description": "Returns the knowledgebase assigned to the agent, including its webpage sources, uploaded documents, and manually-created Q&A references. Responds 404 if the agent has no knowledgebase assigned. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The agent and its knowledgebase.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "agent": {
                          "$ref": "#/components/schemas/AgentRef"
                        },
                        "knowledgebase": {
                          "type": "object",
                          "description": "A knowledgebase and all of its content sources.",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "name": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "description": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "webpages": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/KnowledgebaseWebpage"
                              }
                            },
                            "documents": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/KnowledgebaseDocument"
                              }
                            },
                            "references": {
                              "type": "array",
                              "description": "Manually-created Q&A references (excludes entries extracted from webpages/documents).",
                              "items": {
                                "$ref": "#/components/schemas/KnowledgebaseReference"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Agent not found, or the agent has no knowledgebase assigned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agents/{agentId}/knowledgebase/search": {
      "parameters": [
        {
          "name": "agentId",
          "in": "path",
          "required": true,
          "description": "Unique id of the agent.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "searchAgentKnowledgebase",
        "tags": [
          "Agent knowledgebase"
        ],
        "summary": "Search an agent's knowledgebase",
        "description": "Runs a semantic (vector-similarity) search across the agent's knowledgebase and returns the best-matching references with similarity scores. Each search consumes 1 credit from the team's balance. Rate limit: search (30 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "Natural-language search query to run against the agent's knowledgebase. 1–2000 characters.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2000
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of results to return (1–50).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          },
          {
            "name": "threshold",
            "in": "query",
            "required": false,
            "description": "Minimum cosine-similarity score (0–1) a reference must reach to be included.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 1,
              "default": 0.1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results ordered by similarity, best match first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/KnowledgebaseSearchResult"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "description": "Echo of the search inputs plus billing info.",
                      "properties": {
                        "agentId": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "agentName": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "knowledgebaseId": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "query": {
                          "type": "string",
                          "description": "The query that was run."
                        },
                        "resultCount": {
                          "type": "integer",
                          "description": "Number of results returned."
                        },
                        "limit": {
                          "type": "integer",
                          "description": "Applied result limit."
                        },
                        "threshold": {
                          "type": "number",
                          "description": "Applied similarity threshold."
                        },
                        "creditsUsed": {
                          "type": "integer",
                          "description": "Credits consumed by this search (always 1)."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Agent not found, or the agent has no knowledgebase assigned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agents/{agentId}/knowledgebase/references": {
      "parameters": [
        {
          "name": "agentId",
          "in": "path",
          "required": true,
          "description": "Unique id of the agent.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "operationId": "createAgentKnowledgebaseReference",
        "tags": [
          "Agent knowledgebase"
        ],
        "summary": "Create a Q&A reference in an agent's knowledgebase",
        "description": "Adds a manually-authored question/answer pair to the agent's knowledgebase. An embedding is generated immediately so the reference is searchable right away. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The Q&A pair to add.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "question": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 5000,
                    "description": "The question half of the Q&A pair."
                  },
                  "answer": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50000,
                    "description": "The answer half of the Q&A pair."
                  }
                },
                "required": [
                  "question",
                  "answer"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Reference created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseReference"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Agent not found, or the agent has no knowledgebase assigned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agents/{agentId}/knowledgebase/references/{referenceId}": {
      "parameters": [
        {
          "name": "agentId",
          "in": "path",
          "required": true,
          "description": "Unique id of the agent.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "referenceId",
          "in": "path",
          "required": true,
          "description": "Unique id of the Q&A reference.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getAgentKnowledgebaseReference",
        "tags": [
          "Agent knowledgebase"
        ],
        "summary": "Get a Q&A reference from an agent's knowledgebase",
        "description": "Returns a single question/answer reference from the agent's knowledgebase. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The reference.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseReference"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Agent, knowledgebase, or reference not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "operationId": "updateAgentKnowledgebaseReference",
        "tags": [
          "Agent knowledgebase"
        ],
        "summary": "Update a Q&A reference in an agent's knowledgebase",
        "description": "Updates the question and/or answer of a reference in the agent's knowledgebase. When either field changes, the embedding is regenerated so search stays accurate. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Fields to update; omitted fields keep their current value.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "question": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 5000,
                    "description": "The question half of the Q&A pair."
                  },
                  "answer": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50000,
                    "description": "The answer half of the Q&A pair."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reference updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseReference"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Agent, knowledgebase, or reference not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agents/{agentId}/knowledgebase/documents/{documentId}": {
      "parameters": [
        {
          "name": "agentId",
          "in": "path",
          "required": true,
          "description": "Unique id of the agent.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "documentId",
          "in": "path",
          "required": true,
          "description": "Unique id of the uploaded document.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getAgentKnowledgebaseDocument",
        "tags": [
          "Agent knowledgebase"
        ],
        "summary": "Get a document from an agent's knowledgebase",
        "description": "Returns an uploaded document from the agent's knowledgebase, including the Q&A content extracted from it. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The document with its extracted content.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseDocumentDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Agent, knowledgebase, or document not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "operationId": "updateAgentKnowledgebaseDocument",
        "tags": [
          "Agent knowledgebase"
        ],
        "summary": "Update a document in an agent's knowledgebase",
        "description": "Updates the display name and/or notes of an uploaded document in the agent's knowledgebase. The file itself cannot be changed via the API. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Fields to update; omitted fields keep their current value.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255,
                    "description": "Display name for the document."
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 5000,
                    "description": "Free-form notes shown to the AI alongside the document."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Document updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseDocument"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Agent, knowledgebase, or document not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agents/{agentId}/knowledgebase/webpages/{webpageId}": {
      "parameters": [
        {
          "name": "agentId",
          "in": "path",
          "required": true,
          "description": "Unique id of the agent.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "webpageId",
          "in": "path",
          "required": true,
          "description": "Unique id of the webpage source.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getAgentKnowledgebaseWebpage",
        "tags": [
          "Agent knowledgebase"
        ],
        "summary": "Get a webpage source from an agent's knowledgebase",
        "description": "Returns a webpage source from the agent's knowledgebase, including the Q&A content extracted from the page. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The webpage with its extracted content.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseWebpageDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Agent, knowledgebase, or webpage not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "operationId": "updateAgentKnowledgebaseWebpage",
        "tags": [
          "Agent knowledgebase"
        ],
        "summary": "Update a webpage source in an agent's knowledgebase",
        "description": "Updates the display name, URL, and/or notes of a webpage source in the agent's knowledgebase. Changing the URL does not automatically re-scrape; call the refresh endpoint afterwards. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Fields to update; omitted fields keep their current value.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255,
                    "description": "Display name for the webpage source."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Source URL. Changing it does not trigger a re-scrape; call the refresh endpoint afterwards."
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 5000,
                    "description": "Free-form notes shown to the AI alongside the webpage."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webpage updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseWebpage"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Agent, knowledgebase, or webpage not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agents/{agentId}/knowledgebase/webpages/{webpageId}/refresh": {
      "parameters": [
        {
          "name": "agentId",
          "in": "path",
          "required": true,
          "description": "Unique id of the agent.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "webpageId",
          "in": "path",
          "required": true,
          "description": "Unique id of the webpage source.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "operationId": "refreshAgentKnowledgebaseWebpage",
        "tags": [
          "Agent knowledgebase"
        ],
        "summary": "Refresh a webpage source in an agent's knowledgebase",
        "description": "Queues a re-scrape of the webpage so its extracted content is rebuilt from the live page. Each webpage can be refreshed at most once per 24 hours. Rate limit: write (60 requests/minute) plus the per-webpage daily limit.",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "Refresh queued. Content is re-scraped asynchronously.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "url": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "uri"
                        },
                        "name": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "status": {
                          "type": "string",
                          "description": "Always \"refreshing\" once the job is queued."
                        }
                      }
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Agent, knowledgebase, or webpage not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The webpage is already being refreshed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Either the shared write rate limit was exceeded, or this webpage was already refreshed in the last 24 hours (webpages can be refreshed at most once per day; `nextAvailableAt` says when the next refresh is allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "nextAvailableAt": {
                          "type": "string",
                          "format": "date-time",
                          "description": "Earliest time the webpage can be refreshed again (per-webpage daily limit only)."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/knowledgebase/{knowledgebaseId}": {
      "parameters": [
        {
          "name": "knowledgebaseId",
          "in": "path",
          "required": true,
          "description": "Unique id of the knowledgebase.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getKnowledgebase",
        "tags": [
          "Knowledgebase"
        ],
        "summary": "Get a knowledgebase",
        "description": "Returns a knowledgebase with its webpage sources, uploaded documents, and manually-created Q&A references. Knowledgebase ids are discoverable via GET /agents. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The knowledgebase and its content.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "A knowledgebase and all of its content sources.",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "name": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "description": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "webpages": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/KnowledgebaseWebpage"
                          }
                        },
                        "documents": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/KnowledgebaseDocument"
                          }
                        },
                        "references": {
                          "type": "array",
                          "description": "Manually-created Q&A references (excludes entries extracted from webpages/documents).",
                          "items": {
                            "$ref": "#/components/schemas/KnowledgebaseReference"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Knowledgebase not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/knowledgebase/{knowledgebaseId}/search": {
      "parameters": [
        {
          "name": "knowledgebaseId",
          "in": "path",
          "required": true,
          "description": "Unique id of the knowledgebase.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "searchKnowledgebase",
        "tags": [
          "Knowledgebase"
        ],
        "summary": "Search a knowledgebase",
        "description": "Runs a semantic (vector-similarity) search across the knowledgebase and returns the best-matching references with similarity scores. Each search consumes 1 credit from the team's balance. Rate limit: search (30 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "Natural-language search query to run against the knowledgebase. 1–2000 characters.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2000
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of results to return (1–50).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          },
          {
            "name": "threshold",
            "in": "query",
            "required": false,
            "description": "Minimum cosine-similarity score (0–1) a reference must reach to be included.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 1,
              "default": 0.1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results ordered by similarity, best match first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/KnowledgebaseSearchResult"
                      }
                    },
                    "meta": {
                      "type": "object",
                      "description": "Echo of the search inputs plus billing info.",
                      "properties": {
                        "query": {
                          "type": "string",
                          "description": "The query that was run."
                        },
                        "resultCount": {
                          "type": "integer",
                          "description": "Number of results returned."
                        },
                        "limit": {
                          "type": "integer",
                          "description": "Applied result limit."
                        },
                        "threshold": {
                          "type": "number",
                          "description": "Applied similarity threshold."
                        },
                        "creditsUsed": {
                          "type": "integer",
                          "description": "Credits consumed by this search (always 1)."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Knowledgebase not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/knowledgebase/{knowledgebaseId}/references": {
      "parameters": [
        {
          "name": "knowledgebaseId",
          "in": "path",
          "required": true,
          "description": "Unique id of the knowledgebase.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "operationId": "createKnowledgebaseReference",
        "tags": [
          "Knowledgebase"
        ],
        "summary": "Create a Q&A reference",
        "description": "Adds a manually-authored question/answer pair to the knowledgebase. An embedding is generated immediately so the reference is searchable right away. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The Q&A pair to add.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "question": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 5000,
                    "description": "The question half of the Q&A pair."
                  },
                  "answer": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50000,
                    "description": "The answer half of the Q&A pair."
                  }
                },
                "required": [
                  "question",
                  "answer"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Reference created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseReference"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Knowledgebase not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/knowledgebase/{knowledgebaseId}/references/{referenceId}": {
      "parameters": [
        {
          "name": "knowledgebaseId",
          "in": "path",
          "required": true,
          "description": "Unique id of the knowledgebase.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "referenceId",
          "in": "path",
          "required": true,
          "description": "Unique id of the Q&A reference.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getKnowledgebaseReference",
        "tags": [
          "Knowledgebase"
        ],
        "summary": "Get a Q&A reference",
        "description": "Returns a single question/answer reference from the knowledgebase. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The reference.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseReference"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Knowledgebase or reference not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "operationId": "updateKnowledgebaseReference",
        "tags": [
          "Knowledgebase"
        ],
        "summary": "Update a Q&A reference",
        "description": "Updates the question and/or answer of a reference. When either field changes, the embedding is regenerated so search stays accurate. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Fields to update; omitted fields keep their current value.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "question": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 5000,
                    "description": "The question half of the Q&A pair."
                  },
                  "answer": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50000,
                    "description": "The answer half of the Q&A pair."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reference updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseReference"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Knowledgebase or reference not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/knowledgebase/{knowledgebaseId}/documents/{documentId}": {
      "parameters": [
        {
          "name": "knowledgebaseId",
          "in": "path",
          "required": true,
          "description": "Unique id of the knowledgebase.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "documentId",
          "in": "path",
          "required": true,
          "description": "Unique id of the uploaded document.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getKnowledgebaseDocument",
        "tags": [
          "Knowledgebase"
        ],
        "summary": "Get a document",
        "description": "Returns an uploaded document, including the Q&A content extracted from it. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The document with its extracted content.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseDocumentDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Knowledgebase or document not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "operationId": "updateKnowledgebaseDocument",
        "tags": [
          "Knowledgebase"
        ],
        "summary": "Update a document",
        "description": "Updates the display name and/or notes of an uploaded document. The file itself cannot be changed via the API. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Fields to update; omitted fields keep their current value.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255,
                    "description": "Display name for the document."
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 5000,
                    "description": "Free-form notes shown to the AI alongside the document."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Document updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseDocument"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Knowledgebase or document not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/knowledgebase/{knowledgebaseId}/webpages/{webpageId}": {
      "parameters": [
        {
          "name": "knowledgebaseId",
          "in": "path",
          "required": true,
          "description": "Unique id of the knowledgebase.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "webpageId",
          "in": "path",
          "required": true,
          "description": "Unique id of the webpage source.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getKnowledgebaseWebpage",
        "tags": [
          "Knowledgebase"
        ],
        "summary": "Get a webpage source",
        "description": "Returns a webpage source, including the Q&A content extracted from the page. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The webpage with its extracted content.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseWebpageDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Knowledgebase or webpage not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "operationId": "updateKnowledgebaseWebpage",
        "tags": [
          "Knowledgebase"
        ],
        "summary": "Update a webpage source",
        "description": "Updates the display name, URL, and/or notes of a webpage source. Changing the URL does not automatically re-scrape; call the refresh endpoint afterwards. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Fields to update; omitted fields keep their current value.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255,
                    "description": "Display name for the webpage source."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Source URL. Changing it does not trigger a re-scrape; call the refresh endpoint afterwards."
                  },
                  "notes": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 5000,
                    "description": "Free-form notes shown to the AI alongside the webpage."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webpage updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/KnowledgebaseWebpage"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Knowledgebase or webpage not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/knowledgebase/{knowledgebaseId}/webpages/{webpageId}/refresh": {
      "parameters": [
        {
          "name": "knowledgebaseId",
          "in": "path",
          "required": true,
          "description": "Unique id of the knowledgebase.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "webpageId",
          "in": "path",
          "required": true,
          "description": "Unique id of the webpage source.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "operationId": "refreshKnowledgebaseWebpage",
        "tags": [
          "Knowledgebase"
        ],
        "summary": "Refresh a webpage source",
        "description": "Queues a re-scrape of the webpage so its extracted content is rebuilt from the live page. Each webpage can be refreshed at most once per 24 hours. Rate limit: write (60 requests/minute) plus the per-webpage daily limit.",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "Refresh queued. Content is re-scraped asynchronously.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "url": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "uri"
                        },
                        "name": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "status": {
                          "type": "string",
                          "description": "Always \"refreshing\" once the job is queued."
                        }
                      }
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Knowledgebase or webpage not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The webpage is already being refreshed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Either the shared write rate limit was exceeded, or this webpage was already refreshed in the last 24 hours (webpages can be refreshed at most once per day; `nextAvailableAt` says when the next refresh is allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "nextAvailableAt": {
                          "type": "string",
                          "format": "date-time",
                          "description": "Earliest time the webpage can be refreshed again (per-webpage daily limit only)."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/calls": {
      "get": {
        "operationId": "listCalls",
        "tags": [
          "Calls"
        ],
        "summary": "List calls",
        "description": "Returns the team's call logs with transcripts and recording URLs, filterable by direction, agent, duration, date range, and free-text search across caller/callee numbers, transcripts, and summaries. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Free-text search across from/to numbers, transcript text, and call summaries.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "callType",
            "in": "query",
            "required": false,
            "description": "Filter by call direction.",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "incoming",
                "outgoing"
              ],
              "default": "all"
            }
          },
          {
            "name": "agentId",
            "in": "query",
            "required": false,
            "description": "Only return calls handled by this agent.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "minDuration",
            "in": "query",
            "required": false,
            "description": "Minimum call duration in seconds.",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "maxDuration",
            "in": "query",
            "required": false,
            "description": "Maximum call duration in seconds.",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "dateFrom",
            "in": "query",
            "required": false,
            "description": "Only return calls created on or after this date (ISO 8601).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "dateTo",
            "in": "query",
            "required": false,
            "description": "Only return calls created on or before this date (inclusive of the whole day, ISO 8601).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "required": false,
            "description": "Field to sort by.",
            "schema": {
              "type": "string",
              "enum": [
                "createdAt",
                "duration",
                "endedAt"
              ],
              "default": "createdAt"
            }
          },
          {
            "$ref": "#/components/parameters/SortOrder"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of call logs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Call"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/calls/{callId}": {
      "parameters": [
        {
          "name": "callId",
          "in": "path",
          "required": true,
          "description": "Unique id of the call.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getCall",
        "tags": [
          "Calls"
        ],
        "summary": "Get a call",
        "description": "Returns one call with its full transcript, complete activity timeline (including tool calls), memory, recording URL, and analysis metadata such as sentiment and call quality. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The call details.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CallDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Call not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/chats": {
      "get": {
        "operationId": "listChats",
        "tags": [
          "Chats"
        ],
        "summary": "List chat conversations",
        "description": "Returns chat conversations for one channel — web widget, web agent, voice widget, or WhatsApp — with transcripts, filterable by agent, date range, and transcript text search. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "name": "channel",
            "in": "query",
            "required": false,
            "description": "Which conversation channel to list: web (chat widget), webagent (web agent), voice (voice widget), or whatsapp.",
            "schema": {
              "type": "string",
              "enum": [
                "web",
                "webagent",
                "voice",
                "whatsapp"
              ],
              "default": "web"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Free-text search across conversation transcripts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "agentId",
            "in": "query",
            "required": false,
            "description": "Only return conversations handled by this agent.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "dateFrom",
            "in": "query",
            "required": false,
            "description": "Only return conversations created on or after this date (ISO 8601).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "dateTo",
            "in": "query",
            "required": false,
            "description": "Only return conversations created on or before this date (inclusive of the whole day, ISO 8601).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/SortOrder"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of chat conversations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Chat"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/chats/{chatId}": {
      "parameters": [
        {
          "name": "chatId",
          "in": "path",
          "required": true,
          "description": "Unique id of the conversation.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getChat",
        "tags": [
          "Chats"
        ],
        "summary": "Get a chat conversation",
        "description": "Returns one conversation — the id may belong to a web/web-agent chat, a voice-widget session, or a WhatsApp thread — with its full transcript, activity timeline (including tool calls), and metadata. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation details.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ChatDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Conversation not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contacts": {
      "get": {
        "operationId": "listContacts",
        "tags": [
          "Contacts"
        ],
        "summary": "List contacts",
        "description": "Returns the team's contacts with tags, CRM stage, and user assignments, plus the team's custom-field schemas and available tags to help interpret the records. Supports free-text search and tag filtering. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Free-text search across first name, last name, email, and phone number.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "required": false,
            "description": "Field to sort by.",
            "schema": {
              "type": "string",
              "enum": [
                "createdAt",
                "firstName",
                "lastName",
                "email",
                "number"
              ],
              "default": "createdAt"
            }
          },
          {
            "$ref": "#/components/parameters/SortOrder"
          },
          {
            "name": "tagIds",
            "in": "query",
            "required": false,
            "description": "Comma-separated list of tag ids; only contacts carrying at least one of these tags are returned.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of contacts plus the team's custom-field schemas and tags.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Contact"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    },
                    "customFieldSchemas": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactCustomFieldSchema"
                      }
                    },
                    "availableTags": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactTag"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "createContact",
        "tags": [
          "Contacts"
        ],
        "summary": "Create a contact",
        "description": "Creates a contact with optional custom fields, tags (existing ids or new names), an initial note, user assignments, and board placements — all in one call. Custom-field values are validated against the team's custom-field schemas. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The contact to create.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "firstName": {
                    "type": "string",
                    "description": "First name."
                  },
                  "lastName": {
                    "type": "string",
                    "description": "Last name."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Email address; an empty string is accepted."
                  },
                  "number": {
                    "type": "string",
                    "description": "Phone number. Parentheses, dashes, and spaces are stripped before saving."
                  },
                  "carrier": {
                    "type": "string",
                    "description": "Phone carrier name."
                  },
                  "customFields": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Custom-field values keyed by field name (see GET /contacts/custom-fields). Values are coerced to the field's declared type; missing required fields cause a 400."
                  },
                  "tagIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Existing tag ids to attach."
                  },
                  "tagNames": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Tag names to attach; tags that do not exist yet are created."
                  },
                  "note": {
                    "type": "object",
                    "required": [
                      "note"
                    ],
                    "description": "Optional initial note to attach to the contact.",
                    "properties": {
                      "note": {
                        "type": "string",
                        "minLength": 1
                      },
                      "type": {
                        "type": "string",
                        "enum": [
                          "GENERAL",
                          "INFO",
                          "WARNING",
                          "DANGER"
                        ],
                        "default": "GENERAL"
                      }
                    }
                  },
                  "assignUserIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Team member user ids to assign to this contact. All ids must belong to the team."
                  },
                  "boardIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Kanban board ids to place the contact on."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Contact created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contacts/bulk": {
      "post": {
        "operationId": "bulkCreateContacts",
        "tags": [
          "Contacts"
        ],
        "summary": "Bulk create contacts",
        "description": "Creates up to 100 contacts in one request. Each entry requires a phone number; duplicates are skipped. Per-row failures (for example a missing required custom field) are reported in the response without failing the whole batch. Rate limit: bulk (10 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The contacts to create.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "contacts"
                ],
                "properties": {
                  "contacts": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 100,
                    "items": {
                      "type": "object",
                      "required": [
                        "number"
                      ],
                      "properties": {
                        "firstName": {
                          "type": "string"
                        },
                        "lastName": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string",
                          "format": "email"
                        },
                        "number": {
                          "type": "string",
                          "description": "Phone number (required). Parentheses, dashes, and spaces are stripped before saving."
                        },
                        "carrier": {
                          "type": "string"
                        },
                        "customFields": {
                          "type": "object",
                          "additionalProperties": true,
                          "description": "Custom-field values keyed by field name."
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Bulk creation completed (possibly partially).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "message",
                    "summary"
                  ],
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "requested": {
                          "type": "integer",
                          "description": "Number of contacts in the request."
                        },
                        "created": {
                          "type": "integer",
                          "description": "Number actually created (duplicates are skipped)."
                        },
                        "errors": {
                          "type": "integer",
                          "description": "Number of rows that failed validation."
                        }
                      }
                    },
                    "errors": {
                      "type": "array",
                      "description": "Per-row failures; omitted when every row succeeded.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "index": {
                            "type": "integer",
                            "description": "Zero-based index of the failing row."
                          },
                          "contact": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "The submitted row."
                          },
                          "error": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contacts/custom-fields": {
      "get": {
        "operationId": "listContactCustomFields",
        "tags": [
          "Contacts"
        ],
        "summary": "List contact custom-field schemas",
        "description": "Returns the team's active custom-field definitions in display order. Use the returned field names as keys inside `customFields` when creating or updating contacts. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The active custom-field schemas.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactCustomFieldSchema"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contacts/{contactId}": {
      "parameters": [
        {
          "name": "contactId",
          "in": "path",
          "required": true,
          "description": "Unique id of the contact.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "getContact",
        "tags": [
          "Contacts"
        ],
        "summary": "Get a contact",
        "description": "Returns one contact with tags, CRM stage, user assignments, and its 10 most recent notes, plus the team's custom-field schemas and available tags. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The contact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "customFieldSchemas": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactCustomFieldSchema"
                      }
                    },
                    "availableTags": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactTag"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Contact not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "operationId": "updateContact",
        "tags": [
          "Contacts"
        ],
        "summary": "Update a contact",
        "description": "Partially updates a contact. Only provided fields change; custom-field values are merged (send null or an empty string to clear a field) and providing `tagIds`/`tagNames` replaces the full tag set. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Fields to update.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "firstName": {
                    "type": "string"
                  },
                  "lastName": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "number": {
                    "type": "string",
                    "description": "Phone number. Parentheses, dashes, and spaces are stripped before saving."
                  },
                  "carrier": {
                    "type": "string"
                  },
                  "customFields": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Custom-field values to merge into the contact, keyed by field name. Null or empty-string values remove the field."
                  },
                  "crmStageId": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid",
                    "description": "CRM stage to move the contact to; null clears it."
                  },
                  "tagIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Replaces the contact's tags together with `tagNames`."
                  },
                  "tagNames": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Tag names to set; tags that do not exist yet are created."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Contact"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Contact not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "operationId": "deleteContact",
        "tags": [
          "Contacts"
        ],
        "summary": "Delete a contact",
        "description": "Permanently deletes a contact along with its notes, assignments, and board placements. This cannot be undone. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "Contact deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "message"
                  ],
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Contact not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contacts/{contactId}/notes": {
      "parameters": [
        {
          "name": "contactId",
          "in": "path",
          "required": true,
          "description": "Unique id of the contact.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "listContactNotes",
        "tags": [
          "Contacts"
        ],
        "summary": "List notes for a contact",
        "description": "Returns all notes attached to a contact, newest first, with the profile of each note's creator when available. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The contact's notes.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactNote"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Contact not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "createContactNote",
        "tags": [
          "Contacts"
        ],
        "summary": "Create a note on a contact",
        "description": "Adds a note to a contact. Notes carry a severity type (GENERAL, INFO, WARNING, DANGER) used for display in the CRM. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The note to add.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "note"
                ],
                "properties": {
                  "note": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Note content."
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "GENERAL",
                      "INFO",
                      "WARNING",
                      "DANGER"
                    ],
                    "default": "GENERAL",
                    "description": "Severity/category of the note."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Note created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ContactNote"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Contact not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contacts/{contactId}/assignments": {
      "parameters": [
        {
          "name": "contactId",
          "in": "path",
          "required": true,
          "description": "Unique id of the contact.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "listContactAssignments",
        "tags": [
          "Contacts"
        ],
        "summary": "List user assignments for a contact",
        "description": "Returns the team members assigned to a contact, most recently assigned first. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The contact's assignments.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactAssignment"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Contact not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "addContactAssignments",
        "tags": [
          "Contacts"
        ],
        "summary": "Assign users to a contact",
        "description": "Adds the given team members to the contact's assignments, keeping existing assignments. Every user id must belong to the team or the whole request is rejected. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "User ids to assign.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "userIds"
                ],
                "properties": {
                  "userIds": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Team member user ids to add as assignees."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Assignments added; the full updated assignment list is returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactAssignment"
                      }
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Contact not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "operationId": "replaceContactAssignments",
        "tags": [
          "Contacts"
        ],
        "summary": "Replace a contact's user assignments",
        "description": "Replaces the contact's full assignment list with the given team members. Every user id must belong to the team or the whole request is rejected. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The complete new set of assignees.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "userIds"
                ],
                "properties": {
                  "userIds": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Team member user ids that should remain assigned."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Assignments replaced; the full updated assignment list is returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactAssignment"
                      }
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Contact not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contacts/{contactId}/boards": {
      "parameters": [
        {
          "name": "contactId",
          "in": "path",
          "required": true,
          "description": "Unique id of the contact.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "operationId": "listContactBoards",
        "tags": [
          "Contacts"
        ],
        "summary": "List boards a contact is on",
        "description": "Returns the kanban boards the contact has been placed on, most recently added first, including each board's custom-field schema. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The contact's board placements.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactBoardPlacement"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Contact not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "addContactToBoards",
        "tags": [
          "Contacts"
        ],
        "summary": "Add a contact to boards",
        "description": "Places the contact on the given active kanban boards, keeping existing placements. Every board id must belong to the team and be active or the whole request is rejected. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Board ids to add the contact to.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "boardIds"
                ],
                "properties": {
                  "boardIds": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Active kanban board ids."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Placements added; the full updated placement list is returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactBoardPlacement"
                      }
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Contact not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "operationId": "replaceContactBoards",
        "tags": [
          "Contacts"
        ],
        "summary": "Replace a contact's board placements",
        "description": "Replaces the contact's full set of board placements with the given active kanban boards. Every board id must belong to the team and be active or the whole request is rejected. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The complete new set of boards for the contact.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "boardIds"
                ],
                "properties": {
                  "boardIds": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Active kanban board ids."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Placements replaced; the full updated placement list is returned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactBoardPlacement"
                      }
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Contact not found for this team.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contacts/{contactId}/boards/{boardId}": {
      "parameters": [
        {
          "name": "contactId",
          "in": "path",
          "required": true,
          "description": "Unique id of the contact.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "boardId",
          "in": "path",
          "required": true,
          "description": "Unique id of the board to remove it from.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "operationId": "removeContactFromBoard",
        "tags": [
          "Contacts"
        ],
        "summary": "Remove a contact from one board",
        "description": "Removes a contact from a single kanban board, leaving its other board placements untouched. To clear every placement at once, use DELETE /contacts/{contactId}/boards. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The contact was removed from the board.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The contact does not exist, or is not on that board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/boards": {
      "get": {
        "operationId": "listBoards",
        "tags": [
          "Boards"
        ],
        "summary": "List boards",
        "description": "Returns the team's active kanban boards with each board's custom-field schema and the number of contacts currently on it. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The team's active boards.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Board"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/tags": {
      "get": {
        "operationId": "listTags",
        "tags": [
          "Tags"
        ],
        "summary": "List tags",
        "description": "Returns the team's contact tags in alphabetical order with the number of contacts carrying each tag. Rate limit: standard (100 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "responses": {
          "200": {
            "description": "The team's tags.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactTagWithCount"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "createTag",
        "tags": [
          "Tags"
        ],
        "summary": "Create a tag",
        "description": "Creates a contact tag. Tag names are unique per team; creating a duplicate name responds 400. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The tag to create.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50,
                    "description": "Tag name, unique within the team."
                  },
                  "color": {
                    "type": "string",
                    "pattern": "^#[0-9A-Fa-f]{6}$",
                    "description": "Hex color for the tag (for example #3B82F6, the default)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Tag created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ContactTag"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/agency/subteams": {
      "post": {
        "operationId": "createAgencySubteam",
        "tags": [
          "Agency"
        ],
        "summary": "Create an agency subteam",
        "description": "Provisions a complete client workspace under the agency that owns the API key: a user account (created or reused by email), a team with billing and wallet, and the agency-team link with monthly credits and feature access. Only API keys owned by an agency's owner team may call this; other keys receive 403. Once created, the subteam's id can be passed as `teamId` on any other endpoint to act on the client's data. Rate limit: write (60 requests/minute).",
        "parameters": [
          {
            "$ref": "#/components/parameters/TeamId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The client workspace to provision.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "firstName",
                  "lastName",
                  "teamName",
                  "email"
                ],
                "properties": {
                  "firstName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50,
                    "description": "Client user's first name."
                  },
                  "lastName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50,
                    "description": "Client user's last name."
                  },
                  "teamName": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50,
                    "description": "Name for the client's team/workspace."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Client user's email; lowercased. If a user with this email already exists in the tenant, it is reused."
                  },
                  "totalMonthlyCredits": {
                    "type": "number",
                    "minimum": 0,
                    "default": 1000,
                    "description": "Monthly credit allowance for the subteam."
                  },
                  "accessConfig": {
                    "type": "object",
                    "description": "Feature access toggles for the subteam; every flag defaults to false.",
                    "properties": {
                      "users": {
                        "type": "boolean",
                        "default": false
                      },
                      "instructions": {
                        "type": "boolean",
                        "default": false
                      },
                      "instructionsWrite": {
                        "type": "boolean",
                        "default": false
                      },
                      "testing": {
                        "type": "boolean",
                        "default": false
                      },
                      "knowledgeBase": {
                        "type": "boolean",
                        "default": false
                      },
                      "agentConfiguration": {
                        "type": "boolean",
                        "default": false
                      },
                      "agentCreation": {
                        "type": "boolean",
                        "default": false
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subteam created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "message",
                    "subteam"
                  ],
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "subteam": {
                      "type": "object",
                      "properties": {
                        "agencyTeam": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "name": {
                              "type": "string"
                            },
                            "teamId": {
                              "type": "string",
                              "format": "uuid",
                              "description": "The new team's id — pass this as `teamId` on other endpoints to act on the client's data."
                            },
                            "totalMonthlyCredits": {
                              "type": "number"
                            },
                            "accessConfig": {
                              "type": "object",
                              "additionalProperties": true
                            }
                          }
                        },
                        "user": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "email": {
                              "type": "string",
                              "format": "email"
                            },
                            "firstName": {
                              "type": "string"
                            },
                            "lastName": {
                              "type": "string"
                            },
                            "verificationSent": {
                              "type": "boolean",
                              "description": "True when a verification email was (re)sent to an existing unverified user."
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key's team does not own an agency, or `teamId` is not a subteam of the key's agency.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "API key created in the echowin portal (Build > Integrations > API Keys). The deprecated X-AGENCY-KEY header is still accepted for backward compatibility."
      }
    },
    "parameters": {
      "TeamId": {
        "name": "teamId",
        "in": "query",
        "required": false,
        "description": "Agency subteam scoping: when the API key belongs to a team that owns an agency, pass a subteam's team id to run the request against that client's data. Keys without an agency, or ids outside the key's agency, receive 403; malformed UUIDs receive 400.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "Page": {
        "name": "page",
        "in": "query",
        "required": false,
        "description": "1-based page number.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 1
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Page size (1–100).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 20
        }
      },
      "SortOrder": {
        "name": "sortOrder",
        "in": "query",
        "required": false,
        "description": "Sort direction.",
        "schema": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ],
          "default": "desc"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request — malformed UUID, failed body/query validation (may include a `details` array of validation issues), or a domain rule violation such as a duplicate tag name.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ValidationError"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid X-API-Key header.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The `teamId` query parameter is not a subteam of the API key's agency.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded for the endpoint's tier (standard 100/min, write 60/min, search 30/min, bulk 10/min — per API-key team). Check the Retry-After header before retrying.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying.",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Limit": {
            "description": "Request quota for the current window.",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Remaining": {
            "description": "Requests remaining in the current window.",
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Reset": {
            "description": "ISO 8601 time at which the window resets.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitError"
            }
          }
        }
      },
      "InternalError": {
        "description": "Unexpected server error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Identity": {
        "type": "object",
        "description": "Who the current API key belongs to, what it may do, and the team's standing.",
        "properties": {
          "team": {
            "type": "object",
            "description": "The team this request is scoped to.",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "isAgencyOwner": {
                "type": "boolean",
                "description": "True when this team owns an agency, meaning the key may pass ?teamId to act on its subteams."
              },
              "isAgencySubteam": {
                "type": "boolean",
                "description": "True when this team is a subteam of an agency."
              }
            }
          },
          "actingOnBehalfOf": {
            "description": "The agency team that owns the key, present only when ?teamId scoped this request to one of its subteams. Null otherwise.",
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "name": {
                    "type": "string"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "apiKey": {
            "type": "object",
            "description": "The key making this request. The secret itself is never returned.",
            "properties": {
              "id": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uuid"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "createdAt": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          },
          "credits": {
            "type": "object",
            "description": "The team's credit balance.",
            "properties": {
              "balance": {
                "type": "integer"
              },
              "monthlyBalance": {
                "type": "integer"
              },
              "currency": {
                "type": "string"
              }
            }
          },
          "subscription": {
            "description": "The team's subscription, or null when it has none.",
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "type": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "active": {
                    "type": "boolean"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Standard error envelope returned by every endpoint.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable description of what went wrong."
          },
          "code": {
            "type": "string",
            "description": "Optional machine-readable error code in snake_case (for example `not_found`, `validation_error`, `rate_limit_exceeded`). Codes are stable; new codes may be added over time."
          },
          "hint": {
            "type": "string",
            "description": "Optional suggestion for how to fix the request."
          }
        }
      },
      "ValidationError": {
        "description": "Error envelope for 400 responses; `details` carries per-field validation issues when body/query validation fails.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "details": {
                "type": "array",
                "description": "Validation issues (zod format): each entry has `path`, `message`, and `code`.",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        ]
      },
      "RateLimitError": {
        "description": "Error envelope for 429 responses.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "retryAfter": {
                "type": "integer",
                "description": "Seconds to wait before retrying."
              },
              "resetAt": {
                "type": "string",
                "format": "date-time",
                "description": "When the current rate-limit window resets."
              }
            }
          }
        ]
      },
      "Pagination": {
        "type": "object",
        "description": "Pagination metadata for list endpoints.",
        "properties": {
          "page": {
            "type": "integer",
            "description": "Current 1-based page."
          },
          "limit": {
            "type": "integer",
            "description": "Page size used."
          },
          "totalCount": {
            "type": "integer",
            "description": "Total records matching the query."
          },
          "totalPages": {
            "type": "integer",
            "description": "Total pages available."
          }
        }
      },
      "AgentRef": {
        "type": "object",
        "description": "Minimal agent reference embedded in other resources.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Agent": {
        "type": "object",
        "description": "An AI agent belonging to the team.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Agent display name."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Short description of what the agent does."
          },
          "knowledgebase": {
            "description": "The knowledgebase linked to this agent, if any.",
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "AgentInstructions": {
        "type": "object",
        "description": "An agent's natural-language instructions.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "instructions": {
            "type": [
              "string",
              "null"
            ],
            "description": "The agent's system-prompt instructions."
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "TranscriptMessage": {
        "type": "object",
        "description": "One conversation turn in a transcript.",
        "properties": {
          "speaker": {
            "type": "string",
            "enum": [
              "caller",
              "user",
              "agent"
            ],
            "description": "Who spoke: \"caller\" on phone calls, \"user\" on chats, \"agent\" for the AI."
          },
          "text": {
            "type": "string",
            "description": "What was said."
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ActivityLogEntry": {
        "type": "object",
        "description": "A raw session log entry — responses, tool calls, and system events.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Log entry type (for example RESPONSE or a tool event)."
          },
          "sender": {
            "type": [
              "string",
              "null"
            ]
          },
          "data": {
            "type": [
              "string",
              "null"
            ],
            "description": "Entry payload text."
          },
          "toolData": {
            "description": "Structured tool-call payload when the entry is a tool event."
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Call": {
        "type": "object",
        "description": "A phone call log entry with transcript.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "from": {
            "type": [
              "string",
              "null"
            ],
            "description": "Caller phone number (E.164)."
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "description": "Called phone number (E.164)."
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "INCOMING",
              "OUTGOING",
              null
            ],
            "description": "Call direction."
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Call status."
          },
          "duration": {
            "type": [
              "number",
              "null"
            ],
            "description": "Call duration in seconds."
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "endedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "summary": {
            "type": [
              "string",
              "null"
            ],
            "description": "AI-generated call summary."
          },
          "score": {
            "type": [
              "number",
              "null"
            ],
            "description": "AI-generated quality score for the call."
          },
          "flagged": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "favorite": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "spam": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether the call was classified as spam."
          },
          "agent": {
            "description": "The agent that handled the call, if any.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/AgentRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "transcript": {
            "type": "array",
            "description": "Ordered conversation turns rebuilt from the RESPONSE logs of the session.",
            "items": {
              "$ref": "#/components/schemas/TranscriptMessage"
            }
          },
          "recordingUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "URL of the call recording, when one exists."
          }
        }
      },
      "CallDetail": {
        "description": "Full call detail: everything in Call plus memory, the complete activity timeline, and analysis metadata.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Call"
          },
          {
            "type": "object",
            "properties": {
              "memory": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Durable memory the agent captured from this call."
              },
              "activityTimeline": {
                "type": "array",
                "description": "Every log entry for the session in chronological order, including tool calls and system events.",
                "items": {
                  "$ref": "#/components/schemas/ActivityLogEntry"
                }
              },
              "metadata": {
                "type": "object",
                "description": "Post-call analysis metadata.",
                "properties": {
                  "sentiment": {
                    "description": "Detected caller sentiment, when analyzed."
                  },
                  "callQuality": {
                    "description": "Call quality assessment, when analyzed."
                  },
                  "technicalIssues": {
                    "description": "Technical issues detected during the call."
                  },
                  "usage": {
                    "description": "Usage details recorded for the call."
                  },
                  "callCost": {
                    "description": "Cost details recorded for the call."
                  }
                }
              }
            }
          }
        ]
      },
      "Chat": {
        "type": "object",
        "description": "A chat conversation (web widget, web agent, voice widget, or WhatsApp).",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "channel": {
            "type": "string",
            "enum": [
              "web",
              "webagent",
              "voice",
              "whatsapp"
            ],
            "description": "Which channel the conversation happened on."
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Conversation status; always null for WhatsApp threads."
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "endedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Only set for voice sessions."
          },
          "duration": {
            "type": [
              "number",
              "null"
            ],
            "description": "Session length in seconds; only set for voice sessions."
          },
          "phoneNumber": {
            "type": [
              "string",
              "null"
            ],
            "description": "Counterparty number; only set for WhatsApp threads."
          },
          "agent": {
            "description": "The agent that handled the conversation, if any.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/AgentRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "transcript": {
            "type": "array",
            "description": "Ordered conversation turns rebuilt from the RESPONSE logs of the session.",
            "items": {
              "$ref": "#/components/schemas/TranscriptMessage"
            }
          },
          "messageCount": {
            "type": "integer",
            "description": "Number of transcript messages."
          }
        }
      },
      "ChatDetail": {
        "type": "object",
        "description": "Full conversation detail with activity timeline and metadata.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "channel": {
            "type": "string",
            "enum": [
              "web",
              "webagent",
              "voice",
              "whatsapp"
            ]
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "endedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "duration": {
            "type": [
              "number",
              "null"
            ]
          },
          "phoneNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "memory": {
            "type": [
              "string",
              "null"
            ],
            "description": "Durable memory captured in the session; only set for voice sessions."
          },
          "agent": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/AgentRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "transcript": {
            "type": "array",
            "description": "Ordered conversation turns rebuilt from the RESPONSE logs of the session.",
            "items": {
              "$ref": "#/components/schemas/TranscriptMessage"
            }
          },
          "activityTimeline": {
            "type": "array",
            "description": "Every log entry for the session in chronological order, including tool calls and system events.",
            "items": {
              "$ref": "#/components/schemas/ActivityLogEntry"
            }
          },
          "metadata": {
            "description": "Channel-specific session metadata."
          }
        }
      },
      "UserRef": {
        "type": "object",
        "description": "Minimal user profile embedded in other resources.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "firstName": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastName": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "picture": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Avatar URL; only present on assignment responses."
          }
        }
      },
      "ContactTag": {
        "type": "object",
        "description": "A contact tag.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "color": {
            "type": [
              "string",
              "null"
            ],
            "description": "Hex color, defaults to #3B82F6."
          },
          "teamId": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ContactTagWithCount": {
        "type": "object",
        "description": "A contact tag with its usage count.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "color": {
            "type": [
              "string",
              "null"
            ]
          },
          "contactCount": {
            "type": "integer",
            "description": "Number of contacts carrying this tag."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CrmStage": {
        "type": "object",
        "description": "A CRM pipeline stage.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "order": {
            "type": "integer",
            "description": "Position in the pipeline."
          },
          "teamId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ContactAssignment": {
        "type": "object",
        "description": "A team member assigned to a contact.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "contactId": {
            "type": "string",
            "format": "uuid"
          },
          "userId": {
            "type": "string",
            "format": "uuid"
          },
          "assignedAt": {
            "type": "string",
            "format": "date-time"
          },
          "assignedBy": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "user": {
            "$ref": "#/components/schemas/UserRef"
          }
        }
      },
      "Contact": {
        "type": "object",
        "description": "A CRM contact with tags, stage, and assignments. `notes` is only included on GET /contacts/{contactId} (latest 10).",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "teamId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "firstName": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastName": {
            "type": [
              "string",
              "null"
            ]
          },
          "carrier": {
            "type": [
              "string",
              "null"
            ]
          },
          "number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Phone number, stored without separators."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "metadata": {
            "description": "Internal metadata blob."
          },
          "customFields": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Custom-field values keyed by field name (see GET /contacts/custom-fields)."
          },
          "autoPopulated": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "True when the contact was created automatically from a call or chat."
          },
          "favorite": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "doNotCallOutbound": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Excluded from outbound campaigns when true."
          },
          "crmStageId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "crmStage": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CrmStage"
              },
              {
                "type": "null"
              }
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContactTag"
            }
          },
          "assignments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContactAssignment"
            }
          },
          "notes": {
            "type": "array",
            "description": "Latest 10 notes; only included on GET /contacts/{contactId}.",
            "items": {
              "$ref": "#/components/schemas/ContactNote"
            }
          }
        }
      },
      "ContactNote": {
        "type": "object",
        "description": "A note attached to a contact.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "description": "Note content."
          },
          "link": {
            "type": [
              "string",
              "null"
            ]
          },
          "addedFromScenario": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "description": "GENERAL, INFO, WARNING, or DANGER."
          },
          "contactId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "creatorId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "teamId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "clientContactId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "creator": {
            "description": "Profile of the note's creator; null for API-created notes.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/UserRef"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "ContactCustomFieldSchema": {
        "type": "object",
        "description": "Definition of a team custom field for contacts.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Machine name — use as the key inside a contact's `customFields`."
          },
          "label": {
            "type": "string",
            "description": "Human-friendly display label."
          },
          "type": {
            "type": "string",
            "description": "Field type: text, number, date, boolean, select, multiselect, email, phone, or url."
          },
          "required": {
            "type": "boolean"
          },
          "options": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Choices for select/multiselect fields."
          },
          "placeholder": {
            "type": [
              "string",
              "null"
            ]
          },
          "helpText": {
            "type": [
              "string",
              "null"
            ]
          },
          "order": {
            "type": "integer",
            "description": "Display position."
          }
        }
      },
      "Board": {
        "type": "object",
        "description": "A kanban board for organizing contacts.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "customFieldSchema": {
            "description": "The custom field this board's columns are derived from.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/ContactCustomFieldSchema"
              },
              {
                "type": "null"
              }
            ]
          },
          "contactCount": {
            "type": "integer",
            "description": "Number of contacts currently on the board."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ContactBoardPlacement": {
        "type": "object",
        "description": "A contact's placement on a kanban board.",
        "properties": {
          "boardId": {
            "type": "string",
            "format": "uuid"
          },
          "board": {
            "type": "object",
            "additionalProperties": true,
            "description": "The board record, including its custom-field schema."
          },
          "addedAt": {
            "type": "string",
            "format": "date-time"
          },
          "addedBy": {
            "description": "Profile of who placed the contact; null for API placements.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/UserRef"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "KnowledgebaseWebpage": {
        "type": "object",
        "description": "A webpage content source in a knowledgebase.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastScrapedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the page content was last scraped."
          },
          "isUpdating": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "True while a re-scrape is in progress."
          }
        }
      },
      "KnowledgebaseWebpageDetail": {
        "description": "A webpage source including the Q&A content extracted from it.",
        "allOf": [
          {
            "$ref": "#/components/schemas/KnowledgebaseWebpage"
          },
          {
            "type": "object",
            "properties": {
              "extractedContent": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "question": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "answer": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "KnowledgebaseDocument": {
        "type": "object",
        "description": "An uploaded document in a knowledgebase.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "fileName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Original uploaded file name."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Display name."
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "isUpdating": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "True while content extraction is in progress."
          }
        }
      },
      "KnowledgebaseDocumentDetail": {
        "description": "A document including the Q&A content extracted from it.",
        "allOf": [
          {
            "$ref": "#/components/schemas/KnowledgebaseDocument"
          },
          {
            "type": "object",
            "properties": {
              "extractedContent": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "question": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "answer": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "KnowledgebaseReference": {
        "type": "object",
        "description": "A question/answer knowledge reference.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "question": {
            "type": [
              "string",
              "null"
            ]
          },
          "answer": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Reference type; manually created references are \"QA\"."
          }
        }
      },
      "KnowledgebaseSearchResult": {
        "type": "object",
        "description": "A semantic-search hit from a knowledgebase.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "question": {
            "type": [
              "string",
              "null"
            ]
          },
          "answer": {
            "type": [
              "string",
              "null"
            ]
          },
          "similarity": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Cosine similarity between the query and the reference, rounded to 3 decimals."
          },
          "type": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "string",
            "enum": [
              "webpage",
              "document",
              "manual"
            ],
            "description": "Where the reference came from."
          }
        }
      }
    }
  }
}