{
  "openapi": "3.0.0",
  "info": {
    "title": "Botdog API",
    "description": "API documentation for Botdog.\n\n**Authentication**: Pass your API key as the `x-api-key` header.\nKeys are 32-character hexadecimal strings with no prefix.\nExample: `x-api-key: 0123456789abcdef0123456789abcdef`\n\nDo NOT use the `Authorization` header.",
    "version": "1.0",
    "contact": {}
  },
  "servers": [
    {
      "url": "https://api.botdog.co",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "campaigns",
      "description": "Create, read, update, and control campaigns (pause/resume, pause activities, archive, delete)."
    },
    {
      "name": "leads",
      "description": "List and filter leads, read full detail + event history, update metadata, add to a campaign, or stop a sequence."
    },
    {
      "name": "lists",
      "description": "Manage lead/audience lists and their members."
    },
    {
      "name": "blacklist",
      "description": "Manage suppression lists (excluded profiles)."
    },
    {
      "name": "snippets",
      "description": "Manage message snippets / templates."
    },
    {
      "name": "accounts",
      "description": "Connected LinkedIn accounts (seats), their connection health, and daily sending limits."
    },
    {
      "name": "webhooks",
      "description": "Manage webhook subscriptions."
    },
    {
      "name": "messaging",
      "description": "Send messages and replies, schedule a future send, and mark conversations read/unread. Rate-limited per sending account."
    },
    {
      "name": "analytics",
      "description": "Team-level analytics totals, daily breakdowns, and per-user metrics."
    },
    {
      "name": "users",
      "description": "Read the users in your team."
    },
    {
      "name": "teams",
      "description": "Current team context."
    },
    {
      "name": "health",
      "description": "Service health checks (no authentication)."
    }
  ],
  "paths": {
    "/v1/accounts": {
      "get": {
        "description": "List the connected LinkedIn accounts (seats) for the team, with connection health.",
        "operationId": "LinkedinAccountsController_findAll",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of accounts to return (1-200)",
            "schema": {
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AccountDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List accounts",
        "tags": ["accounts"]
      }
    },
    "/v1/accounts/{id}/limits": {
      "get": {
        "description": "Get an account's daily sending caps and remaining quota for today.",
        "operationId": "LinkedinAccountsController_getLimits",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountLimitsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Account not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get account limits",
        "tags": ["accounts"]
      },
      "patch": {
        "description": "Update an account's daily caps, sending days, timezone, and auto-withdraw settings. Caps are clamped to safe maximums.",
        "operationId": "LinkedinAccountsController_updateLimits",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "number"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAccountLimitsDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountLimitsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Account not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Update account limits",
        "tags": ["accounts"]
      }
    },
    "/v1/analytics": {
      "get": {
        "description": "Account-level analytics across all campaigns for a period.",
        "operationId": "AnalyticsController_getAccountAnalytics",
        "parameters": [
          {
            "name": "period",
            "required": false,
            "in": "query",
            "description": "Time period for analytics",
            "schema": {
              "default": "last-7-days",
              "type": "string",
              "enum": ["last-7-days", "last-30-days", "last-90-days"]
            }
          },
          {
            "name": "userId",
            "required": false,
            "in": "query",
            "description": "Filter to a specific user",
            "schema": {
              "example": "user_abc123",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Aggregated outreach metrics for the account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Account analytics",
        "tags": ["analytics"]
      }
    },
    "/v1/analytics/by-user": {
      "get": {
        "description": "Metrics broken down by each team member for a period.",
        "operationId": "AnalyticsController_getByUserAnalytics",
        "parameters": [
          {
            "name": "period",
            "required": false,
            "in": "query",
            "description": "Time period for analytics",
            "schema": {
              "default": "last-7-days",
              "type": "string",
              "enum": ["last-7-days", "last-30-days", "last-90-days"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Per-user outreach metrics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ByUserAnalyticsResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Analytics by user",
        "tags": ["analytics"]
      }
    },
    "/v1/analytics/daily": {
      "get": {
        "description": "Day-by-day activity breakdown for a date range.",
        "operationId": "AnalyticsController_getDailyAnalytics",
        "parameters": [
          {
            "name": "startDate",
            "required": true,
            "in": "query",
            "description": "Start of the date range (YYYY-MM-DD)",
            "schema": {
              "format": "date",
              "example": "2026-03-16",
              "type": "string"
            }
          },
          {
            "name": "endDate",
            "required": true,
            "in": "query",
            "description": "End of the date range (YYYY-MM-DD)",
            "schema": {
              "format": "date",
              "example": "2026-03-22",
              "type": "string"
            }
          },
          {
            "name": "userId",
            "required": false,
            "in": "query",
            "description": "Filter to a specific user",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "campaignId",
            "required": false,
            "in": "query",
            "description": "Filter to a specific campaign",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Daily activity data with totals",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DailyAnalyticsResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Daily analytics",
        "tags": ["analytics"]
      }
    },
    "/v1/blacklist": {
      "post": {
        "description": "Create a new blacklist (suppression list).",
        "operationId": "BlacklistController_create",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateBlacklistDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedListDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Create blacklist",
        "tags": ["blacklist"]
      },
      "get": {
        "description": "List the blacklist (suppression) lists for the team.",
        "operationId": "BlacklistController_findAll",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of blacklists to return (1-100)",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor from a previous response, to fetch the next page",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedListsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List blacklists",
        "tags": ["blacklist"]
      }
    },
    "/v1/blacklist/{id}/leads": {
      "post": {
        "description": "Add profiles to a blacklist. Batch \u2014 up to 100 at a time.",
        "operationId": "BlacklistController_addLeads",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddLeadsToListDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddLeadsToListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Blacklist not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Add to blacklist",
        "tags": ["blacklist"]
      },
      "delete": {
        "description": "Remove profiles from a blacklist by lead ID and/or LinkedIn URL.",
        "operationId": "BlacklistController_removeLeads",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RemoveLeadsFromListDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveLeadsResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Blacklist not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Remove from blacklist",
        "tags": ["blacklist"]
      },
      "get": {
        "description": "List the excluded profiles in a blacklist.",
        "operationId": "BlacklistController_findLeads",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of leads to return (1-100)",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor from a previous response, to fetch the next page",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedListLeadsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Blacklist not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List blacklist entries",
        "tags": ["blacklist"]
      }
    },
    "/v1/blacklist/{id}": {
      "get": {
        "description": "Fetch a single blacklist by ID.",
        "operationId": "BlacklistController_findOne",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Blacklist not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get blacklist",
        "tags": ["blacklist"]
      }
    },
    "/v1/campaigns": {
      "post": {
        "description": "Create a campaign (name + optional dates). The message sequence is built, and the campaign launched, in the app \u2014 neither is settable via the API.",
        "operationId": "CampaignsController_create",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCampaignDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Create campaign",
        "tags": ["campaigns"]
      },
      "get": {
        "description": "Get all campaigns for the authenticated team, with optional status and owner filters.",
        "operationId": "CampaignsController_findAll",
        "parameters": [
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by campaign status",
            "schema": {
              "enum": ["ACTIVE", "PAUSED", "ARCHIVED", "COMPLETED", "CREATED"],
              "type": "string"
            }
          },
          {
            "name": "userIds",
            "required": false,
            "in": "query",
            "description": "Filter by owner user IDs",
            "schema": {
              "example": ["123e4567-e89b-12d3-a456-426614174001", "123e4567-e89b-12d3-a456-426614174002"],
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Return campaigns matching the criteria",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CampaignDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List campaigns",
        "tags": ["campaigns"]
      }
    },
    "/v1/campaigns/{id}": {
      "patch": {
        "description": "Update a campaign\u2019s name and/or start/end dates. Does not change the message sequence or status.",
        "operationId": "CampaignsController_update",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCampaignDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Campaign not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Update campaign",
        "tags": ["campaigns"]
      },
      "get": {
        "description": "Fetch a single campaign by ID.",
        "operationId": "CampaignsController_findOne",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Return the campaign if found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Campaign not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get campaign",
        "tags": ["campaigns"]
      },
      "delete": {
        "description": "Delete a campaign. The campaign is hidden immediately; its leads and running activity are cancelled and removed in the background (async \u2014 returns 202).",
        "operationId": "CampaignsController_remove",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignAsyncOpResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Campaign not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Delete campaign",
        "tags": ["campaigns"]
      }
    },
    "/v1/campaigns/{id}/archive": {
      "post": {
        "description": "Archive a campaign. The campaign is hidden immediately and moved to ARCHIVED in the background (async \u2014 returns 202).",
        "operationId": "CampaignsController_archive",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignAsyncOpResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Campaign not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Archive campaign",
        "tags": ["campaigns"]
      }
    },
    "/v1/campaigns/{id}/leads": {
      "get": {
        "description": "List the leads in a campaign, with optional filters.",
        "operationId": "CampaignsController_getLeads",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "campaignStatus",
            "required": false,
            "in": "query",
            "description": "Filter by `campaignStatus`: the campaign's status when the lead was added. It does not reflect where the lead is in its sequence.",
            "schema": {
              "type": "string",
              "enum": ["CREATED", "ACTIVE", "PAUSED", "ERROR", "ARCHIVED", "COMPLETED"]
            }
          },
          {
            "name": "hasReplied",
            "required": false,
            "in": "query",
            "description": "Filter to leads that have / have not replied",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of leads to return (1-100)",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor from a previous response, to fetch the next page",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedCampaignLeadsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Campaign not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List campaign leads",
        "tags": ["campaigns"]
      }
    },
    "/v1/campaigns/{id}/pause": {
      "post": {
        "description": "Pause a running campaign.",
        "operationId": "CampaignsController_pause",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignStatusResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Campaign not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Pause campaign",
        "tags": ["campaigns"]
      }
    },
    "/v1/campaigns/{id}/pause-activities": {
      "post": {
        "description": "Pause specific activity types for a campaign without pausing the whole campaign.",
        "operationId": "CampaignsController_pauseActivities",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActivityTypesBodyDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PausedActivitiesResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Campaign not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Pause campaign activities",
        "tags": ["campaigns"]
      }
    },
    "/v1/campaigns/{id}/paused-activities": {
      "get": {
        "description": "List the activity types currently paused for a campaign.",
        "operationId": "CampaignsController_getPausedActivities",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PausedActivitiesResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Campaign not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List paused activities",
        "tags": ["campaigns"]
      }
    },
    "/v1/campaigns/{id}/resume": {
      "post": {
        "description": "Resume a paused campaign.",
        "operationId": "CampaignsController_resume",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignStatusResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Campaign not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Resume campaign",
        "tags": ["campaigns"]
      }
    },
    "/v1/campaigns/{id}/resume-activities": {
      "post": {
        "description": "Resume previously paused activity types for a campaign.",
        "operationId": "CampaignsController_resumeActivities",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActivityTypesBodyDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PausedActivitiesResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Campaign not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Resume campaign activities",
        "tags": ["campaigns"]
      }
    },
    "/v1/campaigns/{id}/statistics": {
      "get": {
        "description": "Detailed campaign performance: invitations/messages sent and accepted/replied, profile visits, and rates.",
        "operationId": "CampaignsController_getStatistics",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign statistics with rates",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignStatisticsResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Campaign not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Campaign statistics",
        "tags": ["campaigns"]
      }
    },
    "/v1/campaigns/{id}/sequence": {
      "get": {
        "description": "The campaign sequence (steps and branches) as configured in the editor. Branching steps expose their conditions \u2014 e.g. connected vs. not connected, invitation accepted vs. expired, replied vs. not replied. Use the `format` query parameter to choose the representation.",
        "operationId": "CampaignsController_getSequence",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "required": false,
            "in": "query",
            "description": "Response shape: `graph` (default, faithful/lossless), `tree` (nested branches), `readable` (human outline), or `all`.",
            "schema": {
              "type": "string",
              "default": "graph",
              "enum": ["graph", "tree", "readable", "all"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The campaign sequence in the requested format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignSequenceResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Campaign not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get campaign sequence",
        "tags": ["campaigns"]
      }
    },
    "/v1/conversations/{id}/read": {
      "post": {
        "operationId": "MessagingController_setReadStatus",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReadStatusDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReadStatusResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Conversation not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Mark a conversation read or unread",
        "tags": ["messaging"]
      }
    },
    "/v1/conversations/{id}/reply": {
      "post": {
        "description": "Reply to a conversation by its id: the `chatId` returned by POST /v1/messages, GET /v1/leads/{id} or the LEAD_MESSAGE_REPLIED webhook, or the conversation id in the Botdog inbox URL. The conversation must be with one of your team\u2019s leads. Rate-limited per sending account.",
        "operationId": "MessagingController_reply",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplyDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendMessageResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Conversation not found, or not with one of your team\u2019s leads"
          },
          "429": {
            "description": "Per-account send limit reached"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Reply to a conversation",
        "tags": ["messaging"]
      }
    },
    "/v1/health": {
      "get": {
        "operationId": "HealthController_check",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The API is healthy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthCheckResponse"
                }
              }
            }
          }
        },
        "summary": "Get API health status",
        "tags": ["health"]
      }
    },
    "/v1/leads": {
      "get": {
        "description": "List leads for the team, with optional filters. Pass `linkedinUrl` to find the lead IDs for one LinkedIn profile \u2014 the usual first step before `DELETE /v1/leads/{id}` when a prospect has replied somewhere else.",
        "operationId": "LeadsController_findMany",
        "parameters": [
          {
            "name": "campaignId",
            "required": false,
            "in": "query",
            "description": "Filter by campaign ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "linkedinUrl",
            "required": false,
            "in": "query",
            "description": "Find the leads for one LinkedIn profile. The URL is normalized before matching, so any spelling works \u2014 `http`/`https`, with or without `www.`, a trailing slash, or tracking parameters. Sales Navigator and private (39-character) profile URLs are matched too.\n\nThe same person can hold one lead per campaign and one per list, so this can return several leads \u2014 combine it with `campaignId` to narrow to the sequence you mean. Returns 400 if the value is not a LinkedIn profile URL.",
            "example": "https://www.linkedin.com/in/jane-doe",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "listId",
            "required": false,
            "in": "query",
            "description": "Filter by list ID",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "campaignStatus",
            "required": false,
            "in": "query",
            "description": "Filter by `campaignStatus`: the campaign's status when the lead was added. It does not reflect where the lead is in its sequence.",
            "schema": {
              "type": "string",
              "enum": ["CREATED", "ACTIVE", "PAUSED", "ERROR", "ARCHIVED", "COMPLETED"]
            }
          },
          {
            "name": "replied",
            "required": false,
            "in": "query",
            "description": "Filter to leads that have / have not replied",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "unread",
            "required": false,
            "in": "query",
            "description": "Filter to leads with / without an unread reply",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "skipped",
            "required": false,
            "in": "query",
            "description": "Filter to leads that were / were not skipped",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "skippedReason",
            "required": false,
            "in": "query",
            "description": "Filter by skip reason",
            "schema": {
              "type": "string",
              "enum": ["DUPLICATE_IN_ANOTHER_CAMPAIGN_AT_IMPORT", "DUPLICATE_IN_ANOTHER_TEAM_CAMPAIGN_AT_IMPORT"]
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of leads to return (1-100)",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor from a previous response, to fetch the next page",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedLeadsDto"
                }
              }
            }
          },
          "400": {
            "description": "`linkedinUrl` is not a LinkedIn profile URL"
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List leads",
        "tags": ["leads"]
      }
    },
    "/v1/leads/add_to_campaign": {
      "post": {
        "description": "Add leads to an existing campaign. Batch operation \u2014 up to 100 leads at a time.",
        "operationId": "LeadsController_createBatch",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateLeadsBatchDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The leads have been successfully added to the campaign",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateLeadsBatchResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Add leads to a campaign",
        "tags": ["leads"]
      }
    },
    "/v1/leads/{id}": {
      "get": {
        "description": "Get a single lead by ID, including its full event history.",
        "operationId": "LeadsController_findOne",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LeadDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Lead not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get lead",
        "tags": ["leads"]
      },
      "patch": {
        "description": "Update a lead\u2019s editable metadata (notes, custom fields, name, company). Does not change campaign or workflow status.",
        "operationId": "LeadsController_update",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateLeadDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LeadDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Lead not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Update lead",
        "tags": ["leads"]
      },
      "delete": {
        "description": "Stop a lead\u2019s campaign sequence (cancels further outreach). The lead record is retained.",
        "operationId": "LeadsController_stop",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopLeadResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Lead not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Stop lead",
        "tags": ["leads"]
      }
    },
    "/v1/lists": {
      "post": {
        "description": "Create a new lead/audience list.",
        "operationId": "ListsController_create",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateListDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedListDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Create list",
        "tags": ["lists"]
      },
      "get": {
        "description": "List the lead/audience lists for the team.",
        "operationId": "ListsController_findAll",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of lists to return (1-100)",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor from a previous response, to fetch the next page",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "includeBlacklist",
            "required": false,
            "in": "query",
            "description": "Include blacklist (suppression) lists in the results",
            "schema": {
              "default": false,
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedListsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List lists",
        "tags": ["lists"]
      }
    },
    "/v1/lists/{id}": {
      "get": {
        "description": "Fetch a single list by ID.",
        "operationId": "ListsController_findOne",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "List not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get list",
        "tags": ["lists"]
      }
    },
    "/v1/lists/{id}/leads": {
      "post": {
        "description": "Add leads to a list. Batch \u2014 up to 100 at a time.",
        "operationId": "ListsController_addLeads",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddLeadsToListDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddLeadsToListResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "List not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Add leads to a list",
        "tags": ["lists"]
      },
      "delete": {
        "description": "Remove leads from a list by lead ID and/or LinkedIn URL.",
        "operationId": "ListsController_removeLeads",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RemoveLeadsFromListDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveLeadsResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "List not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Remove leads from a list",
        "tags": ["lists"]
      },
      "get": {
        "description": "List the leads in a list.",
        "operationId": "ListsController_findLeads",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of leads to return (1-100)",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor from a previous response, to fetch the next page",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedListLeadsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "List not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List list leads",
        "tags": ["lists"]
      }
    },
    "/v1/messages": {
      "post": {
        "description": "Send a message into a lead\u2019s existing LinkedIn conversation. If Botdog has not recorded the conversation yet (for example the lead connected and wrote first), it is found on LinkedIn and recorded, and its id is returned as `chatId`. A lead added by profile URL may need its LinkedIn member id looked up first, which is limited to 10 lookups a minute and 100 a day per sending account. Never starts a new conversation. Rate-limited per sending account.",
        "operationId": "MessagingController_sendMessage",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMessageDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendMessageResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Lead not found"
          },
          "422": {
            "description": "No conversation exists with this lead yet"
          },
          "429": {
            "description": "Per-account send limit reached"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Send a message",
        "tags": ["messaging"]
      }
    },
    "/v1/messages/schedule": {
      "post": {
        "description": "Schedule a message to a lead\u2019s conversation for a future time (60s\u201390 days). Counts against the daily per-account budget.",
        "operationId": "MessagingController_schedule",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScheduleMessageDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledMessageResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "fireAt out of range"
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Lead not found"
          },
          "422": {
            "description": "No conversation exists yet"
          },
          "429": {
            "description": "Per-account daily limit reached"
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Schedule a message",
        "tags": ["messaging"]
      }
    },
    "/v1/snippets": {
      "get": {
        "description": "List message snippets/templates visible to the caller (personal + team-shared).",
        "operationId": "SnippetsController_findAll",
        "parameters": [
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Filter by snippet status",
            "schema": {
              "type": "string",
              "enum": ["ACTIVE", "ARCHIVED"]
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Maximum number of snippets to return (1-100)",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "type": "number"
            }
          },
          {
            "name": "cursor",
            "required": false,
            "in": "query",
            "description": "Cursor from a previous response, to fetch the next page",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedSnippetsDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List snippets",
        "tags": ["snippets"]
      },
      "post": {
        "description": "Create a personal (or team-shared) snippet/template.",
        "operationId": "SnippetsController_create",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSnippetDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SnippetDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "409": {
            "description": "Shortcut already in use"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Create snippet",
        "tags": ["snippets"]
      }
    },
    "/v1/snippets/{id}": {
      "patch": {
        "description": "Update one of your own snippets.",
        "operationId": "SnippetsController_update",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSnippetDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SnippetDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Snippet not found"
          },
          "409": {
            "description": "Shortcut already in use"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Update snippet",
        "tags": ["snippets"]
      },
      "delete": {
        "description": "Delete one of your own snippets.",
        "operationId": "SnippetsController_remove",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Snippet deleted"
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Snippet not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Delete snippet",
        "tags": ["snippets"]
      }
    },
    "/v1/teams/me": {
      "get": {
        "description": "Get the current team's name and user count.",
        "operationId": "TeamsController_getMyTeamInfo",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Team information retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MeResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Current team",
        "tags": ["teams"]
      }
    },
    "/v1/users": {
      "get": {
        "description": "List the users in your team.",
        "operationId": "UsersController_findAll",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Limit the number of results",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of users",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/UserDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List users",
        "tags": ["users"]
      }
    },
    "/v1/users/{id}": {
      "get": {
        "description": "Fetch a single team user by ID.",
        "operationId": "UsersController_findOne",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "User ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The user",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "User not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get user",
        "tags": ["users"]
      }
    },
    "/v1/webhooks": {
      "post": {
        "description": "Create a webhook subscription. The signing secret is never returned.",
        "operationId": "WebhooksController_create",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Create webhook",
        "tags": ["webhooks"]
      },
      "get": {
        "description": "List the webhook subscriptions for the team.",
        "operationId": "WebhooksController_findAll",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebhookDto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List webhooks",
        "tags": ["webhooks"]
      }
    },
    "/v1/webhooks/{id}": {
      "patch": {
        "description": "Update a webhook subscription (name, endpoint, and/or event types).",
        "operationId": "WebhooksController_update",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Webhook not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Update webhook",
        "tags": ["webhooks"]
      },
      "get": {
        "description": "Fetch a single webhook subscription by ID.",
        "operationId": "WebhooksController_findOne",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDto"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. API key is missing or invalid."
          },
          "404": {
            "description": "Webhook not found"
          },
          "429": {
            "description": "Rate limit exceeded. Retry after cooldown."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Get webhook",
        "tags": ["webhooks"]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "api-key": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "x-default": "YOUR_API_KEY"
      }
    },
    "schemas": {
      "MemoryUsage": {
        "type": "object",
        "properties": {
          "rss": {
            "type": "number",
            "description": "Resident Set Size - total memory allocated for the process execution",
            "example": 47378432
          },
          "heapTotal": {
            "type": "number",
            "description": "Total size of the allocated heap",
            "example": 23068672
          },
          "heapUsed": {
            "type": "number",
            "description": "Actual memory used by the heap",
            "example": 11601456
          },
          "external": {
            "type": "number",
            "description": "Memory used by C++ objects bound to JavaScript objects",
            "example": 1789775
          },
          "arrayBuffers": {
            "type": "number",
            "description": "Memory allocated for ArrayBuffers and SharedArrayBuffers",
            "example": 1064517
          }
        },
        "required": ["rss", "heapTotal", "heapUsed", "external"]
      },
      "HealthCheckResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Current status of the API",
            "example": "ok"
          },
          "timestamp": {
            "type": "string",
            "description": "Current server timestamp",
            "example": "2023-04-02T12:00:00.000Z"
          },
          "uptime": {
            "type": "number",
            "description": "Server uptime in seconds",
            "example": 3600
          },
          "memoryUsage": {
            "description": "Memory usage statistics",
            "allOf": [
              {
                "$ref": "#/components/schemas/MemoryUsage"
              }
            ]
          }
        },
        "required": ["status", "timestamp", "uptime", "memoryUsage"]
      },
      "UserDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier for the user",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "email": {
            "type": "string",
            "description": "The user's email address",
            "example": "user@example.com"
          },
          "name": {
            "type": "string",
            "description": "The user's name",
            "example": "John Doe"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the user was created",
            "example": "2023-04-02T12:00:00.000Z"
          }
        },
        "required": ["id", "email", "name", "createdAt"]
      },
      "LeadSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the lead"
          },
          "name": {
            "type": "string",
            "description": "The name of the lead",
            "example": "Jane Doe"
          },
          "linkedinProfile": {
            "type": "string",
            "description": "The LinkedIn profile URL of the lead",
            "example": "https://www.linkedin.com/in/jane-doe"
          },
          "company": {
            "type": "string",
            "description": "The lead's company",
            "nullable": true
          },
          "job": {
            "type": "string",
            "description": "The lead's job title",
            "nullable": true
          },
          "campaignId": {
            "type": "string",
            "description": "The campaign this lead belongs to, if any",
            "nullable": true
          },
          "listId": {
            "type": "string",
            "description": "The list this lead belongs to, if any",
            "nullable": true
          },
          "campaignStatus": {
            "type": "string",
            "description": "The campaign's status when this lead was added to it. It is not updated as the lead moves through its sequence, so it does not show whether the lead's sequence is still running, has finished, failed or was stopped. For that, read the lead's `events` on GET /v1/leads/{id}: `WORKFLOW_CLEANUP` means the sequence finished, `WORKFLOW_FAILED` means it failed, and `stoppedAt` is set when it was stopped through the API.",
            "nullable": true
          },
          "hasReplied": {
            "type": "boolean",
            "description": "Whether the lead has replied"
          },
          "hasUnreadReply": {
            "type": "boolean",
            "description": "Whether the lead has an unread reply"
          },
          "isSkipped": {
            "type": "boolean",
            "description": "Whether the lead was skipped"
          },
          "skippedReason": {
            "type": "string",
            "description": "Why the lead was skipped, if applicable",
            "nullable": true
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the lead was created"
          },
          "invitedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the invite was sent",
            "nullable": true
          },
          "connectedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the lead connected",
            "nullable": true
          },
          "repliedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the lead replied",
            "nullable": true
          },
          "withdrawnAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the invitation was withdrawn",
            "nullable": true
          },
          "stoppedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the lead was stopped, if it was. This is the authoritative confirmation that a stop took effect: it is written only after the lead’s sequence has been torn down, so a non-null value means no further steps — including any message already waiting on a delay — can run. Poll this after DELETE /v1/leads/{id} rather than the `stopped` flag on the stop response.",
            "nullable": true
          },
          "senderUserId": {
            "type": "string",
            "description": "The Botdog user whose LinkedIn account contacts this lead: for a team-campaign lead, the teammate it is assigned to; otherwise the lead’s owner. Null for a team-campaign lead no teammate has picked up yet.",
            "example": "user_abc123",
            "nullable": true
          }
        },
        "required": ["id", "name", "linkedinProfile", "hasReplied", "hasUnreadReply", "isSkipped", "createdAt"]
      },
      "PaginatedLeadsDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LeadSummaryDto"
            }
          },
          "nextCursor": {
            "type": "string",
            "description": "Opaque cursor for the next page; null when there are no more results. Pass it back as the `cursor` query parameter.",
            "nullable": true
          }
        },
        "required": ["data", "nextCursor"]
      },
      "LeadEventDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the event"
          },
          "type": {
            "type": "string",
            "description": "The event type",
            "example": "INVITE_SENT"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the event occurred"
          },
          "scheduledAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the underlying action was scheduled, if applicable",
            "nullable": true
          },
          "details": {
            "type": "string",
            "description": "Human-readable details about the event",
            "nullable": true
          },
          "cancellationReason": {
            "type": "string",
            "description": "Reason an action was cancelled, if applicable",
            "nullable": true
          },
          "senderUserId": {
            "type": "string",
            "description": "The Botdog user whose LinkedIn account performed this event, when recorded (sends). Null for events with no performing account, such as acceptances and replies.",
            "example": "user_abc123",
            "nullable": true
          }
        },
        "required": ["id", "type", "createdAt"]
      },
      "LeadDetailDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the lead"
          },
          "name": {
            "type": "string",
            "description": "The name of the lead",
            "example": "Jane Doe"
          },
          "linkedinProfile": {
            "type": "string",
            "description": "The LinkedIn profile URL of the lead",
            "example": "https://www.linkedin.com/in/jane-doe"
          },
          "company": {
            "type": "string",
            "description": "The lead's company",
            "nullable": true
          },
          "job": {
            "type": "string",
            "description": "The lead's job title",
            "nullable": true
          },
          "campaignId": {
            "type": "string",
            "description": "The campaign this lead belongs to, if any",
            "nullable": true
          },
          "listId": {
            "type": "string",
            "description": "The list this lead belongs to, if any",
            "nullable": true
          },
          "campaignStatus": {
            "type": "string",
            "description": "The campaign's status when this lead was added to it. It is not updated as the lead moves through its sequence, so it does not show whether the lead's sequence is still running, has finished, failed or was stopped. For that, read the lead's `events` on GET /v1/leads/{id}: `WORKFLOW_CLEANUP` means the sequence finished, `WORKFLOW_FAILED` means it failed, and `stoppedAt` is set when it was stopped through the API.",
            "nullable": true
          },
          "hasReplied": {
            "type": "boolean",
            "description": "Whether the lead has replied"
          },
          "hasUnreadReply": {
            "type": "boolean",
            "description": "Whether the lead has an unread reply"
          },
          "isSkipped": {
            "type": "boolean",
            "description": "Whether the lead was skipped"
          },
          "skippedReason": {
            "type": "string",
            "description": "Why the lead was skipped, if applicable",
            "nullable": true
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the lead was created"
          },
          "location": {
            "type": "string",
            "description": "The lead's location",
            "nullable": true
          },
          "lastMessage": {
            "type": "string",
            "description": "The most recent message in the conversation",
            "nullable": true
          },
          "chatId": {
            "type": "string",
            "description": "Id of the lead\u2019s standard LinkedIn conversation \u2014 the `id` that POST /v1/conversations/{id}/reply expects. Null until Botdog has linked one; POST /v1/messages does not need it, and records it on first use.",
            "nullable": true
          },
          "statusNote": {
            "type": "string",
            "description": "A free-text status note",
            "nullable": true
          },
          "customField1": {
            "type": "string",
            "nullable": true
          },
          "customField2": {
            "type": "string",
            "nullable": true
          },
          "customField3": {
            "type": "string",
            "nullable": true
          },
          "customAttributes": {
            "type": "object",
            "description": "Custom key-value attributes supplied when the lead was added",
            "nullable": true
          },
          "invitedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the invite was sent",
            "nullable": true
          },
          "connectedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the lead connected",
            "nullable": true
          },
          "repliedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the lead replied",
            "nullable": true
          },
          "withdrawnAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the invitation was withdrawn",
            "nullable": true
          },
          "stoppedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the lead was stopped, if it was. This is the authoritative confirmation that a stop took effect: it is written only after the lead’s sequence has been torn down, so a non-null value means no further steps — including any message already waiting on a delay — can run. Poll this after DELETE /v1/leads/{id} rather than the `stopped` flag on the stop response.",
            "nullable": true
          },
          "senderUserId": {
            "type": "string",
            "description": "The Botdog user whose LinkedIn account contacts this lead: for a team-campaign lead, the teammate it is assigned to; otherwise the lead’s owner. Null for a team-campaign lead no teammate has picked up yet.",
            "example": "user_abc123",
            "nullable": true
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the lead was last updated"
          },
          "events": {
            "description": "Chronological event history for the lead",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LeadEventDto"
            }
          }
        },
        "required": [
          "id",
          "name",
          "linkedinProfile",
          "hasReplied",
          "hasUnreadReply",
          "isSkipped",
          "createdAt",
          "updatedAt",
          "events"
        ]
      },
      "UpdateLeadDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The lead's name"
          },
          "company": {
            "type": "string",
            "description": "The lead's company"
          },
          "statusNote": {
            "type": "string",
            "description": "A free-text status note"
          },
          "customField1": {
            "type": "string",
            "description": "Custom field 1"
          },
          "customField2": {
            "type": "string",
            "description": "Custom field 2"
          },
          "customField3": {
            "type": "string",
            "description": "Custom field 3"
          },
          "customAttributes": {
            "type": "object",
            "description": "Custom key-value attributes (replaces the existing object)",
            "additionalProperties": true
          }
        }
      },
      "StopLeadResponseDto": {
        "type": "object",
        "properties": {
          "leadId": {
            "type": "string",
            "description": "The lead that was stopped"
          },
          "stopped": {
            "type": "boolean",
            "description": "Always true on success",
            "example": true
          }
        },
        "required": ["leadId", "stopped"]
      },
      "LeadCreateDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The name of the lead",
            "example": "John Doe"
          },
          "linkedinUrl": {
            "type": "string",
            "description": "LinkedIn profile URL of the lead (https:// will be added automatically if missing)",
            "example": "https://www.linkedin.com/in/johndoe"
          },
          "title": {
            "type": "string",
            "description": "Job title of the lead",
            "example": "Software Engineer"
          },
          "company": {
            "type": "string",
            "description": "Company of the lead",
            "example": "Acme Inc."
          },
          "location": {
            "type": "string",
            "description": "Location of the lead",
            "example": "San Francisco, CA"
          },
          "customAttributes": {
            "type": "object",
            "description": "Any additional attributes for the lead that can be used in workflow messages",
            "example": {
              "customKey1": "customValue1",
              "customKey2": "customValue2"
            }
          }
        },
        "required": ["linkedinUrl"]
      },
      "CreateLeadsBatchDto": {
        "type": "object",
        "properties": {
          "campaignId": {
            "type": "string",
            "description": "The campaign ID to add these leads to",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "leads": {
            "description": "Array of leads to create (maximum 100)",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LeadCreateDto"
            }
          }
        },
        "required": ["campaignId", "leads"]
      },
      "CreateLeadsBatchResponseDto": {
        "type": "object",
        "properties": {
          "count": {
            "type": "number",
            "description": "The number of leads successfully created",
            "example": 50
          },
          "campaignId": {
            "type": "string",
            "description": "The campaign ID the leads were added to",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          }
        },
        "required": ["count", "campaignId"]
      },
      "CreateCampaignDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The campaign name",
            "example": "Q4 Outreach"
          },
          "startDate": {
            "type": "string",
            "description": "When the campaign should start (ISO 8601)",
            "example": "2026-07-01T09:00:00Z"
          },
          "endDate": {
            "type": "string",
            "description": "When the campaign should end (ISO 8601)",
            "example": "2026-08-01T09:00:00Z"
          }
        },
        "required": ["name"]
      },
      "CampaignDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the campaign",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "name": {
            "type": "string",
            "description": "The name of the campaign",
            "example": "Q4 Sales Outreach"
          },
          "status": {
            "type": "string",
            "description": "The current status of the campaign",
            "example": "ACTIVE",
            "enum": ["ACTIVE", "PAUSED", "ARCHIVED", "COMPLETED", "CREATED"]
          },
          "ownerId": {
            "type": "string",
            "description": "The ID of the user who owns this campaign",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "ownerEmail": {
            "type": "string",
            "description": "The email of the user who owns this campaign",
            "example": "john.doe@example.com"
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "The date the campaign was created",
            "example": "2023-09-15T12:00:00Z"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "The date the campaign was last updated",
            "example": "2023-09-16T14:30:00Z"
          },
          "startDate": {
            "format": "date-time",
            "type": "string",
            "description": "The date the campaign will start",
            "example": "2023-09-15T12:00:00Z"
          },
          "endDate": {
            "format": "date-time",
            "type": "string",
            "description": "The date the campaign will end",
            "example": "2023-09-15T12:00:00Z"
          }
        },
        "required": ["id", "name", "status", "ownerId", "ownerEmail", "createdAt", "updatedAt", "startDate", "endDate"]
      },
      "SequenceGraphNodeDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "number",
            "description": "Stable numeric id of the step",
            "example": 2
          },
          "stepType": {
            "type": "string",
            "description": "Step type",
            "example": "WAIT_FOR_INVITATION_RESPONSE"
          },
          "label": {
            "type": "string",
            "description": "Human-readable label for the step type",
            "example": "Wait for invitation response"
          },
          "metadata": {
            "type": "object",
            "description": "Step configuration (message text, wait duration, etc.)",
            "additionalProperties": true
          },
          "next": {
            "type": "number",
            "nullable": true,
            "description": "Next step id for a linear step, or null to end this branch. Present only on non-branching steps.",
            "example": 3
          },
          "choice": {
            "type": "object",
            "description": "Branch map for a conditional step: outcome -> next step id (null ends the branch). Present only on branching steps.",
            "additionalProperties": {
              "type": "number",
              "nullable": true
            },
            "example": {
              "invitation_accepted": 3,
              "expired": null
            }
          }
        },
        "required": ["id", "stepType", "label"]
      },
      "CampaignSequenceResponseDto": {
        "type": "object",
        "properties": {
          "campaignId": {
            "type": "string",
            "description": "The id of the campaign this sequence belongs to",
            "example": "123e4567-e89b-12d3-a456-426614174000"
          },
          "editorViewType": {
            "type": "string",
            "description": "Which editor produced the campaign",
            "example": "ADVANCED",
            "enum": ["SIMPLE", "ADVANCED"]
          },
          "source": {
            "type": "string",
            "description": "Where the sequence was read from: `workflow` (modern), `legacy` (old linear sequences), or `empty` (no sequence yet).",
            "example": "workflow",
            "enum": ["workflow", "legacy", "empty"]
          },
          "firstStep": {
            "type": "number",
            "nullable": true,
            "description": "Id of the first step to run, or null when there is no sequence",
            "example": 1
          },
          "steps": {
            "type": "array",
            "description": "Present when format is `graph` or `all`",
            "items": {
              "$ref": "#/components/schemas/SequenceGraphNodeDto"
            }
          },
          "tree": {
            "type": "object",
            "nullable": true,
            "description": "Present when format is `tree` or `all`. Nested step tree rooted at the first step; null for an empty sequence.",
            "additionalProperties": true
          },
          "readable": {
            "type": "array",
            "description": "Present when format is `readable` or `all`. One line per step/branch.",
            "items": {
              "type": "string"
            },
            "example": [
              "Step 1 \u00b7 Send invitation (start)",
              "    \u2192 then step 2 (Wait for invitation response)"
            ]
          }
        },
        "required": ["campaignId", "editorViewType", "source"]
      },
      "UpdateCampaignDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The campaign name"
          },
          "startDate": {
            "type": "string",
            "description": "When the campaign should start (ISO 8601)"
          },
          "endDate": {
            "type": "string",
            "description": "When the campaign should end (ISO 8601)"
          }
        }
      },
      "CampaignStatisticsDto": {
        "type": "object",
        "properties": {
          "progress": {
            "type": "number",
            "description": "Campaign completion percentage",
            "example": 45.2,
            "nullable": true
          },
          "totalLeadCount": {
            "type": "number",
            "example": 500
          },
          "totalLeadsInProgress": {
            "type": "number",
            "example": 224
          },
          "totalLeadsSkipped": {
            "type": "number",
            "example": 12
          },
          "invitationsSent": {
            "type": "number",
            "example": 264
          },
          "invitationsAccepted": {
            "type": "number",
            "example": 37
          },
          "invitationsSkipped": {
            "type": "number",
            "example": 5
          },
          "messagesSent": {
            "type": "number",
            "example": 37
          },
          "inmailsSent": {
            "type": "number",
            "example": 0
          },
          "messagesReplied": {
            "type": "number",
            "example": 3
          },
          "profileVisits": {
            "type": "number",
            "example": 264
          },
          "companyInvitationsSent": {
            "type": "number",
            "example": 0
          },
          "postsReactions": {
            "type": "number",
            "example": 0
          },
          "aiAccepted": {
            "type": "number",
            "example": 480
          },
          "aiRejected": {
            "type": "number",
            "example": 8
          }
        },
        "required": [
          "progress",
          "totalLeadCount",
          "totalLeadsInProgress",
          "totalLeadsSkipped",
          "invitationsSent",
          "invitationsAccepted",
          "invitationsSkipped",
          "messagesSent",
          "inmailsSent",
          "messagesReplied",
          "profileVisits",
          "companyInvitationsSent",
          "postsReactions",
          "aiAccepted",
          "aiRejected"
        ]
      },
      "CampaignRatesDto": {
        "type": "object",
        "properties": {
          "invitationAcceptanceRate": {
            "type": "number",
            "description": "Invitation acceptance rate percentage",
            "example": 14
          },
          "messageReplyRate": {
            "type": "number",
            "description": "Message reply rate percentage",
            "example": 6
          }
        },
        "required": ["invitationAcceptanceRate", "messageReplyRate"]
      },
      "CampaignStatisticsResponseDto": {
        "type": "object",
        "properties": {
          "campaignId": {
            "type": "string",
            "example": "cm1abc123def456"
          },
          "campaignName": {
            "type": "string",
            "example": "Cold Outreach - ML Engineers"
          },
          "status": {
            "type": "string",
            "example": "ACTIVE"
          },
          "owner": {
            "type": "string",
            "example": "sid@pavoai.com"
          },
          "statistics": {
            "$ref": "#/components/schemas/CampaignStatisticsDto"
          },
          "rates": {
            "$ref": "#/components/schemas/CampaignRatesDto"
          }
        },
        "required": ["campaignId", "campaignName", "status", "owner", "statistics", "rates"]
      },
      "CampaignLeadDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the lead"
          },
          "name": {
            "type": "string",
            "description": "The name of the lead",
            "example": "Jane Doe"
          },
          "linkedinProfile": {
            "type": "string",
            "description": "The LinkedIn profile URL of the lead",
            "example": "https://www.linkedin.com/in/jane-doe"
          },
          "company": {
            "type": "string",
            "description": "The lead's company",
            "nullable": true
          },
          "job": {
            "type": "string",
            "description": "The lead's job title",
            "nullable": true
          },
          "campaignStatus": {
            "type": "string",
            "description": "The campaign's status when this lead was added to it. It is not updated as the lead moves through its sequence, so it does not show whether the lead's sequence is still running, has finished, failed or was stopped. For that, read the lead's `events` on GET /v1/leads/{id}: `WORKFLOW_CLEANUP` means the sequence finished, `WORKFLOW_FAILED` means it failed, and `stoppedAt` is set when it was stopped through the API.",
            "enum": ["CREATED", "ACTIVE", "PAUSED", "ERROR", "ARCHIVED", "COMPLETED"]
          },
          "hasReplied": {
            "type": "boolean",
            "description": "Whether the lead has replied",
            "example": false
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the lead was added to the campaign"
          },
          "senderUserId": {
            "type": "string",
            "description": "The Botdog user whose LinkedIn account contacts this lead: for a team-campaign lead, the teammate it is assigned to; otherwise the lead’s owner. Null for a team-campaign lead no teammate has picked up yet.",
            "example": "user_abc123",
            "nullable": true
          }
        },
        "required": ["id", "name", "linkedinProfile", "campaignStatus", "hasReplied", "createdAt"]
      },
      "PaginatedCampaignLeadsDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CampaignLeadDto"
            }
          },
          "nextCursor": {
            "type": "string",
            "description": "Opaque cursor for the next page; null when there are no more results. Pass it back as the `cursor` query parameter.",
            "nullable": true
          }
        },
        "required": ["data", "nextCursor"]
      },
      "CampaignStatusResponseDto": {
        "type": "object",
        "properties": {
          "campaignId": {
            "type": "string",
            "description": "The campaign identifier"
          },
          "status": {
            "type": "string",
            "description": "The resulting campaign status",
            "example": "PAUSED"
          }
        },
        "required": ["campaignId", "status"]
      },
      "PausedActivityDto": {
        "type": "object",
        "properties": {
          "activityType": {
            "type": "string",
            "enum": [
              "PROFILE_VISIT",
              "SEND_INVITATION",
              "SEND_MESSAGE",
              "SEND_INMAIL",
              "SEND_COMPANY_PAGE_INVITATION",
              "REACT_TO_POSTS"
            ]
          },
          "pausedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the activity type was paused"
          }
        },
        "required": ["activityType", "pausedAt"]
      },
      "PausedActivitiesResponseDto": {
        "type": "object",
        "properties": {
          "pausedActivities": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PausedActivityDto"
            }
          }
        },
        "required": ["pausedActivities"]
      },
      "ActivityTypesBodyDto": {
        "type": "object",
        "properties": {
          "activityTypes": {
            "type": "array",
            "description": "The activity types to pause/resume",
            "example": ["SEND_INVITATION"],
            "items": {
              "type": "string",
              "enum": [
                "PROFILE_VISIT",
                "SEND_INVITATION",
                "SEND_MESSAGE",
                "SEND_INMAIL",
                "SEND_COMPANY_PAGE_INVITATION",
                "REACT_TO_POSTS"
              ]
            }
          }
        },
        "required": ["activityTypes"]
      },
      "CampaignAsyncOpResponseDto": {
        "type": "object",
        "properties": {
          "campaignId": {
            "type": "string",
            "description": "The campaign identifier"
          },
          "status": {
            "type": "string",
            "description": "The accepted async operation. The campaign is hidden immediately and removed/archived in the background.",
            "example": "deletion_requested",
            "enum": ["deletion_requested", "archival_requested"]
          }
        },
        "required": ["campaignId", "status"]
      },
      "InvitationsDto": {
        "type": "object",
        "properties": {
          "sent": {
            "type": "number",
            "example": 677
          },
          "accepted": {
            "type": "number",
            "example": 92
          },
          "rate": {
            "type": "number",
            "description": "Acceptance rate as percentage",
            "example": 13.6
          }
        },
        "required": ["sent", "accepted", "rate"]
      },
      "MessagesDto": {
        "type": "object",
        "properties": {
          "sent": {
            "type": "number",
            "example": 92
          },
          "replied": {
            "type": "number",
            "example": 4
          },
          "rate": {
            "type": "number",
            "description": "Reply rate as percentage",
            "example": 4.3
          }
        },
        "required": ["sent", "replied", "rate"]
      },
      "CampaignsSummaryDto": {
        "type": "object",
        "properties": {
          "active": {
            "type": "number",
            "example": 6
          },
          "totalLeadsInProgress": {
            "type": "number",
            "example": 1420
          }
        },
        "required": ["active", "totalLeadsInProgress"]
      },
      "AnalyticsResponseDto": {
        "type": "object",
        "properties": {
          "period": {
            "type": "string",
            "example": "last-7-days"
          },
          "invitations": {
            "$ref": "#/components/schemas/InvitationsDto"
          },
          "messages": {
            "$ref": "#/components/schemas/MessagesDto"
          },
          "campaigns": {
            "$ref": "#/components/schemas/CampaignsSummaryDto"
          }
        },
        "required": ["period", "invitations", "messages", "campaigns"]
      },
      "DailyDataPointDto": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "example": "2026-03-19"
          },
          "invitationsSent": {
            "type": "number",
            "example": 142
          },
          "invitationsAccepted": {
            "type": "number",
            "example": 16
          },
          "messagesSent": {
            "type": "number",
            "example": 16
          },
          "messagesReplied": {
            "type": "number",
            "example": 1
          }
        },
        "required": ["date", "invitationsSent", "invitationsAccepted", "messagesSent", "messagesReplied"]
      },
      "DailyTotalsDto": {
        "type": "object",
        "properties": {
          "invitationsSent": {
            "type": "number",
            "example": 677
          },
          "invitationsAccepted": {
            "type": "number",
            "example": 92
          },
          "messagesSent": {
            "type": "number",
            "example": 92
          },
          "messagesReplied": {
            "type": "number",
            "example": 4
          },
          "invitationAcceptanceRate": {
            "type": "number",
            "example": 13.6
          },
          "messageReplyRate": {
            "type": "number",
            "example": 4.3
          }
        },
        "required": [
          "invitationsSent",
          "invitationsAccepted",
          "messagesSent",
          "messagesReplied",
          "invitationAcceptanceRate",
          "messageReplyRate"
        ]
      },
      "DailyAnalyticsResponseDto": {
        "type": "object",
        "properties": {
          "startDate": {
            "type": "string",
            "example": "2026-03-16"
          },
          "endDate": {
            "type": "string",
            "example": "2026-03-22"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DailyDataPointDto"
            }
          },
          "totals": {
            "$ref": "#/components/schemas/DailyTotalsDto"
          }
        },
        "required": ["startDate", "endDate", "data", "totals"]
      },
      "UserAnalyticsDto": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "example": "user_abc123"
          },
          "email": {
            "type": "string",
            "example": "anannya@pavoai.com"
          },
          "name": {
            "type": "string",
            "example": "Anannya Mishra"
          },
          "invitations": {
            "$ref": "#/components/schemas/InvitationsDto"
          },
          "messages": {
            "$ref": "#/components/schemas/MessagesDto"
          }
        },
        "required": ["userId", "email", "name", "invitations", "messages"]
      },
      "ByUserAnalyticsResponseDto": {
        "type": "object",
        "properties": {
          "period": {
            "type": "string",
            "example": "last-7-days"
          },
          "users": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UserAnalyticsDto"
            }
          }
        },
        "required": ["period", "users"]
      },
      "MeResponseDto": {
        "type": "object",
        "properties": {
          "teamName": {
            "type": "string",
            "example": "Acme Corp",
            "description": "Name of your organization"
          },
          "userCount": {
            "type": "number",
            "example": 5,
            "description": "Count of users in your organization"
          }
        },
        "required": ["teamName", "userCount"]
      },
      "DcrRegisterDto": {
        "type": "object",
        "properties": {
          "client_name": {
            "type": "string"
          },
          "redirect_uris": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "grant_types": {
            "default": ["authorization_code", "refresh_token"],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "token_endpoint_auth_method": {
            "type": "string"
          },
          "response_types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "scope": {
            "type": "string"
          },
          "client_uri": {
            "type": "string"
          },
          "logo_uri": {
            "type": "string"
          },
          "contacts": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "tos_uri": {
            "type": "string"
          },
          "policy_uri": {
            "type": "string"
          },
          "jwks_uri": {
            "type": "string"
          },
          "jwks": {
            "type": "object"
          },
          "software_id": {
            "type": "string"
          },
          "software_version": {
            "type": "string"
          }
        },
        "required": ["client_name", "redirect_uris"]
      },
      "AuthorizeQueryDto": {
        "type": "object",
        "properties": {}
      },
      "FinalizeRequestDto": {
        "type": "object",
        "properties": {}
      },
      "TokenRequestDto": {
        "type": "object",
        "properties": {}
      },
      "RevokeRequestDto": {
        "type": "object",
        "properties": {}
      },
      "CreateListDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The name of the list",
            "example": "Q4 prospects"
          },
          "isBlacklist": {
            "type": "boolean",
            "description": "Whether this list is a blacklist (suppression list)",
            "default": false
          }
        },
        "required": ["name"]
      },
      "CreatedListDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "isBlacklist": {
            "type": "boolean"
          }
        },
        "required": ["id", "name", "isBlacklist"]
      },
      "ListLeadInputDto": {
        "type": "object",
        "properties": {
          "linkedinUrl": {
            "type": "string",
            "description": "LinkedIn profile URL (https:// is added automatically if missing)",
            "example": "https://www.linkedin.com/in/johndoe"
          },
          "name": {
            "type": "string",
            "description": "The name of the lead"
          },
          "company": {
            "type": "string",
            "description": "Company of the lead"
          },
          "location": {
            "type": "string",
            "description": "Location of the lead"
          },
          "title": {
            "type": "string",
            "description": "Job title of the lead"
          }
        },
        "required": ["linkedinUrl"]
      },
      "AddLeadsToListDto": {
        "type": "object",
        "properties": {
          "leads": {
            "description": "Profiles to add (maximum 100). Duplicates are skipped.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListLeadInputDto"
            }
          }
        },
        "required": ["leads"]
      },
      "AddLeadsToListResponseDto": {
        "type": "object",
        "properties": {
          "listId": {
            "type": "string",
            "description": "The list the leads were added to"
          },
          "count": {
            "type": "number",
            "description": "Number of leads created (duplicates skipped)"
          }
        },
        "required": ["listId", "count"]
      },
      "RemoveLeadsFromListDto": {
        "type": "object",
        "properties": {
          "leadIds": {
            "description": "Lead IDs to remove",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "linkedinUrls": {
            "description": "LinkedIn profile URLs to remove",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "RemoveLeadsResponseDto": {
        "type": "object",
        "properties": {
          "listId": {
            "type": "string",
            "description": "The list the leads were removed from"
          },
          "deletedCount": {
            "type": "number",
            "description": "Number of leads removed"
          }
        },
        "required": ["listId", "deletedCount"]
      },
      "ListDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the list"
          },
          "name": {
            "type": "string",
            "description": "The name of the list",
            "example": "Q4 prospects"
          },
          "type": {
            "type": "string",
            "description": "How the list was created",
            "example": "API_IMPORT"
          },
          "isBlacklist": {
            "type": "boolean",
            "description": "Whether this list is a blacklist (suppression list)",
            "example": false
          },
          "leadCount": {
            "type": "number",
            "description": "Number of leads in the list",
            "example": 42
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the list was created"
          }
        },
        "required": ["id", "name", "type", "isBlacklist", "leadCount", "createdAt"]
      },
      "PaginatedListsDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListDto"
            }
          },
          "nextCursor": {
            "type": "string",
            "description": "Opaque cursor for the next page; null when there are no more results. Pass it back as the `cursor` query parameter.",
            "nullable": true
          }
        },
        "required": ["data", "nextCursor"]
      },
      "ListLeadDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the lead"
          },
          "name": {
            "type": "string",
            "description": "The name of the lead",
            "example": "Jane Doe"
          },
          "linkedinProfile": {
            "type": "string",
            "description": "The LinkedIn profile URL of the lead",
            "example": "https://www.linkedin.com/in/jane-doe"
          },
          "company": {
            "type": "string",
            "description": "The lead's company",
            "nullable": true
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the lead was added"
          }
        },
        "required": ["id", "name", "linkedinProfile", "createdAt"]
      },
      "PaginatedListLeadsDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListLeadDto"
            }
          },
          "nextCursor": {
            "type": "string",
            "description": "Opaque cursor for the next page; null when there are no more results. Pass it back as the `cursor` query parameter.",
            "nullable": true
          }
        },
        "required": ["data", "nextCursor"]
      },
      "CreateBlacklistDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The name of the blacklist",
            "example": "Do not contact"
          }
        },
        "required": ["name"]
      },
      "AccountDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "number",
            "description": "The internal identifier of the connected account"
          },
          "unipileId": {
            "type": "string",
            "description": "The provider (Unipile) account identifier"
          },
          "status": {
            "type": "string",
            "description": "The account status",
            "example": "UNIPILE_OK"
          },
          "unipileStatus": {
            "type": "string",
            "description": "Detailed provider connection status / health",
            "nullable": true
          },
          "lastUnipileStatusUpdate": {
            "format": "date-time",
            "type": "string",
            "description": "When the connection status was last updated",
            "nullable": true
          },
          "ownerId": {
            "type": "string",
            "description": "The ID of the user who owns this account"
          },
          "ownerEmail": {
            "type": "string",
            "description": "The email of the user who owns this account"
          }
        },
        "required": ["id", "unipileId", "status", "ownerId", "ownerEmail"]
      },
      "LimitBucketDto": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "number",
            "description": "The configured daily cap",
            "example": 15
          },
          "usedToday": {
            "type": "number",
            "description": "How many have been used so far today",
            "example": 4
          },
          "remaining": {
            "type": "number",
            "description": "Remaining quota for today (never negative)",
            "example": 11
          }
        },
        "required": ["limit", "usedToday", "remaining"]
      },
      "AccountLimitsBreakdownDto": {
        "type": "object",
        "properties": {
          "invitations": {
            "$ref": "#/components/schemas/LimitBucketDto"
          },
          "messages": {
            "$ref": "#/components/schemas/LimitBucketDto"
          },
          "profileVisits": {
            "$ref": "#/components/schemas/LimitBucketDto"
          },
          "inmails": {
            "$ref": "#/components/schemas/LimitBucketDto"
          },
          "companyPageInvitations": {
            "$ref": "#/components/schemas/LimitBucketDto"
          },
          "reactToPosts": {
            "$ref": "#/components/schemas/LimitBucketDto"
          }
        },
        "required": ["invitations", "messages", "profileVisits", "inmails", "companyPageInvitations", "reactToPosts"]
      },
      "AccountLimitsDto": {
        "type": "object",
        "properties": {
          "accountId": {
            "type": "number",
            "description": "The account (seat) identifier"
          },
          "ownerId": {
            "type": "string",
            "description": "The ID of the user who owns this account"
          },
          "timezone": {
            "type": "string",
            "description": "The timezone used to determine the daily reset boundary",
            "example": "America/New_York"
          },
          "date": {
            "type": "string",
            "description": "The date (in the account timezone) the usage counts apply to",
            "example": "2026-06-18"
          },
          "isCurrentlyThrottled": {
            "type": "boolean",
            "description": "Whether the account is currently throttled by LinkedIn",
            "example": false
          },
          "activeDaysOfWeek": {
            "description": "Days of the week on which sending is active",
            "example": ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "inMailCredit": {
            "type": "number",
            "description": "Remaining InMail credits",
            "example": 0
          },
          "searchesPerDay": {
            "type": "number",
            "description": "Daily search cap (no per-day usage counter is tracked)",
            "example": 80
          },
          "limits": {
            "$ref": "#/components/schemas/AccountLimitsBreakdownDto"
          }
        },
        "required": [
          "accountId",
          "ownerId",
          "timezone",
          "date",
          "isCurrentlyThrottled",
          "activeDaysOfWeek",
          "inMailCredit",
          "searchesPerDay",
          "limits"
        ]
      },
      "UpdateAccountLimitsDto": {
        "type": "object",
        "properties": {
          "maxDailyInvites": {
            "type": "number",
            "description": "Daily connection-invite cap",
            "minimum": 0
          },
          "maxDailyMessages": {
            "type": "number",
            "description": "Daily message cap",
            "minimum": 0
          },
          "maxDailyInmails": {
            "type": "number",
            "description": "Daily InMail cap",
            "minimum": 0
          },
          "maxDailyProfileVisits": {
            "type": "number",
            "description": "Daily profile-visit cap",
            "minimum": 0
          },
          "maxDailyCompanyPageInvitations": {
            "type": "number",
            "description": "Daily company-page-invitation cap",
            "minimum": 0
          },
          "maxDailyReactToPosts": {
            "type": "number",
            "description": "Daily post-reaction cap",
            "minimum": 0
          },
          "enableAutoWithdrawal": {
            "type": "boolean",
            "description": "Whether to auto-withdraw pending invites"
          },
          "inviteWithdrawalDuration": {
            "type": "number",
            "description": "Days after which a pending invite is auto-withdrawn (1-91)",
            "minimum": 1,
            "maximum": 91
          },
          "activeDaysOfWeek": {
            "type": "array",
            "description": "Days of the week on which sending is active",
            "items": {
              "type": "string",
              "enum": ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"]
            }
          },
          "timezone": {
            "type": "string",
            "description": "IANA timezone used for the daily reset and sending window",
            "example": "America/New_York"
          }
        }
      },
      "CreateWebhookDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "A human-readable name for the webhook"
          },
          "endpoint": {
            "type": "string",
            "description": "The URL that will receive event notifications (POST)",
            "example": "https://example.com/hooks/botdog"
          },
          "eventTypes": {
            "type": "array",
            "description": "The event types to subscribe to",
            "items": {
              "type": "string",
              "enum": [
                "CAMPAIGN_STARTED",
                "CAMPAIGN_COMPLETED",
                "LEAD_PROFILE_VISITED",
                "LEAD_INVITATION_SENT",
                "LEAD_INVITATION_ACCEPTED",
                "LEAD_MESSAGE_SENT",
                "LEAD_MESSAGE_REPLIED",
                "LEAD_INVITATION_WITHDRAWN",
                "LEAD_SKIPPED",
                "ACCOUNT_DISCONNECTED",
                "DAILY_LIMIT_REACHED"
              ]
            }
          },
          "campaignIds": {
            "description": "Restrict delivery to specific campaign IDs (default: all)",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": ["name", "endpoint", "eventTypes"]
      },
      "WebhookDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the webhook"
          },
          "name": {
            "type": "string",
            "description": "A human-readable name for the webhook"
          },
          "endpoint": {
            "type": "string",
            "description": "The URL that receives event notifications",
            "example": "https://example.com/hooks/botdog"
          },
          "status": {
            "type": "string",
            "description": "The webhook status",
            "example": "ACTIVE"
          },
          "scope": {
            "type": "string",
            "description": "Delivery scope: ALL campaigns or SPECIFIC_CAMPAIGNS",
            "example": "ALL"
          },
          "type": {
            "type": "string",
            "description": "The webhook type",
            "example": "GENERIC"
          },
          "isTeamWebhook": {
            "type": "boolean",
            "description": "Whether the webhook is shared across the team",
            "example": false
          },
          "eventTypes": {
            "description": "Event types this webhook is subscribed to",
            "example": ["LEAD_INVITATION_ACCEPTED", "LEAD_MESSAGE_REPLIED"],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "campaignIds": {
            "description": "Campaign IDs this webhook is scoped to (empty when scope is ALL)",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the webhook was created"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the webhook was last updated"
          }
        },
        "required": [
          "id",
          "name",
          "endpoint",
          "status",
          "scope",
          "type",
          "isTeamWebhook",
          "eventTypes",
          "campaignIds",
          "createdAt",
          "updatedAt"
        ]
      },
      "UpdateWebhookDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "A human-readable name for the webhook"
          },
          "endpoint": {
            "type": "string",
            "description": "The URL that receives notifications"
          },
          "eventTypes": {
            "type": "array",
            "description": "Replace the subscribed event types",
            "items": {
              "type": "string",
              "enum": [
                "CAMPAIGN_STARTED",
                "CAMPAIGN_COMPLETED",
                "LEAD_PROFILE_VISITED",
                "LEAD_INVITATION_SENT",
                "LEAD_INVITATION_ACCEPTED",
                "LEAD_MESSAGE_SENT",
                "LEAD_MESSAGE_REPLIED",
                "LEAD_INVITATION_WITHDRAWN",
                "LEAD_SKIPPED",
                "ACCOUNT_DISCONNECTED",
                "DAILY_LIMIT_REACHED"
              ]
            }
          }
        }
      },
      "SnippetDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The unique identifier of the snippet"
          },
          "name": {
            "type": "string",
            "description": "The name of the snippet"
          },
          "shortcut": {
            "type": "string",
            "description": "The keyboard shortcut for the snippet, if any",
            "nullable": true
          },
          "type": {
            "type": "string",
            "description": "The snippet type: a static TEXT template or an LLM prompt",
            "example": "TEXT"
          },
          "status": {
            "type": "string",
            "description": "The snippet status",
            "example": "ACTIVE"
          },
          "content": {
            "type": "string",
            "description": "The template body (for TEXT snippets)",
            "nullable": true
          },
          "prompt": {
            "type": "string",
            "description": "The generation prompt (for LLM snippets)",
            "nullable": true
          },
          "isShared": {
            "type": "boolean",
            "description": "Whether the snippet is shared with the team",
            "example": false
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the snippet was created"
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "When the snippet was last updated"
          }
        },
        "required": ["id", "name", "type", "status", "isShared", "createdAt", "updatedAt"]
      },
      "PaginatedSnippetsDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SnippetDto"
            }
          },
          "nextCursor": {
            "type": "string",
            "description": "Opaque cursor for the next page; null when there are no more results. Pass it back as the `cursor` query parameter.",
            "nullable": true
          }
        },
        "required": ["data", "nextCursor"]
      },
      "CreateSnippetDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The snippet name"
          },
          "type": {
            "type": "string",
            "description": "A static TEXT template or an LLM prompt",
            "enum": ["TEXT", "LLM"],
            "default": "TEXT"
          },
          "content": {
            "type": "string",
            "description": "Template body (required for TEXT)"
          },
          "prompt": {
            "type": "string",
            "description": "Generation prompt (required for LLM)"
          },
          "shortcut": {
            "type": "string",
            "description": "A quick-access shortcut (must be unique)"
          }
        },
        "required": ["name"]
      },
      "UpdateSnippetDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "The snippet name"
          },
          "content": {
            "type": "string",
            "description": "Template body (for TEXT snippets)"
          },
          "prompt": {
            "type": "string",
            "description": "Generation prompt (for LLM snippets)"
          },
          "shortcut": {
            "type": "string",
            "description": "A quick-access shortcut (must be unique)"
          },
          "status": {
            "type": "string",
            "description": "Set ACTIVE or ARCHIVED",
            "enum": ["ACTIVE", "ARCHIVED"]
          }
        }
      },
      "SendMessageDto": {
        "type": "object",
        "properties": {
          "leadId": {
            "type": "string",
            "description": "The lead to message (must already have a conversation)"
          },
          "text": {
            "type": "string",
            "description": "The message text",
            "maxLength": 8000
          }
        },
        "required": ["leadId", "text"]
      },
      "SendMessageResponseDto": {
        "type": "object",
        "properties": {
          "leadId": {
            "type": "string",
            "description": "The lead that was messaged"
          },
          "chatId": {
            "type": "string",
            "description": "The conversation the message was sent to. Use it with POST /v1/conversations/{id}/reply."
          },
          "messageId": {
            "type": "string",
            "description": "The provider message id, if returned",
            "nullable": true
          }
        },
        "required": ["chatId", "messageId"]
      },
      "ReplyDto": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "description": "The reply text",
            "maxLength": 8000
          }
        },
        "required": ["text"]
      },
      "ScheduleMessageDto": {
        "type": "object",
        "properties": {
          "leadId": {
            "type": "string",
            "description": "The lead to message (must already have a conversation)"
          },
          "text": {
            "type": "string",
            "description": "The message text",
            "maxLength": 8000
          },
          "fireAt": {
            "type": "string",
            "description": "When to send (ISO 8601). Must be 60s\u201390 days in the future. Counts against the daily per-account send budget.",
            "example": "2026-06-19T09:00:00Z"
          }
        },
        "required": ["leadId", "text", "fireAt"]
      },
      "ScheduledMessageResponseDto": {
        "type": "object",
        "properties": {
          "scheduledMessageId": {
            "type": "string",
            "description": "The scheduled message id"
          },
          "chatId": {
            "type": "string",
            "description": "The conversation it will be sent to"
          },
          "fireAt": {
            "format": "date-time",
            "type": "string",
            "description": "When it will fire"
          },
          "status": {
            "type": "string",
            "description": "The status",
            "example": "PENDING"
          }
        },
        "required": ["scheduledMessageId", "chatId", "fireAt", "status"]
      },
      "ReadStatusDto": {
        "type": "object",
        "properties": {
          "read": {
            "type": "boolean",
            "description": "true to mark read, false to mark unread",
            "default": true
          }
        },
        "required": ["read"]
      },
      "ReadStatusResponseDto": {
        "type": "object",
        "properties": {
          "chatId": {
            "type": "string",
            "description": "The conversation"
          },
          "read": {
            "type": "boolean",
            "description": "The resulting read state"
          }
        },
        "required": ["chatId", "read"]
      }
    }
  }
}
