{
  "openapi": "3.1.0",
  "info": {
    "title": "ZenMode API",
    "version": "1.0.0",
    "summary": "Read and steer ZenMode LinkedIn outreach.",
    "description": "ZenMode runs LinkedIn outreach through its desktop app. This API reads the resulting funnel and queues actions for that desktop app to perform.\n\n**When to use it.** Pull leads and reply activity into a CRM, watch the funnel, suppress contacts your team should not touch, or queue connection requests from your own system. Check `GET /api/v1/status` first: tasks queued while the desktop app is offline stay pending until it next runs.\n\n**Authentication.** Every endpoint takes `Authorization: Bearer <api-key>`. Keys are created in the dashboard on the API page (/dashboard/api). API access requires an active paid plan; a valid key on an ineligible plan returns 403.\n\n**Rate limits.** 100 requests per minute per key. Queueing tasks is additionally bounded by the account's daily LinkedIn action cap, which returns 429 with `Retry-After: 3600`.",
    "contact": {
      "name": "ZenMode support",
      "url": "https://www.zen-mode.io/contact"
    },
    "termsOfService": "https://www.zen-mode.io/terms"
  },
  "servers": [
    {
      "url": "https://www.zen-mode.io",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Leads",
      "description": "Contacts in your campaigns and their message history."
    },
    {
      "name": "Analytics",
      "description": "Funnel counts and daily series."
    },
    {
      "name": "Status",
      "description": "Whether the desktop app is running."
    },
    {
      "name": "Suppressions",
      "description": "Account-wide exclusions."
    },
    {
      "name": "Tasks",
      "description": "Actions queued for the desktop app."
    },
    {
      "name": "Webhooks",
      "description": "Outbound event delivery."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key created in the dashboard on the API page (/dashboard/api)."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable description of what went wrong."
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable identifier. Absent on some older handlers."
          },
          "hint": {
            "type": "string",
            "description": "What to change to make the request succeed."
          }
        },
        "required": [
          "error"
        ]
      },
      "Lead": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "campaign_id": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "company": {
            "type": "string",
            "nullable": true
          },
          "location": {
            "type": "string",
            "nullable": true
          },
          "linkedin_url": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "warming_up",
              "sent",
              "connected",
              "replied",
              "meeting_booked",
              "not_interested",
              "failed",
              "no_response",
              "stale_connection",
              "disconnected",
              "connection_removed",
              "messaging_closed",
              "calendar_clicked",
              "invitation_withdrawn"
            ]
          },
          "follow_up_number": {
            "type": "integer"
          },
          "calendar_clicked": {
            "type": "boolean"
          },
          "connection_sent_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "connection_accepted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "replied_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "Task": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^task_",
            "description": "Task id, e.g. `task_mb3k2x1c_q8z0v7ke`."
          },
          "user_id": {
            "type": "string"
          },
          "linkedin_account_id": {
            "type": "string",
            "nullable": true,
            "description": "Your own label for the LinkedIn account, stored and returned as-is. It is not a ZenMode account id and does not choose which account runs the task."
          },
          "action_type": {
            "type": "string",
            "enum": [
              "connect",
              "message",
              "follow",
              "view_profile",
              "withdraw_connection"
            ]
          },
          "payload": {
            "type": "object",
            "additionalProperties": true
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "completed",
              "failed",
              "cancelled"
            ]
          },
          "result": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "What the desktop app reported when it ran the task. On a free or unconfirmed LinkedIn plan the connection request is sent without a note, and the task result carries `note_dropped: true` with `note_dropped_reason: \"free_or_unconfirmed_plan\"`. `note_dropped` is `false` when the note went out or none was requested, and absent on results from desktop versions that do not report it.",
            "properties": {
              "note_dropped": {
                "type": "boolean",
                "description": "`connect` tasks only. `true` when the connection request was sent without the note in `payload.message` because the LinkedIn plan is free or not confirmed as paid. `false` when the note went out or no note was requested. Absent when the desktop app that ran the task does not report it."
              },
              "note_sent": {
                "type": "boolean",
                "description": "`connect` tasks only. `true` only when the note was typed into LinkedIn's note box and the request went out with it. `false` when the request went out without a note, for any reason, including a dropped note (`note_dropped: true`) and a task with no `payload.message`. Absent when the desktop app that ran the task does not report it."
              },
              "note_dropped_reason": {
                "type": "string",
                "enum": [
                  "free_or_unconfirmed_plan"
                ],
                "description": "Present only when `note_dropped` is `true`. `free_or_unconfirmed_plan`: the account is on a free LinkedIn plan, or ZenMode has not confirmed a paid plan (Premium or Sales Navigator)."
              }
            }
          },
          "priority": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "retry_count": {
            "type": "integer"
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "is_active": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "WebhookCreated": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "is_active": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "secret": {
            "type": "string",
            "pattern": "^whsec_",
            "description": "Signing secret. Verify each delivery by computing HMAC-SHA256 of the raw body with this secret and comparing its hex digest to `X-ZenMode-Signature` in constant time. Returned only here; webhooks registered in the dashboard can reveal theirs there."
          }
        },
        "required": [
          "id",
          "url",
          "events",
          "secret"
        ]
      }
    }
  },
  "paths": {
    "/api/v1/analytics": {
      "get": {
        "operationId": "getAnalytics",
        "summary": "Outreach funnel and daily series",
        "description": "Aggregate counts for the authenticated account, optionally narrowed to one campaign. `reply_rate_pct` is computed over accepted connections, not over sent.",
        "tags": [
          "Analytics"
        ],
        "responses": {
          "200": {
            "description": "Funnel and series.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "window": {
                      "oneOf": [
                        {
                          "type": "string",
                          "enum": [
                            "all"
                          ]
                        },
                        {
                          "type": "object",
                          "properties": {
                            "days": {
                              "type": "integer"
                            }
                          }
                        }
                      ]
                    },
                    "campaign_id": {
                      "type": "integer",
                      "nullable": true
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "total_contacts": {
                          "type": "integer"
                        },
                        "sent": {
                          "type": "integer"
                        },
                        "accepted": {
                          "type": "integer"
                        },
                        "replied": {
                          "type": "integer"
                        },
                        "meetings_booked": {
                          "type": "integer"
                        },
                        "acceptance_rate_pct": {
                          "type": "number"
                        },
                        "reply_rate_pct": {
                          "type": "number"
                        },
                        "reply_rate_denominator": {
                          "type": "string",
                          "enum": [
                            "accepted"
                          ]
                        }
                      }
                    },
                    "funnel": {
                      "type": "object",
                      "properties": {
                        "sent": {
                          "type": "integer"
                        },
                        "accepted": {
                          "type": "integer"
                        },
                        "replied": {
                          "type": "integer"
                        },
                        "meetings_booked": {
                          "type": "integer"
                        }
                      }
                    },
                    "daily": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string"
                          },
                          "connections_sent": {
                            "type": "integer"
                          },
                          "connections_accepted": {
                            "type": "integer"
                          },
                          "messages_sent": {
                            "type": "integer"
                          },
                          "replies_received": {
                            "type": "integer"
                          },
                          "meetings_booked": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "campaigns": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "name": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "total_contacts": {
                            "type": "integer"
                          },
                          "sent_count": {
                            "type": "integer"
                          },
                          "connected_count": {
                            "type": "integer"
                          },
                          "replied_count": {
                            "type": "integer"
                          },
                          "meeting_count": {
                            "type": "integer"
                          }
                        }
                      },
                      "description": "Present only when campaign_id is omitted."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid `days` or `campaign_id`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "campaign_id not found for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "Window length in days, or the literal `all` for lifetime.",
            "schema": {
              "oneOf": [
                {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 365
                },
                {
                  "type": "string",
                  "enum": [
                    "all"
                  ]
                }
              ],
              "default": 30
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "required": false,
            "description": "Restrict to one campaign. Ownership is enforced; a campaign you do not own returns 404.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ]
      }
    },
    "/api/v1/analytics/outreach-volume": {
      "get": {
        "operationId": "getOutreachVolume",
        "summary": "Connection requests and follow-ups sent, by step",
        "description": "How many connection requests and 1st, 2nd and 3rd follow-ups were SENT in a date range, per campaign and in total. Days are UTC. Follow-ups sent before `recorded_from` are reconstructed from message history and counted in `reconstructed`.",
        "tags": [
          "Analytics"
        ],
        "responses": {
          "200": {
            "description": "Counts per campaign and in total.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "from": {
                      "type": "string"
                    },
                    "to": {
                      "type": "string"
                    },
                    "time_zone": {
                      "type": "string",
                      "enum": [
                        "UTC"
                      ]
                    },
                    "campaign_id": {
                      "type": "integer",
                      "nullable": true
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "connection_requests": {
                          "type": "integer"
                        },
                        "follow_up_1": {
                          "type": "integer"
                        },
                        "follow_up_2": {
                          "type": "integer"
                        },
                        "follow_up_3": {
                          "type": "integer"
                        },
                        "follow_ups_total": {
                          "type": "integer"
                        },
                        "reconstructed": {
                          "type": "integer"
                        }
                      }
                    },
                    "campaigns": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "campaign_id": {
                            "type": "integer"
                          },
                          "campaign_name": {
                            "type": "string"
                          },
                          "campaign_type": {
                            "type": "string",
                            "nullable": true
                          },
                          "connection_requests": {
                            "type": "integer"
                          },
                          "follow_up_1": {
                            "type": "integer"
                          },
                          "follow_up_2": {
                            "type": "integer"
                          },
                          "follow_up_3": {
                            "type": "integer"
                          },
                          "follow_ups_total": {
                            "type": "integer"
                          },
                          "reconstructed": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "recorded_from": {
                      "type": "string",
                      "format": "date",
                      "nullable": true,
                      "description": "First UTC day on which every follow-up count is recorded at send time rather than reconstructed."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid `from`/`to` or `campaign_id`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "campaign_id not found for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "First day, YYYY-MM-DD (UTC), inclusive. Pass with `to`; omit both for the last 7 days.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Last day, YYYY-MM-DD (UTC), inclusive. At most 366 days after `from`.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "required": false,
            "description": "Restrict to one campaign. Ownership is enforced; a campaign you do not own returns 404.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ]
      }
    },
    "/api/v1/status": {
      "get": {
        "operationId": "getStatus",
        "summary": "Desktop app and LinkedIn account state",
        "description": "Whether the desktop app is up, and per-account pacing counters. The desktop reports about once an hour, so `desktop_active` is true when it last reported within 75 minutes; `desktop_state` separates a desktop that went quiet from one never seen. Active means the app was open at `last_seen`, not that a campaign is running: read `accounts[].is_running` for that. A desktop that closed a minute after reporting still reads active for up to 75 minutes.",
        "tags": [
          "Status"
        ],
        "responses": {
          "200": {
            "description": "Current state. When `desktop_state` is `went_quiet` or `never_seen`, `accounts` is `[]` and `phase`/`linkedin_status` are absent; `last_seen` is null only for `never_seen`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "desktop_active": {
                      "type": "boolean"
                    },
                    "desktop_state": {
                      "type": "string",
                      "enum": [
                        "active",
                        "went_quiet",
                        "never_seen"
                      ]
                    },
                    "last_seen": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "phase": {
                      "type": "string",
                      "enum": [
                        "idle",
                        "starting",
                        "scraping",
                        "connecting",
                        "checking_replies",
                        "monitoring",
                        "error"
                      ],
                      "nullable": true
                    },
                    "linkedin_status": {
                      "type": "string",
                      "nullable": true
                    },
                    "active_campaign_count": {
                      "type": "integer"
                    },
                    "connections_sent_today": {
                      "type": "integer"
                    },
                    "accounts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "display_name": {
                            "type": "string"
                          },
                          "is_running": {
                            "type": "boolean"
                          },
                          "phase": {
                            "type": "string",
                            "nullable": true
                          },
                          "campaign_id": {
                            "type": "integer",
                            "nullable": true
                          },
                          "daily_actions": {
                            "type": "integer"
                          },
                          "daily_limit": {
                            "type": "integer"
                          },
                          "weekly_actions": {
                            "type": "integer"
                          },
                          "weekly_limit": {
                            "type": "integer"
                          },
                          "linkedin_status": {
                            "type": "string",
                            "nullable": true
                          },
                          "started_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "last_action_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/leads": {
      "get": {
        "operationId": "listLeads",
        "summary": "List leads",
        "description": "Contacts across all campaigns, newest first.",
        "tags": [
          "Leads"
        ],
        "responses": {
          "200": {
            "description": "A page of leads.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "leads": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Lead"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "limit": {
                          "type": "integer"
                        },
                        "offset": {
                          "type": "integer"
                        },
                        "returned": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid status, timestamp or campaign_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "campaign_id not found for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Rows to return. Clamped to 500.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by lifecycle status.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "warming_up",
                "sent",
                "connected",
                "replied",
                "meeting_booked",
                "not_interested",
                "failed",
                "no_response",
                "stale_connection",
                "disconnected",
                "connection_removed",
                "messaging_closed",
                "calendar_clicked",
                "invitation_withdrawn"
              ]
            }
          },
          {
            "name": "updated_since",
            "in": "query",
            "required": false,
            "description": "ISO 8601. Only leads changed at or after this instant.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "required": false,
            "description": "Restrict to one campaign you own.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ]
      },
      "post": {
        "operationId": "createLead",
        "summary": "Add a lead to a campaign",
        "description": "The URL must be a linkedin.com profile (`/in/<slug>`) or Sales Navigator lead (`/sales/lead/<id>`); one of those identities must be extractable or the request is refused. Suppressed contacts, people the campaign's own exclusion list matches, and campaigns whose messages come from CSV columns are rejected with 409 rather than silently skipped. The same rules decide every item of POST /api/v1/leads/bulk.",
        "tags": [
          "Leads"
        ],
        "responses": {
          "201": {
            "description": "Created. `delivery` says whether anything the server can see will work this lead, at the moment of the check. It is never a promise to send: the best state is `queued_running`, and even then the next pickup can be up to two hours away and `reasons` can still block it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "lead": {
                      "$ref": "#/components/schemas/Lead"
                    },
                    "identity": {
                      "type": "object",
                      "properties": {
                        "linkedin_slug": {
                          "type": "string",
                          "nullable": true
                        },
                        "sales_lead_id": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "delivery": {
                      "type": "object",
                      "properties": {
                        "state": {
                          "type": "string",
                          "enum": [
                            "paused_by_this_push",
                            "campaign_not_active",
                            "awaiting_approval",
                            "unknown",
                            "no_desktop_seen",
                            "desktop_idle",
                            "queued_running"
                          ]
                        },
                        "reasons": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "approval_reset_by_this_push",
                              "account_disabled",
                              "account_cooldown",
                              "warmup_first",
                              "stop_date_passed",
                              "daily_cap_reached",
                              "location_filter_applies"
                            ]
                          }
                        },
                        "campaign": {
                          "type": "object",
                          "properties": {
                            "status": {
                              "type": "string",
                              "nullable": true
                            },
                            "approval_state": {
                              "type": "string",
                              "nullable": true
                            },
                            "linkedin_account_id": {
                              "type": "integer",
                              "nullable": true
                            }
                          }
                        },
                        "desktop": {
                          "type": "object",
                          "properties": {
                            "state": {
                              "type": "string",
                              "enum": [
                                "active",
                                "went_quiet",
                                "never_seen"
                              ],
                              "nullable": true
                            },
                            "last_seen_at": {
                              "type": "string",
                              "format": "date-time",
                              "nullable": true
                            },
                            "age_seconds": {
                              "type": "integer",
                              "nullable": true
                            },
                            "app_version": {
                              "type": "string",
                              "nullable": true
                            },
                            "account_running": {
                              "type": "boolean",
                              "nullable": true
                            },
                            "running_campaign_id": {
                              "type": "integer",
                              "nullable": true
                            },
                            "works_all_active_campaigns_on_account": {
                              "type": "boolean",
                              "nullable": true
                            }
                          }
                        },
                        "checked_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, or `invalid_campaign_id` / `invalid_url` / `no_identity` / `inmail_requires_sn_url` (an InMail campaign accepts only Sales Navigator lead URLs, `/sales/lead/ACwAA...`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Campaign not found for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`suppressed`, `campaign_exclusion` (carries `matched_on`: `company`, `url` or `title`), `csv_message_campaign` (the campaign's messages come from CSV columns and the lead carries no `messages`), a `messages` refusal (`ai_sequence_campaign`, `unsupported_campaign_type`), or `already_exists`. A lead removed by hand from a different campaign does not block; any other existing row does. The already_exists body usually carries `lead` and `scope`, but the insert-race loser carries neither — treat both as optional. If this request's approval check paused or reset the campaign before losing the race, the 409 also carries `delivery`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "hint": {
                      "type": "string"
                    },
                    "lead": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "campaign_id": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string"
                        },
                        "removed": {
                          "type": "boolean"
                        }
                      }
                    },
                    "scope": {
                      "type": "string",
                      "enum": [
                        "all_campaigns",
                        "this_campaign"
                      ]
                    },
                    "matched_on": {
                      "type": "string",
                      "enum": [
                        "company",
                        "url",
                        "title"
                      ]
                    },
                    "linkedin_url": {
                      "type": "string"
                    },
                    "identity": {
                      "type": "object",
                      "properties": {
                        "linkedin_slug": {
                          "type": "string",
                          "nullable": true
                        },
                        "sales_lead_id": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "delivery": {
                      "type": "object",
                      "properties": {
                        "state": {
                          "type": "string",
                          "enum": [
                            "paused_by_this_push",
                            "campaign_not_active",
                            "awaiting_approval",
                            "unknown",
                            "no_desktop_seen",
                            "desktop_idle",
                            "queued_running"
                          ]
                        },
                        "reasons": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "approval_reset_by_this_push",
                              "account_disabled",
                              "account_cooldown",
                              "warmup_first",
                              "stop_date_passed",
                              "daily_cap_reached",
                              "location_filter_applies"
                            ]
                          }
                        },
                        "campaign": {
                          "type": "object",
                          "properties": {
                            "status": {
                              "type": "string",
                              "nullable": true
                            },
                            "approval_state": {
                              "type": "string",
                              "nullable": true
                            },
                            "linkedin_account_id": {
                              "type": "integer",
                              "nullable": true
                            }
                          }
                        },
                        "desktop": {
                          "type": "object",
                          "properties": {
                            "state": {
                              "type": "string",
                              "enum": [
                                "active",
                                "went_quiet",
                                "never_seen"
                              ],
                              "nullable": true
                            },
                            "last_seen_at": {
                              "type": "string",
                              "format": "date-time",
                              "nullable": true
                            },
                            "age_seconds": {
                              "type": "integer",
                              "nullable": true
                            },
                            "app_version": {
                              "type": "string",
                              "nullable": true
                            },
                            "account_running": {
                              "type": "boolean",
                              "nullable": true
                            },
                            "running_campaign_id": {
                              "type": "integer",
                              "nullable": true
                            },
                            "works_all_active_campaigns_on_account": {
                              "type": "boolean",
                              "nullable": true
                            }
                          }
                        },
                        "checked_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "`messages` failed a check (see PUT /api/v1/leads/{id}/messages); nothing was added.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "hint": {
                      "type": "string"
                    },
                    "message_key": {
                      "type": "string"
                    },
                    "missing_variables": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "length": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`approval_check_failed`: the campaign's approval state could not be confirmed, so nothing was added. Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "Only `campaign_id` and `linkedin_url` are required; the rest are stored verbatim and default to null. `messages` is this lead's own text, checked exactly as PUT /api/v1/leads/{id}/messages checks it, before anything is written; a CSV-message campaign accepts a lead only with it.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "campaign_id": {
                    "type": "integer"
                  },
                  "linkedin_url": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string",
                    "nullable": true
                  },
                  "title": {
                    "type": "string",
                    "nullable": true
                  },
                  "company": {
                    "type": "string",
                    "nullable": true
                  },
                  "location": {
                    "type": "string",
                    "nullable": true
                  },
                  "messages": {
                    "type": "object",
                    "properties": {
                      "connection_request": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 8000
                      },
                      "follow_up_1": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 8000
                      },
                      "follow_up_2": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 8000
                      },
                      "follow_up_3": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 8000
                      }
                    }
                  }
                },
                "required": [
                  "campaign_id",
                  "linkedin_url"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/leads/bulk": {
      "post": {
        "operationId": "createLeadsBulk",
        "summary": "Add up to 500 leads to a campaign",
        "description": "One campaign, up to 500 items, one result per item in input order. Each item is judged exactly as POST /api/v1/leads would judge it on its own, plus `duplicate_in_request` for a person who appears earlier in the same request. The request is 200 whenever it is valid, even if every item was refused. A retry is safe: an item that already landed comes back `duplicate`. The approval check runs once for the whole request and only if at least one item is about to be added. Limits: 100 requests/minute per key (shared with every v1 route) plus 2,000 items/minute per key; both are per server instance.",
        "tags": [
          "Leads"
        ],
        "responses": {
          "200": {
            "description": "Processed. `summary` counts outcomes; `results[i].http_status` is what POST /api/v1/leads would have returned for that item alone; `delivery` describes the campaign once for the whole request.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "campaign_id": {
                      "type": "integer"
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "received": {
                          "type": "integer"
                        },
                        "created": {
                          "type": "integer"
                        },
                        "duplicate": {
                          "type": "integer"
                        },
                        "duplicate_in_request": {
                          "type": "integer"
                        },
                        "suppressed": {
                          "type": "integer"
                        },
                        "excluded": {
                          "type": "integer"
                        },
                        "refused": {
                          "type": "integer"
                        },
                        "invalid": {
                          "type": "integer"
                        }
                      }
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "index": {
                            "type": "integer"
                          },
                          "outcome": {
                            "type": "string",
                            "enum": [
                              "created",
                              "duplicate",
                              "duplicate_in_request",
                              "suppressed",
                              "excluded",
                              "refused",
                              "invalid"
                            ]
                          },
                          "http_status": {
                            "type": "integer"
                          },
                          "code": {
                            "type": "string"
                          },
                          "error": {
                            "type": "string"
                          },
                          "hint": {
                            "type": "string"
                          },
                          "lead": {
                            "oneOf": [
                              {
                                "$ref": "#/components/schemas/Lead"
                              },
                              {
                                "type": "object",
                                "properties": {
                                  "id": {
                                    "type": "integer"
                                  },
                                  "campaign_id": {
                                    "type": "integer"
                                  },
                                  "status": {
                                    "type": "string"
                                  },
                                  "removed": {
                                    "type": "boolean"
                                  }
                                }
                              }
                            ],
                            "nullable": true
                          },
                          "identity": {
                            "type": "object",
                            "properties": {
                              "linkedin_slug": {
                                "type": "string",
                                "nullable": true
                              },
                              "sales_lead_id": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          },
                          "scope": {
                            "type": "string",
                            "enum": [
                              "all_campaigns",
                              "this_campaign"
                            ],
                            "nullable": true
                          },
                          "matched_on": {
                            "type": "string",
                            "enum": [
                              "company",
                              "url",
                              "title"
                            ]
                          },
                          "linkedin_url": {
                            "type": "string"
                          },
                          "first_index": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "index",
                          "outcome",
                          "http_status"
                        ]
                      }
                    },
                    "delivery": {
                      "type": "object",
                      "properties": {
                        "state": {
                          "type": "string",
                          "enum": [
                            "paused_by_this_push",
                            "campaign_not_active",
                            "awaiting_approval",
                            "unknown",
                            "no_desktop_seen",
                            "desktop_idle",
                            "queued_running"
                          ]
                        },
                        "reasons": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "approval_reset_by_this_push",
                              "account_disabled",
                              "account_cooldown",
                              "warmup_first",
                              "stop_date_passed",
                              "daily_cap_reached",
                              "location_filter_applies"
                            ]
                          }
                        },
                        "campaign": {
                          "type": "object",
                          "properties": {
                            "status": {
                              "type": "string",
                              "nullable": true
                            },
                            "approval_state": {
                              "type": "string",
                              "nullable": true
                            },
                            "linkedin_account_id": {
                              "type": "integer",
                              "nullable": true
                            }
                          }
                        },
                        "desktop": {
                          "type": "object",
                          "properties": {
                            "state": {
                              "type": "string",
                              "enum": [
                                "active",
                                "went_quiet",
                                "never_seen"
                              ],
                              "nullable": true
                            },
                            "last_seen_at": {
                              "type": "string",
                              "format": "date-time",
                              "nullable": true
                            },
                            "age_seconds": {
                              "type": "integer",
                              "nullable": true
                            },
                            "app_version": {
                              "type": "string",
                              "nullable": true
                            },
                            "account_running": {
                              "type": "boolean",
                              "nullable": true
                            },
                            "running_campaign_id": {
                              "type": "integer",
                              "nullable": true
                            },
                            "works_all_active_campaigns_on_account": {
                              "type": "boolean",
                              "nullable": true
                            }
                          }
                        },
                        "checked_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, `invalid_campaign_id`, `items_required` (missing or empty) or `too_many_items` (more than 500).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Campaign not found for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "`rate_limit_exceeded` (100 requests/minute) or `item_rate_limit_exceeded` (2,000 items/minute; nothing in the request was processed). Both carry `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error. Some items may have been added; retrying is safe.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "`approval_check_failed`: the campaign's approval state could not be confirmed, so nothing in the request was added. Retry the whole request with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "`items` holds 1 to 500 objects with the same fields as POST /api/v1/leads (without `campaign_id`).",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "campaign_id": {
                    "type": "integer"
                  },
                  "items": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 500,
                    "items": {
                      "type": "object",
                      "properties": {
                        "linkedin_url": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string",
                          "nullable": true
                        },
                        "title": {
                          "type": "string",
                          "nullable": true
                        },
                        "company": {
                          "type": "string",
                          "nullable": true
                        },
                        "location": {
                          "type": "string",
                          "nullable": true
                        },
                        "messages": {
                          "type": "object",
                          "properties": {
                            "connection_request": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 8000
                            },
                            "follow_up_1": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 8000
                            },
                            "follow_up_2": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 8000
                            },
                            "follow_up_3": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 8000
                            }
                          }
                        }
                      },
                      "required": [
                        "linkedin_url"
                      ]
                    }
                  }
                },
                "required": [
                  "campaign_id",
                  "items"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/leads/{id}": {
      "patch": {
        "operationId": "updateLead",
        "summary": "Update a lead's status",
        "description": "Only four statuses are writable by a human or an integration; the rest are written by the automation and refused here. `changed` appears on the 200 and on every 409, and on nothing else — it is absent from the 400s, the 404, the auth failures and the 500, because nothing was evaluated on those paths.",
        "tags": [
          "Leads"
        ],
        "responses": {
          "200": {
            "description": "Applied, or a no-op if the lead already had that status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "changed": {
                      "type": "boolean"
                    },
                    "lead": {
                      "$ref": "#/components/schemas/Lead"
                    },
                    "previous_status": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`status_required` or `invalid_status`; both carry `valid_statuses`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "hint": {
                      "type": "string"
                    },
                    "valid_statuses": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Lead not found for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`withdrawn_status_write_refused`, `promote_only_guard` or `conflict`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "hint": {
                      "type": "string"
                    },
                    "changed": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Positive integer id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The target status.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "meeting_booked",
                      "replied",
                      "not_interested",
                      "connected"
                    ]
                  }
                },
                "required": [
                  "status"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/leads/{id}/messages": {
      "put": {
        "operationId": "setLeadMessages",
        "summary": "Set one lead's connection note and follow-ups",
        "description": "Set or replace this lead's text for any of `connection_request`, `follow_up_1`, `follow_up_2`, `follow_up_3`; steps not sent are kept. Each step is accepted only while it has not been sent. Each message is filled in with the lead's name, title, company and location and checked like a CSV upload: a connection note over the account's note limit is refused, never shortened; a variable the lead has no value for is refused; `{{calendar_link}}` becomes the lead's tracked link. Once a lead has any message set, ZenMode's AI no longer writes messages for that lead. It sends nothing.",
        "tags": [
          "Leads"
        ],
        "responses": {
          "200": {
            "description": "Saved. `messages` is the lead's stored text, exactly as it will be sent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "lead_id": {
                      "type": "integer"
                    },
                    "campaign_id": {
                      "type": "integer"
                    },
                    "messages": {
                      "type": "object",
                      "properties": {
                        "connection_request": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8000
                        },
                        "follow_up_1": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8000
                        },
                        "follow_up_2": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8000
                        },
                        "follow_up_3": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 8000
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_lead_id`, `invalid_json`, `messages_required` or `invalid_messages`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`lead_not_found`: not this account's lead, or it was removed from its campaign.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`ai_sequence_campaign`, `unsupported_campaign_type`, `message_already_sent` or `concurrent_update`. Nothing was saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "hint": {
                      "type": "string"
                    },
                    "message_key": {
                      "type": "string"
                    },
                    "missing_variables": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "length": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "`campaign_sends_no_connection_note`, `follow_up_step_not_configured`, `missing_variables` (carries `missing_variables`), `connection_note_over_limit` (carries `length` and `limit`) or `empty_message`. Nothing was saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "hint": {
                      "type": "string"
                    },
                    "message_key": {
                      "type": "string"
                    },
                    "missing_variables": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "length": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Positive integer id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The steps to set.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "messages": {
                    "type": "object",
                    "properties": {
                      "connection_request": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 8000
                      },
                      "follow_up_1": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 8000
                      },
                      "follow_up_2": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 8000
                      },
                      "follow_up_3": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 8000
                      }
                    }
                  }
                },
                "required": [
                  "messages"
                ]
              }
            }
          }
        }
      }
    },
    "/api/v1/leads/{id}/activity": {
      "get": {
        "operationId": "listLeadActivity",
        "summary": "Message history for one lead",
        "description": "Sent and received messages, newest first. `source` distinguishes desktop automation from the web composer.",
        "tags": [
          "Leads"
        ],
        "responses": {
          "200": {
            "description": "A page of activity.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "lead": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "campaign_id": {
                          "type": "integer"
                        },
                        "name": {
                          "type": "string",
                          "nullable": true
                        },
                        "linkedin_url": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        }
                      }
                    },
                    "activity": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "direction": {
                            "type": "string",
                            "enum": [
                              "sent",
                              "received"
                            ]
                          },
                          "text": {
                            "type": "string",
                            "nullable": true
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "automation",
                              "voyager-sync",
                              "sync",
                              "manual",
                              "web_sent",
                              "web_queued"
                            ]
                          },
                          "read_by_you": {
                            "type": "boolean"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "limit": {
                          "type": "integer"
                        },
                        "offset": {
                          "type": "integer"
                        },
                        "returned": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid id or direction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Lead not found for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Positive integer id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Rows to return. Clamped to 500.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "Restrict to one direction.",
            "schema": {
              "type": "string",
              "enum": [
                "sent",
                "received"
              ]
            }
          }
        ]
      }
    },
    "/api/v1/suppressions": {
      "get": {
        "operationId": "listSuppressions",
        "summary": "List suppressed contacts",
        "description": "Contacts excluded from all future outreach, whichever campaign they appear in.",
        "tags": [
          "Suppressions"
        ],
        "responses": {
          "200": {
            "description": "A page of suppressions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "suppressions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "linkedin_url": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string",
                            "nullable": true
                          },
                          "reason": {
                            "type": "string"
                          },
                          "identity": {
                            "type": "object",
                            "properties": {
                              "linkedin_slug": {
                                "type": "string",
                                "nullable": true
                              },
                              "sales_lead_id": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          }
                        }
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "limit": {
                          "type": "integer"
                        },
                        "offset": {
                          "type": "integer"
                        },
                        "returned": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Rows to return. Clamped to 500.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ]
      },
      "post": {
        "operationId": "createSuppression",
        "summary": "Suppress a contact",
        "description": "Adds an account-wide exclusion and stops any in-flight sequences for that person. Idempotent: suppressing an already-suppressed contact returns 201 with `already_suppressed: true`.",
        "tags": [
          "Suppressions"
        ],
        "responses": {
          "201": {
            "description": "Suppressed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "suppressed": {
                      "type": "boolean"
                    },
                    "already_suppressed": {
                      "type": "boolean"
                    },
                    "linkedin_url": {
                      "type": "string"
                    },
                    "identity": {
                      "type": "object",
                      "properties": {
                        "linkedin_slug": {
                          "type": "string",
                          "nullable": true
                        },
                        "sales_lead_id": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "leads_stopped": {
                      "type": "integer"
                    },
                    "stopped_lead_ids": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, missing url, or `no_identity`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "The contact to suppress.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "linkedin_url": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string",
                    "nullable": true
                  }
                },
                "required": [
                  "linkedin_url"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteSuppression",
        "summary": "Lift a suppression",
        "description": "Idempotent. Deleting a suppression that does not exist is a 200 with zero counts, not a 404.",
        "tags": [
          "Suppressions"
        ],
        "responses": {
          "200": {
            "description": "Lifted, or nothing to lift.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "unsuppressed": {
                      "type": "boolean"
                    },
                    "linkedin_url": {
                      "type": "string"
                    },
                    "identity": {
                      "type": "object",
                      "properties": {
                        "linkedin_slug": {
                          "type": "string",
                          "nullable": true
                        },
                        "sales_lead_id": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    },
                    "exclusions_removed": {
                      "type": "integer"
                    },
                    "leads_restored": {
                      "type": "integer"
                    },
                    "restored_lead_ids": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or unusable linkedin_url.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "linkedin_url",
            "in": "query",
            "required": true,
            "description": "The contact to un-suppress.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/v1/tasks": {
      "post": {
        "operationId": "createTask",
        "summary": "Queue an action for the desktop app",
        "description": "The desktop app polls this queue. `payload.profile_url` is required for `connect` and `message`; `payload.message` is required for `message`. A suppressed contact is refused with 409. Two distinct 429s exist: the per-minute rate limit (`Retry-After: 60`) and the account's daily action cap (`Retry-After: 3600`). `payload.message` on a `connect` task is the connection note. A `connect` or `message` task is handed to the desktop app only inside the account's business hours (the hours of your campaign on that LinkedIn account, else 08:00-18:00 London time, Monday to Friday); outside them it stays `pending`. On a free or unconfirmed LinkedIn plan the connection request is sent without a note, and the task result carries `note_dropped: true` with `note_dropped_reason: \"free_or_unconfirmed_plan\"`.",
        "tags": [
          "Tasks"
        ],
        "responses": {
          "201": {
            "description": "Queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "task": {
                      "$ref": "#/components/schemas/Task"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown action_type, missing payload, or a missing or invalid required payload field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The contact is suppressed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "The action to queue.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action_type": {
                    "type": "string",
                    "enum": [
                      "connect",
                      "message",
                      "follow",
                      "view_profile",
                      "withdraw_connection"
                    ]
                  },
                  "linkedin_account_id": {
                    "type": "string",
                    "nullable": true,
                    "description": "Your own label for the LinkedIn account, stored and returned as-is. It is not a ZenMode account id and does not choose which account runs the task."
                  },
                  "payload": {
                    "type": "object",
                    "properties": {
                      "profile_url": {
                        "type": "string"
                      },
                      "message": {
                        "type": "string"
                      }
                    }
                  },
                  "priority": {
                    "type": "integer"
                  },
                  "max_retries": {
                    "type": "integer"
                  }
                },
                "required": [
                  "action_type",
                  "payload"
                ]
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listTasks",
        "summary": "List queued and completed tasks",
        "description": "Newest first.",
        "tags": [
          "Tasks"
        ],
        "responses": {
          "200": {
            "description": "A page of tasks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tasks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Task"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by task status.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "processing",
                "completed",
                "failed",
                "cancelled"
              ]
            }
          },
          {
            "name": "linkedin_account_id",
            "in": "query",
            "required": false,
            "description": "Filter by the linkedin_account_id label the tasks were queued with (exact match).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Rows to return. Clamped to 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ]
      }
    },
    "/api/v1/tasks/{id}": {
      "get": {
        "operationId": "getTask",
        "summary": "Fetch one task",
        "description": "Includes `max_retries`, which the list endpoint omits.",
        "tags": [
          "Tasks"
        ],
        "responses": {
          "200": {
            "description": "The task.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "task": {
                      "$ref": "#/components/schemas/Task"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Task not found for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The task id returned when it was queued, e.g. `task_mb3k2x1c_q8z0v7ke`.",
            "schema": {
              "type": "string",
              "pattern": "^task_"
            }
          }
        ]
      },
      "patch": {
        "operationId": "updateTask",
        "summary": "Report a task outcome",
        "description": "Moves a task to `processing`, `completed` or `failed`. `cancelled` is rejected — delete the task instead. A terminal transition fires the matching `task.completed` / `task.failed` webhook.",
        "tags": [
          "Tasks"
        ],
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "task": {
                      "$ref": "#/components/schemas/Task"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown or non-transitionable status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Task not found for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The task id returned when it was queued, e.g. `task_mb3k2x1c_q8z0v7ke`.",
            "schema": {
              "type": "string",
              "pattern": "^task_"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The outcome.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "processing",
                      "completed",
                      "failed"
                    ]
                  },
                  "result": {
                    "type": "object",
                    "additionalProperties": true
                  }
                },
                "required": [
                  "status"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteTask",
        "summary": "Cancel a task",
        "description": "Only a task that has not started can be cancelled.",
        "tags": [
          "Tasks"
        ],
        "responses": {
          "200": {
            "description": "Cancelled.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "task": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "pattern": "^task_",
                          "description": "Task id, e.g. `task_mb3k2x1c_q8z0v7ke`."
                        },
                        "status": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Task not found for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The task id returned when it was queued, e.g. `task_mb3k2x1c_q8z0v7ke`.",
            "schema": {
              "type": "string",
              "pattern": "^task_"
            }
          }
        ]
      }
    },
    "/api/v1/webhooks": {
      "post": {
        "operationId": "createWebhook",
        "summary": "Register a webhook",
        "description": "HTTPS only, on the standard port (443), with no credentials in the URL; the host must be public, and the URL is checked before it is stored and again at every delivery. Redirects from the endpoint are not followed. Deliveries carry `X-ZenMode-Signature` (hex HMAC-SHA256 of the exact raw request body, keyed with the webhook's `secret`) and `X-ZenMode-Event`. A `task.completed` or `task.failed` body is `{ event, data, timestamp }`, where `data.result` is the task's `result`. On a free or unconfirmed LinkedIn plan the connection request is sent without a note, and the task result carries `note_dropped: true` with `note_dropped_reason: \"free_or_unconfirmed_plan\"`. The webhook body carries the same two fields in `data.result`.",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "201": {
            "description": "Registered. The response carries the signing `secret`, the only time this endpoint returns it; store it with your endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "$ref": "#/components/schemas/WebhookCreated"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON, a non-HTTPS or disallowed URL, or an unknown event name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "description": "Endpoint and the events to send it.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "task.completed",
                        "task.failed",
                        "lead.replied",
                        "lead.accepted",
                        "lead.sent"
                      ]
                    }
                  }
                },
                "required": [
                  "url",
                  "events"
                ]
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listWebhooks",
        "summary": "List webhooks",
        "description": "Every webhook registered on this account.",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "The webhooks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhook",
        "summary": "Delete a webhook",
        "description": "Deliveries stop immediately.",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or non-integer id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or invalid Authorization header, or an unknown API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but the account's plan does not include API access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Webhook not found for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (100 requests/minute). Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "The webhook id.",
            "schema": {
              "type": "integer"
            }
          }
        ]
      }
    }
  }
}