{
  "openapi": "3.1.0",
  "info": {
    "title": "Emelia API",
    "version": "2026-10-11",
    "summary": "Automate Emelia campaigns, lists, enrichment tools, mailboxes, LinkedIn extraction and webhooks.",
    "description": "The Emelia API automates what you do in Emelia: multichannel prospecting campaigns (email, LinkedIn, WhatsApp, manual tasks), contact lists, Email Finder, Phone Finder, Email Verifier, sending mailboxes and warmup, the blacklist, LinkedIn and Sales Navigator extraction, and campaign webhooks.\n\nDocumentation and guides: https://docs.emelia.io\n\n## Base URL\n\n`https://api.emelia.io`. Lists and contacts (`/lists/...`) are served by a separate service behind the same host: same API key, but limits and error formats differ (see the Lists tags).\n\n## Authentication\n\nSend your API key in the `Authorization` header, as is: `Authorization: YOUR_API_KEY`. A `Bearer ` prefix is accepted. A key is exactly 48 letters and digits: a value of another length is not treated as an API key (most routes then answer 401).\n\nCreate keys in the app, Settings then API: https://v3.emelia.io/settings/api. You can have several keys, and disable or delete them.\n\nA key gives access to the whole account: protect it like a password. Never put it in a URL or in front end code.\n\nThe MCP server `https://mcp.emelia.io/mcp` uses this same API key. The Emelia Inbox API is a different service with its own `mk_` keys, described in a separate OpenAPI file.\n\n## Rate limits\n\nCounted per account (all keys of the account share the counter), in a fixed 60 second window that starts with the first request:\n\n| Plan | Requests per minute |\n|---|---|\n| No active plan | 30 |\n| Start | 100 |\n| Grow | 300 |\n| Scale | 1,000 |\n\nRequests to `/tools/...` (enrichment) and to `/lists/...` are not counted. When the quota is exceeded the API currently answers HTTP 500 `{\"error\": \"Internal server error\"}` without a `Retry-After` header: wait for the next minute before retrying.\n\n## Errors\n\nJSON body `{\"error\": \"message\", \"code\": \"STABLE_CODE\", \"params\": {}}`. `code` and `params` are only present for catalogued errors (mainly campaign start checks, see the `ErrorCode` schema). Most business errors, including \"not found\", use HTTP 400. 401 means a missing, unknown or disabled key. Some routes use their own format, documented on the route. Unknown paths return an HTML 404 page and malformed JSON returns an HTML 400 page.\n\nThe lists service (`/lists/...`) uses another format: `{\"message\": \"...\", \"code\": \"...\"}` with real 404, 413 and 422 statuses, except its 401 which is `{\"error\": \"...\", \"code\": \"AUTH_REQUIRED\"}`.\n\n## Credits\n\nEmail Finder: 1 credit per search, refunded when no email is found. Phone Finder: 50 credits per search, refunded when no number is found. Email Verifier: 0.25 credit, not refunded. Consumed credits are listed by `GET /credits/usage`.\n\n## Asynchronous work\n\nEnrichment tools return a `jobId` at once: poll the matching `GET` route every 3 to 5 seconds until `status` is `done` (or `error`). Campaign start, contact scheduling and list resynchronisation run in the background after the response.\n\n## Pagination\n\nThere is no single convention, check each route. Campaign contacts and campaign activities use a zero based `page`. Tool histories, credit history and step contacts use a one based `page`. Many list routes are not paginated at all.\n\n## Identifiers and dates\n\nIds are 24 character hexadecimal strings (MongoDB ObjectId). Dates are ISO 8601 strings in UTC.\n\n## Campaign lifecycle\n\n`DRAFT` then `RUNNING`, `PAUSED` (and back to `RUNNING`), `FINISHED` (back to `PAUSED` with reactivate) and `ARCHIVED`. Sequence, settings, identities and name can only be changed when the campaign is not `RUNNING`.\n\n## Notes for AI agents\n\n- Read before you write: `GET` the campaign, change the object, send the whole object back (steps and settings replace what is stored).\n- Never regenerate the step or version ids of a campaign that already ran.\n- Every contact value must be a string.\n- For many contacts, fill a list with the Lists endpoints, then attach it with `PATCH /advanced/campaigns/{campaignId}/recipients`.\n- Expect HTTP 400 for \"not found\", and HTTP 500 when the per minute quota is exceeded.",
    "contact": {
      "name": "Emelia",
      "url": "https://emelia.io"
    }
  },
  "servers": [
    {
      "url": "https://api.emelia.io",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Campaigns",
      "description": "Multichannel campaigns (email, LinkedIn, WhatsApp, manual tasks): create, configure the sequence, attach lists and sending identities, start, pause, follow activities and statistics. These are the campaigns of the current app. Typical flow: `POST /advanced/campaigns`, `PATCH .../steps`, `PATCH .../settings`, `PATCH .../recipients` (lists created with the Lists endpoints), `PATCH .../identities`, optionally `POST .../test-email`, then `POST .../start`."
    },
    {
      "name": "Campaign contacts",
      "description": "Contacts of a multichannel campaign. For bulk imports prefer the Lists endpoints (`/lists/...`) and attach the list with `PATCH /advanced/campaigns/{campaignId}/recipients`."
    },
    {
      "name": "Campaign tracking",
      "description": "Activities, statistics, progress, manual tasks and exports of a multichannel campaign."
    },
    {
      "name": "Uploads",
      "description": "Upload files used in campaign steps. Uploaded files are stored with a PUBLIC URL."
    },
    {
      "name": "Lists",
      "description": "Create, list, rename, delete and export contact lists. These endpoints are served by the lists service under `https://api.emelia.io/lists`. Authenticate with your raw API key in the `Authorization` header (a `Bearer ` prefix is accepted). Errors are JSON objects with a `message` field (authentication errors use `error` and `code` instead)."
    },
    {
      "name": "List contacts",
      "description": "Add, find, read, update and delete the contacts of a list. Contacts are sent as flat JSON objects (one key per field). Adding a contact to a list that is used by a campaign makes the contact enter that campaign."
    },
    {
      "name": "List custom variables",
      "description": "Declare the custom variables (personalization fields) of your account before loading contacts, so that your keys land in the right variable. Custom variables are account-wide: they are shared by all your lists."
    },
    {
      "name": "List exports",
      "description": "Follow and download the asynchronous exports started with `POST /lists/list/export` and `async: true`."
    },
    {
      "name": "Enrichment tools",
      "description": "Email Finder, Phone Finder and Email Verifier. Each call consumes credits and returns a `jobId`; poll the matching `GET .../{jobId}` route until `status` is `done` (or `error`). These routes are not subject to the per minute quota."
    },
    {
      "name": "Email providers",
      "description": "Sending mailboxes connected to the account, and their warmup."
    },
    {
      "name": "Blacklist",
      "description": "Account-wide blacklist of email addresses, domains and LinkedIn profiles, checked by the campaign engine before email and LinkedIn actions of every campaign (multichannel and legacy). The paths start with `/emails` for historical reasons."
    },
    {
      "name": "Webhooks",
      "description": "Campaign activity webhooks. A URL registered on a campaign, or on all your campaigns, receives a JSON POST each time a selected event happens: email sent, opened, clicked, replied, bounced, unsubscribed; LinkedIn visit, invitation, acceptance, message, InMail, like, reply; WhatsApp message and reply; campaign finished. Deliveries are fire and forget: no signature, no retry on HTTP errors, no ordering guarantee, no event ID. The exact bodies are described in the webhooks section (emailCampaignActivity, linkedinCampaignActivity, advancedCampaignActivity, campaignEnding, webhookTestEvent)."
    },
    {
      "name": "LinkedIn scraper",
      "description": "Extract the people (or companies) of a LinkedIn search into a CSV, then optionally find their professional emails.\n\n**Typical flow**\n1. Connect a LinkedIn account for scraping with `POST /linkedin-scrappers/authes` (Sales Navigator session cookies), or reuse an existing one. Classic LinkedIn searches use the LinkedIn accounts connected to your campaigns.\n2. Start an extraction with `POST /linkedin-scrappers` and the search URL. It runs in the background.\n3. Follow it: poll `GET /linkedin-scrappers/{id}` (`status`, `scrapIndex` out of `totalContact`) or register a webhook with `POST /linkedin-scrappers/{id}/webhook`.\n4. When `status` is `done`: optionally find emails with `POST /linkedin-scrappers/{id}/enrich` (1 credit per email found), then download the CSV with `GET /linkedin-scrappers/{id}/download`. Finished extractions are also imported automatically into an Emelia list (`convertedListId`).\n\n**Plans**: extraction is included in every subscription (Start: 2,500 contacts per search; Grow and Scale: large searches split automatically, up to 80,000 results). Without subscription, one free extraction, then about 1 credit per 2 contacts.\n\n**Errors**: JSON `{ \"error\": \"<message>\" }` with HTTP 400. Validation errors also carry `\"code\": \"INVALID_REQUEST\"`; business errors are English sentences without code for now."
    },
    {
      "name": "Account",
      "description": "Information about the account behind the API key."
    },
    {
      "name": "Email campaigns (legacy)",
      "description": "Endpoints of the previous email-only campaign system (routes under /emails, plus /stats). They keep working for existing integrations, but new integrations should use the multichannel /advanced campaign endpoints."
    },
    {
      "name": "LinkedIn campaigns (legacy)",
      "description": "Endpoints of the previous LinkedIn-only campaign system (routes under /linkedin, plus /stats/linkedin). New integrations should use the multichannel /advanced campaign endpoints."
    }
  ],
  "paths": {
    "/advanced/campaigns": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "createCampaign",
        "summary": "Create a campaign",
        "description": "Creates an empty multichannel campaign in `DRAFT` status with default settings (35 new email contacts per day, 100 emails per day, 20 LinkedIn contacts per day, Monday to Friday 08:00 to 17:00, tracking off) and a sequence that only contains the START step. The name must be unique in your account. Webhooks registered with `campaignId: \"ALL_CAMPAIGNS\"` and `type: \"advanced\"` are copied onto the new campaign.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Unique campaign name."
                  },
                  "timeZone": {
                    "type": "string",
                    "description": "IANA time zone. Ignored when unknown (the default `Europe/Brussels` is kept)."
                  }
                }
              },
              "example": {
                "name": "Acme Q4 outreach",
                "timeZone": "Europe/Paris"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "campaignId": {
                      "$ref": "#/components/schemas/ObjectId"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "campaignId": "6650f1a2b839d80011c5a0f1"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "duplicate": {
                    "summary": "Name already used",
                    "value": {
                      "error": "A campaign with this name already exists"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      },
      "get": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "listCampaigns",
        "summary": "List campaigns",
        "description": "Returns all multichannel campaigns of the account except archived ones, newest first. No pagination. Use `all=true` to get an extra first item `ALL_CAMPAIGNS` (used to register a webhook on every campaign).",
        "parameters": [
          {
            "name": "all",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "description": "Prepend the synthetic `ALL_CAMPAIGNS` entry."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "campaigns": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CampaignSummary"
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "campaigns": [
                    {
                      "_id": "6650f1a2b839d80011c5a0f1",
                      "name": "Acme Q4 outreach",
                      "status": "RUNNING",
                      "createdAt": "2026-10-01T08:00:00.000Z",
                      "startAt": "2026-10-02T07:00:00.000Z",
                      "identities": [
                        {
                          "_id": "6650f1a2b839d80011c5a0f9",
                          "email": "6650f1a2b839d80011c5a0aa",
                          "linkedin": "6650f1a2b839d80011c5a0bb"
                        }
                      ],
                      "hasDisconnectedLinkedin": false,
                      "hasDisconnectedEmail": false
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}": {
      "get": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "getCampaign",
        "summary": "Get a campaign",
        "description": "Returns the full campaign: schedule (settings), sequence tree (`steps`), identities, attached lists with their contact count, status and dates. Read it before any `PATCH`: the steps and settings routes replace what you send.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/Campaign"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d80011c5a0f1",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "tagIds": [
                      "6650f1a2b839d80011c5a0f1"
                    ],
                    "identities": [
                      {
                        "_id": "6650f1a2b839d80011c5a0f1",
                        "userId": "6650f1a2b839d80011c5a0f1",
                        "name": "Marie Martin",
                        "email": "6650f1a2b839d80011c5a0f1",
                        "linkedin": "6650f1a2b839d80011c5a0f1",
                        "whatsapp": "6650f1a2b839d80011c5a0f1"
                      }
                    ],
                    "schedule": {
                      "dailyEmailAdded": 35,
                      "dailyEmailLimit": 100,
                      "dailyLinkedinAdded": 20,
                      "dailyWhatsappAdded": 20,
                      "bcc": "",
                      "trackLinks": false,
                      "trackOpens": false,
                      "timeZone": "Europe/Brussels",
                      "days": [
                        0,
                        1,
                        2,
                        3,
                        4
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "blacklistUnsub": false,
                      "excludeAlreadyMessaged": true,
                      "eventToStop": [
                        "VISITED"
                      ],
                      "alwaysOn": false,
                      "ignoreAutoReplies": false
                    },
                    "steps": {
                      "_id": "6650f1a2b839d800552fc201",
                      "stepType": "START",
                      "delay": {
                        "amount": 1,
                        "unit": "MINUTES"
                      },
                      "identities": [
                        "6650f1a2b839d800d4064809"
                      ],
                      "versions": [
                        {
                          "_id": "6650f1a2b839d800ed4363dc",
                          "subject": "Quick question about Nexora",
                          "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                          "disabled": false,
                          "rawHtml": true,
                          "attachments": [
                            {
                              "mediaType": "MEDIA"
                            }
                          ]
                        }
                      ],
                      "cc": "",
                      "conditions": {
                        "field": "MAIL_REPLIED",
                        "operator": "EQUAL"
                      },
                      "next": {
                        "_id": "6650f1a2b839d800f83a5090",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d800c85e3a84"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d8001daab8ff",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d80040f13c3f",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800c39d462f"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d80035cb8e8a",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8008283738e",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800fabfab18",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d800cf9b2e9d",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800cd9659c5"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8000feddd84",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8006252b58c",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800f02835ee",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d80090000761",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800df7d5a69"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800e7c06cf0",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d80044432310",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d8001cc27212",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      },
                      "yes": {
                        "_id": "6650f1a2b839d8002fcb7330",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d80073add4a4"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800501b769f",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d8008e0454df",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8000fad334f"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8005ea5a9aa",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8002c0be9ae",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800a7cdff38",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d8009c26aa3d",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8005f17e165"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800b36aea24",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800e02962ac",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d80027a94e0e",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d80093737481",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800f3adb689"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8009dccc810",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800a937cdb0",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800a9f44632",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      },
                      "no": {
                        "_id": "6650f1a2b839d800b74f7ab2",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d800d01a715e"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d80015726ab1",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d800919d6c25",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8001b9fadcd"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800afc51a2c",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800a0db0d14",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800cc259cb6",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d800271026d7",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8005e80ef67"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8004728f7c2",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800f6ba16e6",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d8002a3b9db0",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d8001f510c4b",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8004a565f5b"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8004b91260e",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d80018e2a0ca",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800cd0fbfc4",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      }
                    },
                    "recipients": {
                      "lists": [
                        {
                          "_id": "6650f1a2b839d80011c5a0f1",
                          "name": "Q4 outreach: SaaS founders",
                          "contactCount": 1
                        }
                      ],
                      "excludedLists": [
                        {
                          "_id": "6650f1a2b839d80011c5a0f1",
                          "name": "Q4 outreach: SaaS founders",
                          "contactCount": 1
                        }
                      ],
                      "processing": true
                    },
                    "zapierData": [
                      {
                        "_id": "6650f1a2b839d80011c5a0f1",
                        "webhookUrl": "https://hooks.yourcompany.com/emelia",
                        "events": []
                      }
                    ],
                    "hubspotIntegration": {},
                    "pipedriveIntegration": {},
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown campaign or campaign of another account (no 404 is used).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "summary": "Not found",
                    "value": {
                      "error": "Campaign not found"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "deleteCampaign",
        "summary": "Delete a campaign",
        "description": "Deletes the campaign. A running campaign must be paused first. Irreversible.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "running": {
                    "summary": "Campaign running (message in French)",
                    "value": {
                      "error": "Vous devez mettre la campagne en pause avant de la supprimer"
                    }
                  },
                  "notFound": {
                    "summary": "Not found",
                    "value": {
                      "error": "Campaign not found"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/name": {
      "patch": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "renameCampaign",
        "summary": "Rename a campaign",
        "description": "Renames the campaign. Not allowed while the campaign is RUNNING.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "Acme Q4 outreach v2"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/Campaign"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d80011c5a0f1",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "tagIds": [
                      "6650f1a2b839d80011c5a0f1"
                    ],
                    "identities": [
                      {
                        "_id": "6650f1a2b839d80011c5a0f1",
                        "userId": "6650f1a2b839d80011c5a0f1",
                        "name": "Marie Martin",
                        "email": "6650f1a2b839d80011c5a0f1",
                        "linkedin": "6650f1a2b839d80011c5a0f1",
                        "whatsapp": "6650f1a2b839d80011c5a0f1"
                      }
                    ],
                    "schedule": {
                      "dailyEmailAdded": 35,
                      "dailyEmailLimit": 100,
                      "dailyLinkedinAdded": 20,
                      "dailyWhatsappAdded": 20,
                      "bcc": "",
                      "trackLinks": false,
                      "trackOpens": false,
                      "timeZone": "Europe/Brussels",
                      "days": [
                        0,
                        1,
                        2,
                        3,
                        4
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "blacklistUnsub": false,
                      "excludeAlreadyMessaged": true,
                      "eventToStop": [
                        "VISITED"
                      ],
                      "alwaysOn": false,
                      "ignoreAutoReplies": false
                    },
                    "steps": {
                      "_id": "6650f1a2b839d800498e9dff",
                      "stepType": "START",
                      "delay": {
                        "amount": 1,
                        "unit": "MINUTES"
                      },
                      "identities": [
                        "6650f1a2b839d800271af1ef"
                      ],
                      "versions": [
                        {
                          "_id": "6650f1a2b839d800d471e242",
                          "subject": "Quick question about Nexora",
                          "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                          "disabled": false,
                          "rawHtml": true,
                          "attachments": [
                            {
                              "mediaType": "MEDIA"
                            }
                          ]
                        }
                      ],
                      "cc": "",
                      "conditions": {
                        "field": "MAIL_REPLIED",
                        "operator": "EQUAL"
                      },
                      "next": {
                        "_id": "6650f1a2b839d80075fdd3ca",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d800b2167636"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d80048cdb6d9",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d800a4dfb53d",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8001c33f465"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800d76b4b24",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800e2281dac",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800fa90870e",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d80008d9a42f",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800eb3ca81f"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d80061a9645a",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800dcd057fe",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800d26cc328",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d8000a64af83",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800bd846cd3"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8001f2c2de6",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8001c87cb02",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d8003244233c",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      },
                      "yes": {
                        "_id": "6650f1a2b839d800e7933ace",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d8003e92faca"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d8002ce293dd",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d8002f2974c9",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d80009d3fdb1"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800842890d8",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8002ee6ba18",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d8009335aa9a",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d800bf8452b3",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8000842a783"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800bc920876",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800345524b2",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800641f460c",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d800bd5a3307",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800756d2097"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800c1089d12",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800909fd0d6",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800d3c6efc0",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      },
                      "no": {
                        "_id": "6650f1a2b839d800aff80158",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d800e3061a0c"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800ac97aed7",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d800cc57a087",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8002ae1d217"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800e3f18592",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800bf669656",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800cc968440",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d8008b5d86a5",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800f09d444d"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8007df2c9ac",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8000b224f94",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d80018f4e036",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d800d99d6d49",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800e5e64231"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8009fea5a58",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800c0c1ba98",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800dcf0f81a",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      }
                    },
                    "recipients": {
                      "lists": [
                        {
                          "_id": "6650f1a2b839d80011c5a0f1",
                          "name": "Q4 outreach: SaaS founders",
                          "contactCount": 1
                        }
                      ],
                      "excludedLists": [
                        {
                          "_id": "6650f1a2b839d80011c5a0f1",
                          "name": "Q4 outreach: SaaS founders",
                          "contactCount": 1
                        }
                      ],
                      "processing": true
                    },
                    "zapierData": [
                      {
                        "_id": "6650f1a2b839d80011c5a0f1",
                        "webhookUrl": "https://hooks.yourcompany.com/emelia",
                        "events": []
                      }
                    ],
                    "hubspotIntegration": {},
                    "pipedriveIntegration": {},
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "running": {
                    "summary": "Running campaign",
                    "value": {
                      "error": "You must pause your campaign before updating it"
                    }
                  },
                  "missing": {
                    "summary": "Missing name",
                    "value": {
                      "error": "Name is required"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/steps": {
      "patch": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "updateCampaignSteps",
        "summary": "Replace the sequence",
        "description": "Replaces the WHOLE sequence tree of the campaign. Always `GET` the campaign first, modify the `steps` object and send the complete tree back. The campaign must not be RUNNING (pause it first). Missing step and version `_id`s are generated by the server; keep existing ids on a campaign that already ran, otherwise history, statistics and the position of contacts in the sequence are lost and contacts may receive steps again. Content is not validated here: empty email versions, missing subjects or audio are only reported by `POST .../start`.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "steps"
                ],
                "properties": {
                  "steps": {
                    "$ref": "#/components/schemas/CampaignStep"
                  }
                }
              },
              "example": {
                "steps": {
                  "_id": "START",
                  "stepType": "START",
                  "identities": [],
                  "delay": {
                    "amount": 0,
                    "unit": "DAYS"
                  },
                  "next": {
                    "stepType": "CONNECTION",
                    "identities": [],
                    "delay": {
                      "amount": 0,
                      "unit": "DAYS"
                    },
                    "versions": [
                      {
                        "message": "Hi {{firstName}}, glad to connect."
                      }
                    ],
                    "next": {
                      "stepType": "CONDITION",
                      "identities": [],
                      "delay": {
                        "amount": 3,
                        "unit": "DAYS"
                      },
                      "conditions": {
                        "field": "ACCEPTED",
                        "operator": "EQUAL",
                        "value": "true"
                      },
                      "yes": {
                        "stepType": "MESSAGE",
                        "identities": [],
                        "delay": {
                          "amount": 1,
                          "unit": "HOURS"
                        },
                        "versions": [
                          {
                            "message": "Thanks for connecting {{firstName}}!"
                          }
                        ]
                      },
                      "no": {
                        "stepType": "EMAIL",
                        "identities": [],
                        "delay": {
                          "amount": 2,
                          "unit": "HOURS"
                        },
                        "versions": [
                          {
                            "subject": "Quick question, {{firstName}}",
                            "message": "<p>Hi {{firstName}},</p><p>I sent you an invitation on LinkedIn...</p>"
                          }
                        ],
                        "next": {
                          "stepType": "END_OF_CAMPAIGN",
                          "identities": [],
                          "delay": {
                            "amount": 0,
                            "unit": "DAYS"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "running": {
                    "summary": "Running campaign",
                    "value": {
                      "error": "You must pause your campaign before updating it"
                    }
                  },
                  "notObject": {
                    "summary": "steps must be an object",
                    "value": {
                      "error": "\"steps\" must be of type object",
                      "code": "INVALID_REQUEST"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/settings": {
      "patch": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "updateCampaignSettings",
        "summary": "Update campaign settings",
        "description": "Merges the given settings into the campaign schedule. Not allowed while RUNNING. Rules: `start` must be before `end`; `dailyEmailLimit` must be greater than or equal to `dailyEmailAdded`; `dailyLinkedinAdded` is capped at 40 when the sequence contains a CONNECTION step (80 otherwise). Returns the updated campaign.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "settings"
                ],
                "properties": {
                  "settings": {
                    "$ref": "#/components/schemas/CampaignSettingsInput"
                  }
                }
              },
              "example": {
                "settings": {
                  "trackLinks": false,
                  "trackOpens": false,
                  "blacklistUnsub": true,
                  "dailyEmailAdded": 30,
                  "dailyEmailLimit": 100,
                  "dailyLinkedinAdded": 20,
                  "timeZone": "Europe/Paris",
                  "days": [
                    0,
                    1,
                    2,
                    3,
                    4
                  ],
                  "start": "08:30",
                  "end": "18:00",
                  "eventToStop": [
                    "MAIL_REPLIED",
                    "LINKEDIN_REPLIED"
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/Campaign"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d80011c5a0f1",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "tagIds": [
                      "6650f1a2b839d80011c5a0f1"
                    ],
                    "identities": [
                      {
                        "_id": "6650f1a2b839d80011c5a0f1",
                        "userId": "6650f1a2b839d80011c5a0f1",
                        "name": "Marie Martin",
                        "email": "6650f1a2b839d80011c5a0f1",
                        "linkedin": "6650f1a2b839d80011c5a0f1",
                        "whatsapp": "6650f1a2b839d80011c5a0f1"
                      }
                    ],
                    "schedule": {
                      "dailyEmailAdded": 35,
                      "dailyEmailLimit": 100,
                      "dailyLinkedinAdded": 20,
                      "dailyWhatsappAdded": 20,
                      "bcc": "",
                      "trackLinks": false,
                      "trackOpens": false,
                      "timeZone": "Europe/Brussels",
                      "days": [
                        0,
                        1,
                        2,
                        3,
                        4
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "blacklistUnsub": false,
                      "excludeAlreadyMessaged": true,
                      "eventToStop": [
                        "VISITED"
                      ],
                      "alwaysOn": false,
                      "ignoreAutoReplies": false
                    },
                    "steps": {
                      "_id": "6650f1a2b839d800738c8e79",
                      "stepType": "START",
                      "delay": {
                        "amount": 1,
                        "unit": "MINUTES"
                      },
                      "identities": [
                        "6650f1a2b839d800c4ad78e1"
                      ],
                      "versions": [
                        {
                          "_id": "6650f1a2b839d80061ffa284",
                          "subject": "Quick question about Nexora",
                          "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                          "disabled": false,
                          "rawHtml": true,
                          "attachments": [
                            {
                              "mediaType": "MEDIA"
                            }
                          ]
                        }
                      ],
                      "cc": "",
                      "conditions": {
                        "field": "MAIL_REPLIED",
                        "operator": "EQUAL"
                      },
                      "next": {
                        "_id": "6650f1a2b839d800db5ebfa8",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d800e60a709c"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d80069bcea47",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d8006f3f4cf7",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8002afcd407"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800be025a62",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800d66b0e86",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d8007d4f87d0",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d800ac9b82b5",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8001b26d55d"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d80044576ddc",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8001d721aa4",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800de92ba26",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d80025f91939",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800a41d81a1"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8003de932e8",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800ff6e8d48",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d8005553616a",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      },
                      "yes": {
                        "_id": "6650f1a2b839d8006c228008",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d800c32d0efc"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d8006e1fe027",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d800761a0cd7",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8000aec6567"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800447d29c2",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8002dfc5ce6",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800e1f3ffb0",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d800f9659915",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d80084b3603d"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d80008e9d5bc",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d80004619884",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d8001b596a86",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d8009b912d19",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800c3847301"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800a52c6ec8",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800d62ec0a8",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d8005d65cfca",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      },
                      "no": {
                        "_id": "6650f1a2b839d8005817272a",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d800a8ab4b96"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d80013f3f339",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d8005fa8071d",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800d7d67e45"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d80095424704",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8005274960c",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800a24d236e",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d8007a5b5c0f",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800275b907f"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8008fd0d3ba",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8009543f45e",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800aeb4b288",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d800269b91e3",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800341e3fb3"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8007b27d3c6",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800cad2b9e2",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d80002b6d29c",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      }
                    },
                    "recipients": {
                      "lists": [
                        {
                          "_id": "6650f1a2b839d80011c5a0f1",
                          "name": "Q4 outreach: SaaS founders",
                          "contactCount": 1
                        }
                      ],
                      "excludedLists": [
                        {
                          "_id": "6650f1a2b839d80011c5a0f1",
                          "name": "Q4 outreach: SaaS founders",
                          "contactCount": 1
                        }
                      ],
                      "processing": true
                    },
                    "zapierData": [
                      {
                        "_id": "6650f1a2b839d80011c5a0f1",
                        "webhookUrl": "https://hooks.yourcompany.com/emelia",
                        "events": []
                      }
                    ],
                    "hubspotIntegration": {},
                    "pipedriveIntegration": {},
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "validation": {
                    "summary": "Validation error",
                    "value": {
                      "error": "\"settings.trackLinks\" is required",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "hours": {
                    "summary": "Start after end",
                    "value": {
                      "error": "Schedule hours are invalid"
                    }
                  },
                  "limit": {
                    "summary": "Daily limit lower than daily contacts",
                    "value": {
                      "error": "Daily limit must be greater or equal than daily contact"
                    }
                  },
                  "linkedin": {
                    "summary": "LinkedIn ceiling",
                    "value": {
                      "error": "You cannot add more than 40 LinkedIn contacts per day when your sequence contains a connection request",
                      "code": "MC_LINKEDIN_DAILY_LIMIT_CONNECTION",
                      "params": {
                        "limit": 40
                      }
                    }
                  },
                  "running": {
                    "summary": "Running campaign",
                    "value": {
                      "error": "You must pause your campaign before updating it"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/recipients": {
      "patch": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "updateCampaignRecipients",
        "summary": "Set the contact lists of a campaign",
        "description": "Sets the FULL selection of lists (replace semantics): the campaign contacts become the contacts of `lists`, minus any contact whose email or LinkedIn URL appears in one of `excludedLists`. When `excludedLists` is omitted the current exclusion lists are kept. Allowed on a running campaign. A campaign cannot be started with more than 30,000 recipients. Create and fill lists with the Lists endpoints (`/lists/...`).",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "lists"
                ],
                "properties": {
                  "lists": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/ObjectId"
                    },
                    "description": "Ids of the lists to use."
                  },
                  "excludedLists": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/ObjectId"
                    },
                    "description": "Ids of exclusion lists."
                  }
                }
              },
              "example": {
                "lists": [
                  "6650f1a2b839d80011c5b111"
                ],
                "excludedLists": [
                  "6650f1a2b839d80011c5b222"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/Campaign"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d80011c5a0f1",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "tagIds": [
                      "6650f1a2b839d80011c5a0f1"
                    ],
                    "identities": [
                      {
                        "_id": "6650f1a2b839d80011c5a0f1",
                        "userId": "6650f1a2b839d80011c5a0f1",
                        "name": "Marie Martin",
                        "email": "6650f1a2b839d80011c5a0f1",
                        "linkedin": "6650f1a2b839d80011c5a0f1",
                        "whatsapp": "6650f1a2b839d80011c5a0f1"
                      }
                    ],
                    "schedule": {
                      "dailyEmailAdded": 35,
                      "dailyEmailLimit": 100,
                      "dailyLinkedinAdded": 20,
                      "dailyWhatsappAdded": 20,
                      "bcc": "",
                      "trackLinks": false,
                      "trackOpens": false,
                      "timeZone": "Europe/Brussels",
                      "days": [
                        0,
                        1,
                        2,
                        3,
                        4
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "blacklistUnsub": false,
                      "excludeAlreadyMessaged": true,
                      "eventToStop": [
                        "VISITED"
                      ],
                      "alwaysOn": false,
                      "ignoreAutoReplies": false
                    },
                    "steps": {
                      "_id": "6650f1a2b839d8005f79a1ea",
                      "stepType": "START",
                      "delay": {
                        "amount": 1,
                        "unit": "MINUTES"
                      },
                      "identities": [
                        "6650f1a2b839d80019b7c656"
                      ],
                      "versions": [
                        {
                          "_id": "6650f1a2b839d800d98bd779",
                          "subject": "Quick question about Nexora",
                          "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                          "disabled": false,
                          "rawHtml": true,
                          "attachments": [
                            {
                              "mediaType": "MEDIA"
                            }
                          ]
                        }
                      ],
                      "cc": "",
                      "conditions": {
                        "field": "MAIL_REPLIED",
                        "operator": "EQUAL"
                      },
                      "next": {
                        "_id": "6650f1a2b839d8004eeead5d",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d800cbdb9385"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d80086508ae8",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d800dc53f8c4",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800c69323a8"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8008ab805db",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d80007185491",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800c17a3475",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d800bcf039cc",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8004ffc2d10"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8007ca5dd23",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800ea5bb359",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d80028be99dd",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d800abb3302e",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8004274892a"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800b9d920a9",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800ff6e1d93",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d8003b8a62e7",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      },
                      "yes": {
                        "_id": "6650f1a2b839d8007e35934f",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d80093c902bf"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800ded77632",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d8007634f47a",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8000eebcc26"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d80028e8fb4d",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8005ccb575f",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800dad59cd3",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d800bd136f1e",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d80011e1379a"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8002628a479",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d80098753403",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d80009f7ed37",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d800ffb34748",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800b0d9973c"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800eafa6e97",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8006ef115d5",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d8002132d1d9",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      },
                      "no": {
                        "_id": "6650f1a2b839d8003d6e0823",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d80006399af3"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800c7136ed6",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d8001f6e1686",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d80037fc9862"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d80060464f41",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800edc8746b",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d8005df9a81f",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d800d995c4a2",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8009306214e"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8003846edf5",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800403ffbc7",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d8000044905b",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d800e53fa85c",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d80079f2d760"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d80023970813",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8002fe1dea9",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800311ab70d",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      }
                    },
                    "recipients": {
                      "lists": [
                        {
                          "_id": "6650f1a2b839d80011c5a0f1",
                          "name": "Q4 outreach: SaaS founders",
                          "contactCount": 1
                        }
                      ],
                      "excludedLists": [
                        {
                          "_id": "6650f1a2b839d80011c5a0f1",
                          "name": "Q4 outreach: SaaS founders",
                          "contactCount": 1
                        }
                      ],
                      "processing": true
                    },
                    "zapierData": [
                      {
                        "_id": "6650f1a2b839d80011c5a0f1",
                        "webhookUrl": "https://hooks.yourcompany.com/emelia",
                        "events": []
                      }
                    ],
                    "hubspotIntegration": {},
                    "pipedriveIntegration": {},
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing": {
                    "summary": "lists missing",
                    "value": {
                      "error": "lists payload is required"
                    }
                  },
                  "excluded": {
                    "summary": "excludedLists not an array",
                    "value": {
                      "error": "excludedLists must be an array"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/identities": {
      "patch": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "updateCampaignIdentities",
        "summary": "Set the sender identities",
        "description": "Replaces the identities (sending accounts) of the campaign. Not allowed while RUNNING. For each step Emelia picks automatically, among these identities, the least used one that has an account on the channel. Rules checked at start: every identity needs an email provider if the sequence has an email step; with a LinkedIn step either use a single LinkedIn account or attach one to every identity.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "identities"
                ],
                "properties": {
                  "identities": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/IdentityInput"
                    }
                  }
                }
              },
              "example": {
                "identities": [
                  {
                    "name": "Marie Martin",
                    "email": "6650f1a2b839d80011c5a0aa",
                    "linkedin": "6650f1a2b839d80011c5a0bb"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/Campaign"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d80011c5a0f1",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "tagIds": [
                      "6650f1a2b839d80011c5a0f1"
                    ],
                    "identities": [
                      {
                        "_id": "6650f1a2b839d80011c5a0f1",
                        "userId": "6650f1a2b839d80011c5a0f1",
                        "name": "Marie Martin",
                        "email": "6650f1a2b839d80011c5a0f1",
                        "linkedin": "6650f1a2b839d80011c5a0f1",
                        "whatsapp": "6650f1a2b839d80011c5a0f1"
                      }
                    ],
                    "schedule": {
                      "dailyEmailAdded": 35,
                      "dailyEmailLimit": 100,
                      "dailyLinkedinAdded": 20,
                      "dailyWhatsappAdded": 20,
                      "bcc": "",
                      "trackLinks": false,
                      "trackOpens": false,
                      "timeZone": "Europe/Brussels",
                      "days": [
                        0,
                        1,
                        2,
                        3,
                        4
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "blacklistUnsub": false,
                      "excludeAlreadyMessaged": true,
                      "eventToStop": [
                        "VISITED"
                      ],
                      "alwaysOn": false,
                      "ignoreAutoReplies": false
                    },
                    "steps": {
                      "_id": "6650f1a2b839d800a4e02834",
                      "stepType": "START",
                      "delay": {
                        "amount": 1,
                        "unit": "MINUTES"
                      },
                      "identities": [
                        "6650f1a2b839d80044baeb18"
                      ],
                      "versions": [
                        {
                          "_id": "6650f1a2b839d80013570f83",
                          "subject": "Quick question about Nexora",
                          "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                          "disabled": false,
                          "rawHtml": true,
                          "attachments": [
                            {
                              "mediaType": "MEDIA"
                            }
                          ]
                        }
                      ],
                      "cc": "",
                      "conditions": {
                        "field": "MAIL_REPLIED",
                        "operator": "EQUAL"
                      },
                      "next": {
                        "_id": "6650f1a2b839d800a25c662b",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d8007164273b"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800e3cff90e",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d8000c8b40ee",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800c97cfdea"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8000e5ac9e9",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8005883bad3",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d80061d63f27",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d800e67c4eaa",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800738d2716"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800a9299d9d",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8009dcb7b8f",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d80015dca463",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d8003f5b7624",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d80031d36b08"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8004f4341bb",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800e426b271",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d80045c41fd5",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      },
                      "yes": {
                        "_id": "6650f1a2b839d8006176ac81",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d800688e2e89"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d8003873fb5c",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d800f6c87010",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800fa504e04"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d8001e6fbabf",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8001acbc61d",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800d0a9c1e1",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d800c23685b0",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800606da324"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800d934ec5f",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800dcd064bd",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800f5c16001",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d800950dae32",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800711678de"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800d24726a5",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800895e1257",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800590ca2cb",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      },
                      "no": {
                        "_id": "6650f1a2b839d8006b649885",
                        "stepType": "START",
                        "delay": {
                          "amount": 1,
                          "unit": "MINUTES"
                        },
                        "identities": [
                          "6650f1a2b839d800d2be6fad"
                        ],
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800c7040c20",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": []
                          }
                        ],
                        "cc": "",
                        "conditions": {
                          "field": "MAIL_REPLIED",
                          "operator": "EQUAL"
                        },
                        "next": {
                          "_id": "6650f1a2b839d8003c66838c",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d800223b38d0"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d80041903763",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d800d9b8ce99",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d800fdcd421d",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "yes": {
                          "_id": "6650f1a2b839d8008390e2f4",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d8007f9038d8"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800caf55a6b",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8001f7d0ac1",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d80009d680c5",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        },
                        "no": {
                          "_id": "6650f1a2b839d8006e1f3416",
                          "stepType": "START",
                          "delay": {
                            "amount": 1,
                            "unit": "MINUTES"
                          },
                          "identities": [
                            "6650f1a2b839d80068072332"
                          ],
                          "versions": [
                            {}
                          ],
                          "cc": "",
                          "conditions": {
                            "field": "MAIL_REPLIED",
                            "operator": "EQUAL"
                          },
                          "next": {
                            "_id": "6650f1a2b839d800aa7d5d31",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "yes": {
                            "_id": "6650f1a2b839d8006e15b9fb",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          },
                          "no": {
                            "_id": "6650f1a2b839d80076a1078f",
                            "stepType": "START",
                            "delay": {
                              "unit": "MINUTES"
                            },
                            "identities": [],
                            "versions": [],
                            "cc": "",
                            "conditions": {
                              "operator": "EQUAL"
                            },
                            "next": {
                              "stepType": "START"
                            },
                            "yes": {
                              "stepType": "START"
                            },
                            "no": {
                              "stepType": "START"
                            }
                          }
                        }
                      }
                    },
                    "recipients": {
                      "lists": [
                        {
                          "_id": "6650f1a2b839d80011c5a0f1",
                          "name": "Q4 outreach: SaaS founders",
                          "contactCount": 1
                        }
                      ],
                      "excludedLists": [
                        {
                          "_id": "6650f1a2b839d80011c5a0f1",
                          "name": "Q4 outreach: SaaS founders",
                          "contactCount": 1
                        }
                      ],
                      "processing": true
                    },
                    "zapierData": [
                      {
                        "_id": "6650f1a2b839d80011c5a0f1",
                        "webhookUrl": "https://hooks.yourcompany.com/emelia",
                        "events": []
                      }
                    ],
                    "hubspotIntegration": {},
                    "pipedriveIntegration": {},
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "running": {
                    "summary": "Running campaign",
                    "value": {
                      "error": "You must pause your campaign before updating it"
                    }
                  },
                  "validation": {
                    "summary": "Validation error",
                    "value": {
                      "error": "\"identities[0].name\" is required",
                      "code": "INVALID_REQUEST"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/start": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "startCampaign",
        "summary": "Start a campaign",
        "description": "Starts (or resumes) the campaign. Requires an active subscription. All start checks are run and the first failing one is returned with a stable `code` (see the examples and the ErrorCode schema): identities, accounts connected, email versions with content and subject, LinkedIn daily ceiling, 1 to 30,000 recipients, at least one contact reachable on each channel used. A planned start, if any, is cancelled. Scheduling of contacts happens asynchronously after the response.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The campaign cannot be started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "subscription": {
                    "summary": "No active subscription",
                    "value": {
                      "error": "You must have a subscription to start a campaign",
                      "code": "SUBSCRIPTION_REQUIRED"
                    }
                  },
                  "noIdentity": {
                    "summary": "No identity",
                    "value": {
                      "error": "You must add an Identity to start the campaign",
                      "code": "NO_IDENTITY_SET"
                    }
                  },
                  "noRecipients": {
                    "summary": "No recipients",
                    "value": {
                      "error": "You must have at least one recipient to start campaign",
                      "code": "NO_RECIPIENTS"
                    }
                  },
                  "tooMany": {
                    "summary": "More than 30,000 recipients",
                    "value": {
                      "error": "You cannot have more than 30,000 recipients in your campaign",
                      "code": "TOO_MANY_RECIPIENTS",
                      "params": {
                        "max": 30000
                      }
                    }
                  },
                  "noSubject": {
                    "summary": "First email without subject",
                    "value": {
                      "error": "Version A of your first email step has no subject",
                      "code": "EMAIL_VERSION_NO_SUBJECT",
                      "params": {
                        "version": "A"
                      }
                    }
                  },
                  "noEmail": {
                    "summary": "Email step but no contact with email",
                    "value": {
                      "error": "Your campaign has an email step but none of your contacts has an email address. Please check the column mapping of your list",
                      "code": "RECIPIENTS_NO_EMAIL"
                    }
                  },
                  "running": {
                    "summary": "Already running",
                    "value": {
                      "error": "Campaign is already running",
                      "code": "CAMPAIGN_ALREADY_RUNNING"
                    }
                  },
                  "notFound": {
                    "summary": "Not found",
                    "value": {
                      "error": "This campaign could not be found",
                      "code": "CAMPAIGN_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/pause": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "pauseCampaign",
        "summary": "Pause a campaign",
        "description": "Pauses a RUNNING campaign: its scheduled actions, pending conditions and pending manual tasks are deleted. They are created again when the campaign is restarted and the contacts reach those steps again.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notRunning": {
                    "summary": "Not running",
                    "value": {
                      "error": "This campaign must be in RUNNING status to pause it"
                    }
                  },
                  "notFound": {
                    "summary": "Not found",
                    "value": {
                      "error": "This campaign could not be found"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/planned-start": {
      "patch": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "scheduleCampaignStart",
        "summary": "Schedule or cancel a planned start",
        "description": "Schedules an automatic start at `plannedStart` (ISO 8601 date in the future). The campaign must pass the start checks at scheduling time (same errors as `POST .../start`). Send `null` (or an empty value) to cancel a planned start.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "plannedStart": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  }
                }
              },
              "example": {
                "plannedStart": "2026-11-03T08:00:00.000Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "plannedStart": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "plannedStart": "2026-11-03T08:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid": {
                    "summary": "Invalid date",
                    "value": {
                      "error": "Invalid plannedStart date",
                      "code": "PLANNED_START_INVALID"
                    }
                  },
                  "past": {
                    "summary": "Date in the past",
                    "value": {
                      "error": "The planned start must be in the future",
                      "code": "PLANNED_START_IN_PAST"
                    }
                  },
                  "noIdentity": {
                    "summary": "No identity",
                    "value": {
                      "error": "You must add an Identity to start the campaign",
                      "code": "NO_IDENTITY_SET"
                    }
                  },
                  "noRecipients": {
                    "summary": "No recipients",
                    "value": {
                      "error": "You must have at least one recipient to start campaign",
                      "code": "NO_RECIPIENTS"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/duplicate": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "duplicateCampaign",
        "summary": "Duplicate a campaign",
        "description": "Creates a new DRAFT campaign from an existing one. Choose what to copy. Copied contacts and lists are shared references (not copies). The new name must be unique.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "copySettings": {
                    "type": "boolean",
                    "default": false
                  },
                  "copySteps": {
                    "type": "boolean",
                    "default": false
                  },
                  "copyContacts": {
                    "type": "boolean",
                    "default": false
                  },
                  "copyIdentities": {
                    "type": "boolean",
                    "default": false
                  }
                }
              },
              "example": {
                "name": "Acme Q4 outreach (copy)",
                "copySettings": true,
                "copySteps": true,
                "copyContacts": false,
                "copyIdentities": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "campaignId": {
                      "$ref": "#/components/schemas/ObjectId"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "campaignId": "6650f1a2b839d80011c5a0f2"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "duplicate": {
                    "summary": "Name already used",
                    "value": {
                      "error": "A campaign with this name already exists on your account"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/archive": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "archiveCampaign",
        "summary": "Archive a campaign",
        "description": "Sets the status to ARCHIVED (hidden from `GET /advanced/campaigns`). A running campaign must be paused first.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "running": {
                    "summary": "Campaign running (message in French)",
                    "value": {
                      "error": "Vous devez mettre la campagne en pause avant de pouvoir l'archiver"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/reactivate": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "reactivateCampaign",
        "summary": "Reactivate a finished campaign",
        "description": "Moves a FINISHED campaign back to PAUSED so that it can be edited and started again.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFinished": {
                    "summary": "Not finished",
                    "value": {
                      "error": "Only a finished campaign can be reactivated"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/finish": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "finishCampaign",
        "summary": "Mark a campaign as finished",
        "description": "Moves a RUNNING or PAUSED campaign to FINISHED and deletes its scheduled actions and pending conditions.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "wrongStatus": {
                    "summary": "Wrong status",
                    "value": {
                      "error": "Only a running or paused campaign can be marked as finished"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/test-email": {
      "post": {
        "tags": [
          "Campaigns"
        ],
        "operationId": "sendCampaignTestEmail",
        "summary": "Send a test email",
        "description": "Sends one version of an EMAIL step to `email`, from one of the campaign identities, with the variables of a random contact of the campaign. Use the step `_id` and version `_id` from `GET /advanced/campaigns/{campaignId}`.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "stepId",
                  "versionId",
                  "email"
                ],
                "properties": {
                  "stepId": {
                    "type": "string"
                  },
                  "versionId": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Recipient of the test."
                  }
                }
              },
              "example": {
                "stepId": "6650f1a2b839d80011c5c001",
                "versionId": "6650f1a2b839d80011c5c002",
                "email": "john.doe@acme.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or sending failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "step": {
                    "summary": "Unknown step",
                    "value": {
                      "error": "Step not found"
                    }
                  },
                  "version": {
                    "summary": "Unknown version",
                    "value": {
                      "error": "Version not found"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaign/contacts": {
      "post": {
        "tags": [
          "Campaign contacts"
        ],
        "operationId": "addContactToCampaign",
        "summary": "Add a contact to a campaign",
        "description": "Adds ONE contact to a multichannel campaign. The contact is stored in a list named \"<campaign name> contacts from API\" (created if needed) attached to the campaign. If the campaign is FINISHED it is started again automatically (all start checks apply); if it is RUNNING the contact is scheduled immediately. A contact already present in that list (same email or LinkedIn URL) is rejected. For many contacts, use the Lists endpoints and `PATCH .../recipients` instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "contact"
                ],
                "properties": {
                  "id": {
                    "description": "Campaign id.",
                    "$ref": "#/components/schemas/ObjectId"
                  },
                  "contact": {
                    "$ref": "#/components/schemas/ContactInput"
                  }
                }
              },
              "example": {
                "id": "6650f1a2b839d80011c5a0f1",
                "contact": {
                  "email": "john.doe@acme.com",
                  "firstName": "John",
                  "lastName": "Doe",
                  "linkedinUrlProfile": "https://www.linkedin.com/in/johndoe",
                  "companyName": "Acme",
                  "jobTitle": "Head of Sales"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "contactId": {
                      "$ref": "#/components/schemas/ObjectId"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "contactId": "6650f1a2b839d80011c5d001"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "noKey": {
                    "summary": "Neither email nor LinkedIn",
                    "value": {
                      "error": "Invalid contact: need either email or LinkedIn profile"
                    }
                  },
                  "badEmail": {
                    "summary": "Bad email",
                    "value": {
                      "error": "Invalid email format"
                    }
                  },
                  "badLinkedin": {
                    "summary": "Bad LinkedIn URL",
                    "value": {
                      "error": "Invalid LinkedIn profile URL format"
                    }
                  },
                  "duplicate": {
                    "summary": "Already in the campaign",
                    "value": {
                      "error": "This contact is already included in this campaign"
                    }
                  },
                  "notString": {
                    "summary": "A value is not a string",
                    "value": {
                      "error": "(intermediate value).trim is not a function"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      },
      "get": {
        "tags": [
          "Campaign contacts"
        ],
        "operationId": "listCampaignContacts",
        "summary": "List the contacts of a campaign",
        "description": "Paginated contacts of a campaign with their status in the campaign. `page` is ZERO based. `perPage` below 10 is replaced by 20; maximum 10,000.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "description": "Campaign id."
          },
          {
            "name": "page",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Zero based page index.",
            "example": 0
          },
          {
            "name": "perPage",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 10,
              "maximum": 10000,
              "default": 20
            }
          },
          {
            "name": "event",
            "in": "query",
            "required": false,
            "schema": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              ]
            },
            "description": "Filter on contact status (for example `SENT`, `MAIL_REPLIED`; `NO_ACTIVITY` = no activity yet). Send it as an array: `event[]=SENT` or repeat the parameter. A single plain `event=SENT` is accepted by validation but currently fails with a 400 error."
          },
          {
            "name": "query",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Exact email, or words matched against email, names, phone and LinkedIn URL."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "list": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CampaignContact"
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "_id": {
                      "type": "string",
                      "description": "Campaign id."
                    }
                  }
                },
                "example": {
                  "success": true,
                  "list": [
                    {
                      "_id": "6650f1a2b839d80011c5a0f1",
                      "email": "claire.fontaine@nexora.io",
                      "firstName": "Claire",
                      "lastName": "Fontaine",
                      "phone": "+33 6 12 34 56 78",
                      "linkedinUrlProfile": "https://www.linkedin.com/in/claire-fontaine",
                      "status": "SENT",
                      "lastContacted": "2026-10-06T09:30:00.000Z",
                      "lastReplied": "2026-10-06T09:30:00.000Z",
                      "integrationContactId": "6650f1a2b839d800ba9811eb",
                      "sentiment": "INTERESTED",
                      "custom": {}
                    }
                  ],
                  "count": 250,
                  "_id": "6650f1a2b839d800dcab1309"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Campaign contacts"
        ],
        "operationId": "removeContactFromCampaign",
        "summary": "Remove a contact from a campaign",
        "description": "Removes one contact from the campaign recipients and deletes its activities in this campaign (in the background). The contact itself and its lists are kept. The parameters are sent in the JSON BODY of the DELETE request.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "contactId"
                ],
                "properties": {
                  "id": {
                    "description": "Campaign id.",
                    "$ref": "#/components/schemas/ObjectId"
                  },
                  "contactId": {
                    "$ref": "#/components/schemas/ObjectId"
                  }
                }
              },
              "example": {
                "id": "6650f1a2b839d80011c5a0f1",
                "contactId": "6650f1a2b839d80011c5d001"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/contacts": {
      "patch": {
        "tags": [
          "Campaign contacts"
        ],
        "operationId": "setCampaignContactField",
        "summary": "Set a field on a campaign contact",
        "description": "Sets one field of a contact that belongs to the campaign, found by `email` OR `linkedinUrlProfile` (exactly one of them). Spaces are removed from `fieldName`. If `fieldName` is a native field of the contact (for example `firstName`, `jobTitle`, `companyName`) it is overwritten, otherwise a custom variable is created or updated.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "campaignId",
                  "fieldName",
                  "fieldValue"
                ],
                "properties": {
                  "campaignId": {
                    "$ref": "#/components/schemas/ObjectId"
                  },
                  "email": {
                    "type": "string"
                  },
                  "linkedinUrlProfile": {
                    "type": "string"
                  },
                  "fieldName": {
                    "type": "string"
                  },
                  "fieldValue": {
                    "type": "string"
                  }
                },
                "oneOf": [
                  {
                    "required": [
                      "email"
                    ]
                  },
                  {
                    "required": [
                      "linkedinUrlProfile"
                    ]
                  }
                ]
              },
              "example": {
                "campaignId": "6650f1a2b839d80011c5a0f1",
                "email": "john.doe@acme.com",
                "fieldName": "meetingLink",
                "fieldValue": "https://cal.example.com/john"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "xor": {
                    "summary": "Both or none of email and LinkedIn",
                    "value": {
                      "error": "\"value\" must contain at least one of [email, linkedinUrlProfile]",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "notFound": {
                    "summary": "Contact not in this campaign",
                    "value": {
                      "error": "Campaign not found"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/activities": {
      "get": {
        "tags": [
          "Campaign tracking"
        ],
        "operationId": "listCampaignActivities",
        "summary": "List campaign activities",
        "description": "Activities of the campaign, newest first, 30 per page. `page` is ZERO based; an empty array means you reached the end. Results are cached for up to 10 minutes. Filters: `contactId` wins over `search`, which wins over `query`. When `query` is an exact email, the type filters are ignored.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[0-9]+$",
              "default": "0"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/ActivityEvent"
            },
            "description": "Single event filter. `MESSAGE_SENT` also returns `INMAIL_SENT`."
          },
          {
            "name": "types",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[A-Z_]+(,[A-Z_]+)*$"
            },
            "description": "Comma separated events, for example `MAIL_REPLIED,LINKEDIN_REPLIED`. Wins over `type`."
          },
          {
            "name": "contactId",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            }
          },
          {
            "name": "versionId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Search contacts by name, email or LinkedIn URL."
          },
          {
            "name": "query",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Legacy filter: an exact email address."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "activities": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Activity"
                      }
                    }
                  }
                },
                "example": {
                  "activities": [
                    {
                      "_id": "6650f1a2b839d80011c5e001",
                      "contact": {
                        "_id": "6650f1a2b839d80011c5d001",
                        "email": "john.doe@acme.com",
                        "firstName": "John",
                        "lastName": "Doe"
                      },
                      "event": "MAIL_REPLIED",
                      "date": "2026-10-08T09:12:00.000Z",
                      "customData": {},
                      "step": 2,
                      "version": 0,
                      "reply": {
                        "content": "<p>Sounds good, call me Tuesday.</p>",
                        "text": "Sounds good, call me Tuesday.",
                        "subject": "Re: Quick question, John",
                        "senderName": "John Doe"
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "summary": "Not found",
                    "value": {
                      "error": "Campaign not found"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/activities/{activityId}/message": {
      "get": {
        "tags": [
          "Campaign tracking"
        ],
        "operationId": "getActivityMessage",
        "summary": "Get the message of a send activity",
        "description": "Rebuilds the content sent for a `SENT`, `MESSAGE_SENT`, `INMAIL_SENT`, `INVITED` or `WHATSAPP_MESSAGE_SENT` activity by replaying the step template against the contact. The sent content itself is not stored: the result can differ if the step or the contact changed since. Errors use a different format here: `{\"error\": \"NOT_FOUND\"}` (404) or `{\"error\": \"NO_MESSAGE\"}` (422).",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          },
          {
            "name": "activityId",
            "in": "path",
            "required": true,
            "description": "Activity id.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5e001"
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "channel": {
                      "type": "string",
                      "enum": [
                        "EMAIL",
                        "LINKEDIN",
                        "WHATSAPP"
                      ]
                    },
                    "subject": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "message": {
                      "type": "string"
                    },
                    "attachments": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "mediaType": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "channel": "EMAIL",
                  "subject": "Quick question, John",
                  "message": "<p>Hi John,</p>",
                  "attachments": []
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Unknown activity, campaign, step or contact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "NOT_FOUND"
                }
              }
            }
          },
          "422": {
            "description": "The activity is not a send event.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "NO_MESSAGE"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/manual-tasks": {
      "get": {
        "tags": [
          "Campaign tracking"
        ],
        "operationId": "listManualTasks",
        "summary": "List pending manual tasks",
        "description": "Pending TASK steps of the campaign (contacts waiting for you to do something by hand), oldest first. Pausing the campaign deletes pending tasks; they are created again after the next start, when contacts reach the task step again.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          },
          {
            "name": "stepId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only the tasks of this TASK step."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "tasks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ManualTask"
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "tasks": [
                    {
                      "_id": "6650f1a2b839d80011c5a0f1",
                      "firstName": "Claire",
                      "lastName": "Fontaine",
                      "email": "claire.fontaine@nexora.io",
                      "linkedinUrlProfile": "https://www.linkedin.com/in/claire-fontaine",
                      "taskId": "6650f1a2b839d800b174ee18",
                      "taskName": "Call Claire",
                      "date": "2026-10-06T09:30:00.000Z",
                      "multipleChoice": true
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "summary": "Not found",
                    "value": {
                      "error": "Campaign not found"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/manual-tasks/status": {
      "post": {
        "tags": [
          "Campaign tracking"
        ],
        "operationId": "setManualTaskStatus",
        "summary": "Complete a manual task",
        "description": "Marks the task of one contact as done (`ok`, event TASK_COMPLETED) or failed (`ko`, event TASK_FAILED) and moves the contact to the next step. A task can only be completed once.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "contactId",
                  "taskId",
                  "status"
                ],
                "properties": {
                  "contactId": {
                    "$ref": "#/components/schemas/ObjectId"
                  },
                  "taskId": {
                    "type": "string",
                    "description": "`taskId` from the list of tasks."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "ok",
                      "ko"
                    ]
                  }
                }
              },
              "example": {
                "contactId": "6650f1a2b839d80011c5d001",
                "taskId": "6650f1a2b839d80011c5c010",
                "status": "ok"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "done": {
                    "summary": "Already completed",
                    "value": {
                      "error": "Task already completed"
                    }
                  },
                  "status": {
                    "summary": "Bad status",
                    "value": {
                      "error": "Status must be \"ok\" or \"ko\""
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/progress": {
      "get": {
        "tags": [
          "Campaign tracking"
        ],
        "operationId": "getCampaignProgress",
        "summary": "Get per step progress",
        "description": "For each step id: how many contacts already passed the step (`completed`) and how many are currently waiting at it (`waiting`). Works on paused campaigns. Returns `{}` for a DRAFT or unknown campaign.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "progress": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "object",
                        "properties": {
                          "completed": {
                            "type": "integer"
                          },
                          "waiting": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "progress": {
                    "6650f1a2b839d80011c5c001": {
                      "completed": 120,
                      "waiting": 35
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/progress/contacts": {
      "get": {
        "tags": [
          "Campaign tracking"
        ],
        "operationId": "listStepContacts",
        "summary": "List the contacts of a step",
        "description": "Contacts that passed (`completed`) or wait at (`waiting`) a given step, newest first, 20 per page. Here `page` is ONE based.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          },
          {
            "name": "stepId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "65f1a2b3c4d5e6f7ec005b62"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "completed",
                "waiting"
              ],
              "default": "all"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "contacts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "contactId": {
                            "type": "string"
                          },
                          "firstName": {
                            "type": "string"
                          },
                          "lastName": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string"
                          },
                          "linkedinUrlProfile": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "completed",
                              "waiting"
                            ]
                          },
                          "date": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "totalPages": {
                      "type": "integer"
                    },
                    "completedTotal": {
                      "type": "integer"
                    },
                    "waitingTotal": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "contacts": [
                    {
                      "contactId": "6650f1a2b839d8001ee1c24d",
                      "firstName": "Claire",
                      "lastName": "Fontaine",
                      "email": "claire.fontaine@nexora.io",
                      "linkedinUrlProfile": "https://www.linkedin.com/in/claire-fontaine",
                      "status": "completed",
                      "date": "2026-10-06T09:30:00.000Z"
                    }
                  ],
                  "total": 250,
                  "totalPages": 13,
                  "completedTotal": 180,
                  "waitingTotal": 70
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "step": {
                    "summary": "Missing stepId",
                    "value": {
                      "error": "Step ID is required"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/campaigns/{campaignId}/export": {
      "get": {
        "tags": [
          "Campaign tracking"
        ],
        "operationId": "exportCampaign",
        "summary": "Export campaign data as CSV",
        "description": "Returns a CSV file (UTF-8 with BOM, `Content-Disposition: attachment`). `contacts`: one line per contact with its custom fields, activities and replied flag; `activities`: activity log; `replies`: replies. Optional date range on activities.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "description": "Identifier of a multichannel campaign (24 hexadecimal characters), as returned by `POST /advanced/campaigns` or `GET /advanced/campaigns`.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0f1"
          },
          {
            "name": "type",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "contacts",
                "activities",
                "replies"
              ]
            }
          },
          {
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV file.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/stats/advanced": {
      "get": {
        "tags": [
          "Campaign tracking"
        ],
        "operationId": "getCampaignStats",
        "summary": "Get campaign statistics",
        "description": "Global and per step statistics of a multichannel campaign. Computed on demand (can take several seconds) and then cached; `Cache-Control: max-age=300` is set when `detailed` is not `true`. Errors are unusual here: an unknown campaign or ANY error (including an invalid parameter) returns 401, and a failure of the statistics engine can return HTTP 200 with `{\"error\": \"Error\"}`. For a few accounts a 503 `STATS_COMPUTING` with `Retry-After: 30` is returned while a computation is in progress.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "query",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            }
          },
          {
            "name": "detailed",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "For multichannel campaigns the shape is always `{global, steps}`."
          },
          {
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "providerId",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "description": "Restrict to one email provider."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/CampaignStats"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "global": {
                    "contacts": 250,
                    "contacted": 240,
                    "replied": 18,
                    "visited": 120,
                    "invited": 100,
                    "accepted": 41,
                    "message_sent": 38,
                    "inmail_sent": 0,
                    "linkedin_replied": 9,
                    "mail_replied": 9,
                    "liked": 30,
                    "sent": 240,
                    "opened": 96,
                    "clicked": 21,
                    "bounced": 3,
                    "unsubscribed": 2,
                    "progress_percent": 96,
                    "accepted_percent": 41,
                    "opened_percent": 40,
                    "clicked_percent": 8.75,
                    "linkedin_replied_percent": 9,
                    "mail_replied_percent": 3.75,
                    "bounced_percent": 1.25,
                    "unsubscribed_percent": 0.83
                  },
                  "steps": [
                    [
                      {
                        "stepType": "EMAIL"
                      }
                    ]
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Unknown campaign, invalid parameter or any other error (all mapped to 401).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "value": {
                      "error": "Campaign not found"
                    }
                  },
                  "other": {
                    "value": {
                      "error": "You must be logged in"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          },
          "503": {
            "description": "Statistics already being computed (only for some accounts).",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "30"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Statistics are being computed, please try again in a few moments",
                  "code": "STATS_COMPUTING"
                }
              }
            }
          }
        }
      }
    },
    "/advanced/attachments": {
      "post": {
        "tags": [
          "Uploads"
        ],
        "operationId": "uploadAttachments",
        "summary": "Upload message attachments",
        "description": "Uploads 1 to 3 files (20 MB maximum each) and returns their URLs, to reference in `versions[].attachments` of EMAIL, MESSAGE or WHATSAPP steps. Files are stored as is with a PUBLIC URL: anyone with the URL can download them.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "attachments"
                ],
                "properties": {
                  "attachments": {
                    "type": "array",
                    "maxItems": 3,
                    "items": {
                      "type": "string",
                      "format": "binary"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "attachments": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "name": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "attachments": [
                    {
                      "url": "https://eml-public.s3.eu-west-3.amazonaws.com/linkedin/attachments/6650f1a2b839d80011c5a001/1760000000000_ab12cd34ef56ab78_brochure.pdf",
                      "name": "brochure.pdf"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "none": {
                    "summary": "No file",
                    "value": {
                      "error": "At least one file is required"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/advanced/linkedin/audio": {
      "post": {
        "tags": [
          "Uploads"
        ],
        "operationId": "uploadLinkedinAudio",
        "summary": "Upload a LinkedIn voice note",
        "description": "Converts an audio file (webm expected, 10 MB maximum, 5 minutes maximum) to m4a and returns a PUBLIC URL plus its duration IN MILLISECONDS, to use as an attachment with `mediaType: \"AUDIO\"` in a MESSAGE step.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "audio"
                ],
                "properties": {
                  "audio": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "audio": {
                      "type": "object",
                      "properties": {
                        "duration": {
                          "type": "integer",
                          "description": "Milliseconds."
                        },
                        "url": {
                          "type": "string",
                          "format": "uri"
                        },
                        "name": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "audio": {
                    "duration": 32000,
                    "url": "https://eml-public.s3.eu-west-3.amazonaws.com/linkedin/audio/1760000000000_voice-note.m4a",
                    "name": "voice-note.m4a"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "tooLong": {
                    "summary": "Longer than 5 minutes",
                    "value": {
                      "error": "Failed to process audio: Audio duration is too long, it cannot exceed 5 minutes"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/tools/find/email": {
      "post": {
        "tags": [
          "Enrichment tools"
        ],
        "operationId": "findEmail",
        "summary": "Find the email of a person",
        "description": "Starts an email search for one person and returns a `jobId` immediately. Cost: 1 credit, debited when the job starts and refunded when no email is found (it is NOT refunded when the search times out). The search runs in two passes when `companyWebsite` is given and finds nothing: a second pass with the company name only. Poll `GET /tools/find/email/{jobId}` every 3 to 5 seconds until `status` is `done`; a search can take up to about 2 minutes per pass.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "fullname",
                  "companyName",
                  "country"
                ],
                "properties": {
                  "fullname": {
                    "type": "string",
                    "description": "First and last name."
                  },
                  "companyName": {
                    "type": "string"
                  },
                  "companyWebsite": {
                    "type": "string",
                    "description": "Company domain or website. Strongly improves results."
                  },
                  "country": {
                    "type": "string",
                    "description": "REQUIRED. Country code of the person, ISO 3166-1 alpha-2 (for example `FR`), passed to the provider as `country_code`."
                  }
                }
              },
              "example": {
                "fullname": "John Doe",
                "companyName": "Acme",
                "companyWebsite": "acme.com",
                "country": "FR"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "jobId": {
                      "$ref": "#/components/schemas/ObjectId"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "jobId": "6650f1a2b839d80011c5f001"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "country": {
                    "summary": "country missing",
                    "value": {
                      "error": "\"country\" is required",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "credits": {
                    "summary": "Not enough credits",
                    "value": {
                      "error": "You do not have enough credits to perform this action"
                    }
                  },
                  "provider": {
                    "summary": "Provider failure (credit refunded)",
                    "value": {
                      "error": "Error while submitting Email Verifier. Please contact support"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "get": {
        "tags": [
          "Enrichment tools"
        ],
        "operationId": "listFindEmailJobs",
        "summary": "List email searches",
        "description": "History of your single email searches, newest first. `page` is one based.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 15
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EmailFinderJob"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "_id": "6650f1a2b839d80011c5a0f1",
                      "fullname": "Claire Fontaine",
                      "companyName": "Nexora",
                      "companyWebsite": "nexora.io",
                      "status": "running",
                      "email": "claire.fontaine@nexora.io",
                      "qualification": "valid",
                      "fallbackFound": true,
                      "date": "2026-10-06T09:30:00.000Z"
                    }
                  ],
                  "total": 250,
                  "page": 1,
                  "pageSize": 20
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/tools/find/email/{jobId}": {
      "get": {
        "tags": [
          "Enrichment tools"
        ],
        "operationId": "getFindEmailJob",
        "summary": "Get an email search result",
        "description": "Returns the job. While `status` is `running`, poll again. When `done`, `email` holds the result (absent if nothing was found).",
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "description": "The `jobId` returned by the POST route.",
            "example": "6650f1a2b839d80011c5f001"
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailFinderJob"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "_id": "6650f1a2b839d80011c5f001",
                    "fullname": "John Doe",
                    "companyName": "Acme",
                    "companyWebsite": "acme.com",
                    "status": "done",
                    "email": "john.doe@acme.com",
                    "qualification": "valid",
                    "fallbackFound": false,
                    "date": "2026-10-11T08:00:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "summary": "Not found",
                    "value": {
                      "error": "Job not found"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/tools/find/phone": {
      "post": {
        "tags": [
          "Enrichment tools"
        ],
        "operationId": "findPhone",
        "summary": "Find the phone number of a person",
        "description": "Starts a mobile phone search from a LinkedIn profile URL and returns a `jobId`. Cost: 50 credits, debited when the job starts and refunded when no number is found (results found during the last 6 months are served from cache and still cost 50 credits). The result arrives asynchronously from the provider: poll `GET /tools/find/phone/{jobId}` until `status` is `done`; there is no fixed upper bound on the wait.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "linkedinUrl"
                ],
                "properties": {
                  "linkedinUrl": {
                    "type": "string",
                    "description": "Must contain `linkedin.com/in/`."
                  }
                }
              },
              "example": {
                "linkedinUrl": "https://www.linkedin.com/in/johndoe"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "jobId": {
                      "$ref": "#/components/schemas/ObjectId"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "jobId": "6650f1a2b839d80011c5f002"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "url": {
                    "summary": "Not a profile URL",
                    "value": {
                      "error": "Invalid LinkedIn URL"
                    }
                  },
                  "credits": {
                    "summary": "Not enough credits",
                    "value": {
                      "error": "You do not have enough credits to perform this action"
                    }
                  },
                  "provider": {
                    "summary": "Provider failure (credits refunded)",
                    "value": {
                      "error": "Error while submitting Phone Finder. Please contact support"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "get": {
        "tags": [
          "Enrichment tools"
        ],
        "operationId": "listFindPhoneJobs",
        "summary": "List phone searches",
        "description": "History of your single phone searches, newest first. `page` is one based.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 15
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PhoneFinderJob"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "_id": "6650f1a2b839d80011c5a0f1",
                      "linkedinUrl": "https://www.linkedin.com/in/claire-fontaine",
                      "status": "running",
                      "qualification": "found",
                      "phoneNumber": "+33 6 12 34 56 78",
                      "country": "FR",
                      "date": "2026-10-06T09:30:00.000Z"
                    }
                  ],
                  "total": 250,
                  "page": 1,
                  "pageSize": 20
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/tools/find/phone/{jobId}": {
      "get": {
        "tags": [
          "Enrichment tools"
        ],
        "operationId": "getFindPhoneJob",
        "summary": "Get a phone search result",
        "description": "Returns the job. `qualification` is `found` and `phoneNumber` is set when a number was found.",
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "description": "The `jobId` returned by the POST route.",
            "example": "6650f1a2b839d80011c5f001"
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/PhoneFinderJob"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "_id": "6650f1a2b839d80011c5f002",
                    "linkedinUrl": "https://www.linkedin.com/in/johndoe",
                    "status": "done",
                    "qualification": "found",
                    "phoneNumber": "+33612345678",
                    "country": "FR",
                    "date": "2026-10-11T08:00:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "summary": "Not found",
                    "value": {
                      "error": "Job not found"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/tools/verify/email": {
      "post": {
        "tags": [
          "Enrichment tools"
        ],
        "operationId": "verifyEmail",
        "summary": "Verify an email address",
        "description": "Starts the verification of one email address and returns a `jobId`. Cost: 0.25 credit, never refunded (even when the provider fails). Poll `GET /tools/verify/email/{jobId}` every few seconds; the job is marked `error` after about 60 seconds without a verdict.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              },
              "example": {
                "email": "john.doe@acme.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "jobId": {
                      "$ref": "#/components/schemas/ObjectId"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "jobId": "6650f1a2b839d80011c5f003"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missing": {
                    "summary": "Missing email",
                    "value": {
                      "error": "Email is required"
                    }
                  },
                  "invalid": {
                    "summary": "Invalid email",
                    "value": {
                      "error": "Invalid email"
                    }
                  },
                  "credits": {
                    "summary": "Not enough credits",
                    "value": {
                      "error": "You do not have enough credits to perform this action"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "get": {
        "tags": [
          "Enrichment tools"
        ],
        "operationId": "listVerifyEmailJobs",
        "summary": "List email verifications",
        "description": "History of your single verifications, newest first. `page` is one based.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 15
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EmailVerifierJob"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "_id": "6650f1a2b839d80011c5a0f1",
                      "email": "claire.fontaine@nexora.io",
                      "status": "running",
                      "qualification": "valid",
                      "date": "2026-10-06T09:30:00.000Z"
                    }
                  ],
                  "total": 250,
                  "page": 1,
                  "pageSize": 20
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/tools/verify/email/{jobId}": {
      "get": {
        "tags": [
          "Enrichment tools"
        ],
        "operationId": "getVerifyEmailJob",
        "summary": "Get an email verification result",
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "description": "The `jobId` returned by the POST route.",
            "example": "6650f1a2b839d80011c5f001"
          }
        ],
        "description": "Returns the job with its `qualification` once `status` is `done`.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailVerifierJob"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "_id": "6650f1a2b839d80011c5f003",
                    "email": "john.doe@acme.com",
                    "status": "done",
                    "qualification": "valid",
                    "date": "2026-10-11T08:00:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "summary": "Not found",
                    "value": {
                      "error": "Job not found"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/email-providers": {
      "get": {
        "tags": [
          "Email providers"
        ],
        "operationId": "listEmailProviders",
        "summary": "List email providers",
        "description": "All sending mailboxes of the account with the campaigns that use them. Passwords are masked. No pagination. Their `_id` is what you put in `identities[].email` of a campaign.",
        "parameters": [
          {
            "name": "filter",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "disconnected"
              ]
            },
            "description": "Only disconnected mailboxes."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "providers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EmailProvider"
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "providers": [
                    {
                      "_id": "6650f1a2b839d80011c5a0f1",
                      "userId": "6650f1a2b839d80011c5a0f1",
                      "senderName": "Marie Martin",
                      "senderEmail": "marie.martin@yourcompany.com",
                      "senderPassword": "********",
                      "emailType": "GOOGLE",
                      "smtp": {
                        "login": "marie.martin@yourcompany.com",
                        "password": "********",
                        "server": "smtp.yourcompany.com",
                        "port": 587,
                        "ssl": true
                      },
                      "imap": {
                        "login": "marie.martin@yourcompany.com",
                        "password": "********",
                        "server": "imap.yourcompany.com",
                        "port": 993,
                        "ssl": true
                      },
                      "customDomain": {
                        "_id": "6650f1a2b839d80011c5a0f1",
                        "domainName": "track.yourcompany.com",
                        "dateAdded": "2026-10-06T09:30:00.000Z"
                      },
                      "signature": "6650f1a2b839d80011c5a0f1",
                      "warmup": "6650f1a2b839d80011c5a0f1",
                      "health": "6650f1a2b839d80011c5a0f1",
                      "disabled": false,
                      "disconnected": false,
                      "campaigns": [
                        {
                          "_id": "6650f1a2b839d800bb200e24",
                          "name": "Q4 outreach: SaaS founders",
                          "type": "EMAIL"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Email providers"
        ],
        "operationId": "createEmailProvider",
        "summary": "Connect an SMTP/IMAP mailbox",
        "description": "Adds a mailbox after testing its SMTP and IMAP credentials. The number of mailboxes is limited by your subscription (1 plus the additional mailboxes you pay for).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailProviderInput"
              },
              "example": {
                "senderName": "Marie Martin",
                "senderEmail": "marie@acme.com",
                "emailType": "SMTP",
                "smtp": {
                  "login": "marie@acme.com",
                  "password": "app-password",
                  "server": "smtp.acme.com",
                  "port": 465,
                  "ssl": true
                },
                "imap": {
                  "login": "marie@acme.com",
                  "password": "app-password",
                  "server": "imap.acme.com",
                  "port": 993,
                  "ssl": true
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "id": {
                      "$ref": "#/components/schemas/ObjectId"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "id": "6650f1a2b839d80011c5a0aa"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or credentials rejected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "quota": {
                    "summary": "Mailbox quota reached",
                    "value": {
                      "error": "You cannot have more than 1 email provider on your account"
                    }
                  },
                  "exists": {
                    "summary": "Already connected",
                    "value": {
                      "error": "This email is already on your account"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/email-providers/{providerId}": {
      "patch": {
        "tags": [
          "Email providers"
        ],
        "operationId": "updateEmailProvider",
        "summary": "Update an SMTP/IMAP mailbox",
        "description": "Updates the credentials of an SMTP or Google IMAP mailbox, retests them and marks the mailbox as connected again. Masked passwords (`********`) are replaced by the stored values before the test.",
        "parameters": [
          {
            "name": "providerId",
            "in": "path",
            "required": true,
            "description": "Email provider id.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0aa"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailProviderInput"
              },
              "example": {
                "senderName": "Marie Martin",
                "senderEmail": "marie.martin@yourcompany.com",
                "senderPassword": "your-mailbox-password",
                "emailType": "GOOGLE",
                "smtp": {
                  "login": "marie.martin@yourcompany.com",
                  "password": "your-mailbox-password",
                  "server": "smtp.yourcompany.com",
                  "port": 587,
                  "ssl": true
                },
                "imap": {
                  "login": "marie.martin@yourcompany.com",
                  "password": "your-mailbox-password",
                  "server": "imap.yourcompany.com",
                  "port": 993,
                  "ssl": true
                },
                "domainName": "track.yourcompany.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "id": {
                      "$ref": "#/components/schemas/ObjectId"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "id": "6650f1a2b839d80011c5a0f1"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "summary": "Not found",
                    "value": {
                      "error": "Provider not found"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Email providers"
        ],
        "operationId": "deleteEmailProvider",
        "summary": "Delete a mailbox",
        "parameters": [
          {
            "name": "providerId",
            "in": "path",
            "required": true,
            "description": "Email provider id.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0aa"
          }
        ],
        "description": "Removes the mailbox from the account.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/email-providers/{providerId}/sender-name": {
      "patch": {
        "tags": [
          "Email providers"
        ],
        "operationId": "updateSenderName",
        "summary": "Change the sender name",
        "description": "Updates only the sender name of a mailbox (any type), without retesting credentials. Note: Outlook always displays the name of the Microsoft account.",
        "parameters": [
          {
            "name": "providerId",
            "in": "path",
            "required": true,
            "description": "Email provider id.",
            "schema": {
              "$ref": "#/components/schemas/ObjectId"
            },
            "example": "6650f1a2b839d80011c5a0aa"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "senderName"
                ],
                "properties": {
                  "senderName": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "senderName": "Marie from Acme"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "id": {
                      "$ref": "#/components/schemas/ObjectId"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "id": "6650f1a2b839d80011c5a0f1"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/email-providers/warmup": {
      "get": {
        "tags": [
          "Email providers"
        ],
        "operationId": "listWarmups",
        "summary": "List warmups",
        "description": "Warmup state of every mailbox (a warmup record is created on the fly for mailboxes that have none). No pagination: a `page` parameter is ignored. The `_id` of an item is the warmup id.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "warmups": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true,
                        "properties": {
                          "email": {
                            "type": "string"
                          },
                          "_id": {
                            "description": "Warmup id.",
                            "$ref": "#/components/schemas/ObjectId"
                          },
                          "provider": {
                            "$ref": "#/components/schemas/ObjectId"
                          },
                          "running": {
                            "type": "boolean"
                          },
                          "disabledReason": {
                            "type": "string"
                          },
                          "stats": {
                            "type": "object",
                            "additionalProperties": true
                          },
                          "health": {
                            "type": [
                              "object",
                              "null"
                            ],
                            "description": "Last DNS and deliverability diagnosis, or null.",
                            "additionalProperties": true
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "warmups": [
                    {
                      "email": "claire.fontaine@nexora.io",
                      "_id": "6650f1a2b839d80011c5a0f1",
                      "provider": "6650f1a2b839d80011c5a0f1",
                      "running": true,
                      "stats": {},
                      "health": {}
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/email-providers/warmup/enable": {
      "post": {
        "tags": [
          "Email providers"
        ],
        "operationId": "enableWarmup",
        "summary": "Enable the warmup of a mailbox",
        "description": "Starts the warmup of a mailbox. Requires an active subscription. `providerId` accepts the email provider id or the warmup id. The mailbox connection is tested first. Some hosts are refused (`WARMUP_PROVIDER_UNSUPPORTED`). A warmup disabled for incompatibility can only be reactivated a few times.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "providerId"
                ],
                "properties": {
                  "providerId": {
                    "type": "string",
                    "description": "Email provider id (or warmup id)."
                  }
                }
              },
              "example": {
                "providerId": "6650f1a2b839d80011c5a0aa"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "subscription": {
                    "summary": "No subscription",
                    "value": {
                      "error": "You must have a subscription to enable warmup"
                    }
                  },
                  "running": {
                    "summary": "Already running",
                    "value": {
                      "error": "Warmup already running"
                    }
                  },
                  "unsupported": {
                    "summary": "Unsupported host",
                    "value": {
                      "error": "Your provider doesn't support our system, it cannot be activated. If you think this is a mistake, please contact us.",
                      "code": "WARMUP_PROVIDER_UNSUPPORTED"
                    }
                  },
                  "connection": {
                    "summary": "Connection test failed",
                    "value": {
                      "error": "Email Provider connection failed."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/email-providers/warmup/check-health": {
      "post": {
        "tags": [
          "Email providers"
        ],
        "operationId": "checkMailboxHealth",
        "summary": "Run a deliverability diagnosis",
        "description": "Asks for a new DNS, sending and blacklist diagnosis of a mailbox. Asynchronous: the result appears a few minutes later in `health` of `GET /email-providers/warmup`. Note: a missing `warmupId` returns `{\"success\": false, \"message\": ...}`, not the usual error format.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "warmupId"
                ],
                "properties": {
                  "warmupId": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "warmupId": "6650f1a2b839d80011c5a0cc"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Success"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Missing warmupId, or other error.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "success": false,
                      "message": "warmupId is required"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/credits/usage": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "listCreditUsage",
        "summary": "List credit debits",
        "description": "History of the credits really consumed (refunds and refunded debits are not listed), newest first. `page` is one based.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "_id": {
                            "$ref": "#/components/schemas/ObjectId"
                          },
                          "amount": {
                            "type": "number"
                          },
                          "action": {
                            "type": "string",
                            "description": "For example `email_finder`, `phone_finder`, `email_verification`, `phone_finder_bulk`, `scrap_phone_enrichment`."
                          },
                          "meta": {
                            "type": "object",
                            "additionalProperties": true
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "totalPages": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "items": [
                    {
                      "_id": "6650f1a2b839d80011c5f101",
                      "amount": 1,
                      "action": "email_finder",
                      "createdAt": "2026-10-11T08:00:30.000Z"
                    }
                  ],
                  "total": 1,
                  "page": 1,
                  "totalPages": 1
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/ServerErrorOrRateLimited"
          }
        }
      }
    },
    "/lists/list": {
      "get": {
        "operationId": "listsGetLists",
        "summary": "List your lists",
        "description": "Returns the contact lists of the account (top-level lists only: lists attached to a parent list are not returned).\n\nPagination is opt-in. Send `page` and/or `limit` to get the paginated envelope `{ items, total, page, pageSize, totalLeads }`. Without both parameters the endpoint returns a bare JSON array with ALL your lists, unpaginated, newest first, and every filter parameter is ignored. Integrations should always send `page` and `limit`.\n\nTo find which lists contain a person, pass `leadSearch` with a full email address (case-insensitive match) or a LinkedIn profile URL (matched on the profile handle, so trailing slashes and `www` do not matter). Any other value returns zero lists.\n\nEach item is the stored list document. Besides the documented fields it can contain internal fields (column preferences, AI action settings, CRM integration settings, linked lists): do not rely on them.",
        "tags": [
          "Lists"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number, starting at 1. Sending it switches the response to the paginated envelope.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Values above 100 are clamped to 100. Sending it switches the response to the paginated envelope.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Case-insensitive substring of the list name. Paginated mode only.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "leadSearch",
            "in": "query",
            "required": false,
            "description": "Full email address or LinkedIn profile URL: only the lists containing this contact are returned. Paginated mode only.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "description": "Import source, exact value, case-insensitive. Values written by the service: `API` (created through this API), `Manual` (empty list created in the app), `CSV` (file import), `Sales Navigator` (LinkedIn scrap), `HubSpot`, `Pipedrive`, `Filter` (list created from a filtered selection). Paginated mode only.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "List status. Paginated mode only.",
            "schema": {
              "type": "string",
              "enum": [
                "mapped",
                "pending mapping"
              ]
            }
          },
          {
            "name": "tags",
            "in": "query",
            "required": false,
            "description": "Comma-separated tag ids: returns the lists carrying at least one of them. Paginated mode only.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "required": false,
            "description": "Sort field: `leads`, `emails` and `phones` sort on the counters. Unknown values fall back to `createdAt`. Paginated mode only.",
            "schema": {
              "type": "string",
              "enum": [
                "name",
                "leads",
                "emails",
                "phones",
                "createdAt"
              ],
              "default": "createdAt"
            }
          },
          {
            "name": "sortDir",
            "in": "query",
            "required": false,
            "description": "Sort direction. Any value other than `asc` sorts descending.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated envelope when `page` or `limit` is sent, otherwise a bare array of all lists.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ListsListsPage"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ListsList"
                      }
                    }
                  ]
                },
                "examples": {
                  "paginated": {
                    "summary": "With page and limit",
                    "value": {
                      "items": [
                        {
                          "_id": "66f1c0ffee5a7d3b2c4e8a10",
                          "userId": "65a9b8c7d6e5f4a3b2c1d0e9",
                          "name": "Acme prospects Q4",
                          "source": "API",
                          "status": "mapped",
                          "companiesOnly": false,
                          "numberOfContacts": 42,
                          "numberOfEmails": 40,
                          "numberOfPhones": 12,
                          "numberOfLinkedins": 38,
                          "emailFindRunAt": null,
                          "phoneFindRunAt": null,
                          "tagIds": [],
                          "parentListId": null,
                          "linkedLists": [],
                          "createdAt": "2026-10-01T09:12:44.000Z",
                          "updatedAt": "2026-10-03T16:40:02.000Z"
                        }
                      ],
                      "total": 1,
                      "page": 1,
                      "pageSize": 25,
                      "totalLeads": 42
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "listsCreateList",
        "summary": "Create a list",
        "description": "Creates an empty list with source `API` and status `mapped`, immediately ready to receive contacts through `POST /lists/list/{listId}/contacts` or the batch route. List names do not need to be unique: calling this twice creates two lists, so store the returned `listId`.\n\nTo make the contacts of the list enter a campaign, attach the list to the campaign (in the app or through the Campaigns API). Contacts added afterwards flow into the campaigns that use the list.\n\nDo not send `source`: the value `Manual` is reserved to the web app and starts the list with most columns hidden, which removes their fields from `GET /lists/list/{listId}/rows` until contacts fill them.",
        "tags": [
          "Lists"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "description": "List name, trimmed. Must not be empty."
                  }
                }
              },
              "example": {
                "name": "Acme prospects Q4"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "List created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsCreateListResult"
                },
                "example": {
                  "success": true,
                  "listId": "66f1c0ffee5a7d3b2c4e8a10",
                  "name": "Acme prospects Q4"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Missing or empty name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "name is required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      }
    },
    "/lists/list/{listId}": {
      "get": {
        "operationId": "listsGetList",
        "summary": "Get a list",
        "description": "Returns one list with its counters.\n\nThe counters are denormalized: `numberOfEmails` and `numberOfPhones` count the contacts that had an email or a `phone` (not `mobilePhone`) when they were added, and are recomputed only at the end of a find-email or find-phone run. They are not updated when a contact is edited: treat them as indicative and use `GET /lists/list/{listId}/rows` for exact data.\n\n`enrichmentRun` is non-null while a bulk job runs on the list (find email, find phone, verify email, column normalization, file import, CRM sync). While it is set, starting another bulk job on the list returns 409.",
        "tags": [
          "Lists"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "List id (24-character ObjectId).",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "65f1a2b3c4d5e6f760f68df4"
          }
        ],
        "responses": {
          "200": {
            "description": "The list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsListDetail"
                },
                "example": {
                  "_id": "66f1c0ffee5a7d3b2c4e8a10",
                  "name": "Acme prospects Q4",
                  "fileId": null,
                  "source": "API",
                  "status": "mapped",
                  "companiesOnly": false,
                  "numberOfContacts": 42,
                  "numberOfEmails": 40,
                  "numberOfPhones": 12,
                  "numberOfLinkedins": 38,
                  "emailFindRunAt": null,
                  "phoneFindRunAt": null,
                  "enrichmentRun": null,
                  "hubspotIntegration": null,
                  "pipedriveIntegration": null,
                  "createdAt": "2026-10-01T09:12:44.000Z",
                  "updatedAt": "2026-10-03T16:40:02.000Z"
                }
              }
            }
          },
          "400": {
            "description": "The list id is not a valid ObjectId.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Invalid list id"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The list does not exist or belongs to another account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "List not found"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "listsRenameList",
        "summary": "Rename a list",
        "description": "Renames a list. Only `name` can be changed through this route.",
        "tags": [
          "Lists"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "List id.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "65f1a2b3c4d5e6f760f68df4"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "description": "New name, trimmed."
                  }
                }
              },
              "example": {
                "name": "Acme prospects Q4 (France)"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "List renamed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsRenameListResult"
                },
                "example": {
                  "success": true,
                  "_id": "66f1c0ffee5a7d3b2c4e8a10",
                  "name": "Acme prospects Q4 (France)"
                }
              }
            }
          },
          "400": {
            "description": "Invalid list id, or missing or empty name (note: 400 here, while list creation answers 422 for the same mistake).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "examples": {
                  "invalidId": {
                    "value": {
                      "message": "Invalid list id"
                    }
                  },
                  "missingName": {
                    "value": {
                      "message": "name is required"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The list does not exist or belongs to another account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "List not found"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "listsDeleteList",
        "summary": "Delete a list",
        "description": "Permanently deletes the list, every contact and company row it contains, and its child or linked lists. The deleted list and its contacts are removed from the recipients of your campaigns, which are then resynchronized: deleting a list used as an exclusion list puts the contacts it was excluding back into the campaigns. This cannot be undone.\n\nThe call is synchronous: deleting a very large list can take a while.",
        "tags": [
          "Lists"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "List id.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "65f1a2b3c4d5e6f760f68df4"
          }
        ],
        "responses": {
          "200": {
            "description": "List deleted, with deletion counters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsDeleteListResult"
                },
                "example": {
                  "success": true,
                  "deletedLists": 1,
                  "deletedContacts": 42,
                  "deletedCompanies": 40,
                  "campaignsUpdated": 1
                }
              }
            }
          },
          "400": {
            "description": "The list id is not a valid ObjectId.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Invalid list id"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The list does not exist or belongs to another account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "List not found"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      }
    },
    "/lists/list/{listId}/rows": {
      "get": {
        "operationId": "listsGetListContacts",
        "summary": "List or find the contacts of a list",
        "description": "Two modes.\n\n**Exact lookup** (use it before an update or a delete): pass `email` OR `linkedinUrl`. The email is matched exactly but case-insensitively, the LinkedIn URL is compared as an exact string (a trailing slash or a missing `www` is a different URL). The search covers this list and the lists linked to it, returns up to 50 matches, ignores `search`, filters and pagination, and returns the full contact objects. The envelope then has `page: 1`, `pageSize` equal to the number of items (1 when empty) and no `customVariables`.\n\n**Paginated listing**: without `email` and `linkedinUrl`, returns one page of the contacts stored in this list (not in linked lists), in creation order unless `sortBy` is set. Company rows without a contact (company-only lists) are included with `rowKind: \"company_only\"` and an id of the form `co:<companyId>` that the contact routes do not accept.\n\nIn paginated mode, when the list has column settings (every list created by this API or by the current app has them), each item only contains the fields of the columns visible in the list table of the app: a column hidden in the app disappears from the API response, `labels` is not returned and `company` carries no `companyId`. Use the exact lookup to get the full object.\n\nDeep pages are expensive (the service sorts `page x pageSize` rows to serve one page): page through large lists with `pageSize=100` and avoid restarting from page 1, or use the export.",
        "tags": [
          "List contacts"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "List id.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "65f1a2b3c4d5e6f760f68df4"
          },
          {
            "name": "email",
            "in": "query",
            "required": false,
            "description": "Exact lookup by email (case-insensitive). Mutually exclusive with `linkedinUrl`.",
            "schema": {
              "type": "string",
              "format": "email"
            }
          },
          {
            "name": "linkedinUrl",
            "in": "query",
            "required": false,
            "description": "Exact lookup by LinkedIn profile URL (compared verbatim with the stored `linkedinUrlProfile`). Mutually exclusive with `email`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number. Invalid or negative values fall back to 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "description": "Page size, clamped between 1 and 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Free-text search, 200 characters max. A full email address is matched exactly (case-insensitive). Otherwise every word must appear (case-insensitive substring) in one of `firstName`, `lastName`, `fullName`, `email` or `linkedinUrlProfile`; company-only rows are matched on the company name.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "required": false,
            "description": "Sort field. Text fields sort case- and accent-insensitively. Any other value is ignored (creation order).",
            "schema": {
              "type": "string",
              "enum": [
                "firstName",
                "lastName",
                "fullName",
                "jobTitle",
                "email",
                "phone",
                "linkedinUrlProfile",
                "country",
                "city",
                "createdAt",
                "updatedAt"
              ]
            }
          },
          {
            "name": "sortDir",
            "in": "query",
            "required": false,
            "description": "Sort direction, used with `sortBy`.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            }
          },
          {
            "name": "statuses",
            "in": "query",
            "required": false,
            "description": "Comma-separated engagement statuses (as stored on the contacts, for example `SENT`, `OPENED`, `MAIL_REPLIED`, `ACCEPTED`). The token `NO_ACTIVITY` matches contacts without status. Up to 30 values. Company-only rows are excluded when this filter is set.",
            "schema": {
              "type": "string"
            },
            "example": "MAIL_REPLIED,LINKEDIN_REPLIED"
          },
          {
            "name": "filters",
            "in": "query",
            "required": false,
            "description": "URL-encoded JSON array of column rules (up to 10), all combined with AND. See `ListsFilterRule`.",
            "schema": {
              "type": "string"
            },
            "example": "[{\"column\":\"companyName\",\"operator\":\"contains\",\"value\":\"acme\"}]"
          }
        ],
        "responses": {
          "200": {
            "description": "Contacts of the list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsContactsPage"
                },
                "examples": {
                  "exactLookup": {
                    "summary": "?email=john.doe@acme.com",
                    "value": {
                      "items": [
                        {
                          "id": "66f2a1b2c3d4e5f6a7b8c9d0",
                          "firstName": "John",
                          "lastName": "Doe",
                          "fullName": "John Doe",
                          "jobTitle": "Head of Sales",
                          "gender": null,
                          "email": "john.doe@acme.com",
                          "secondaryEmail": null,
                          "phone": "+33612345678",
                          "mobilePhone": null,
                          "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
                          "twitterUrl": null,
                          "country": "France",
                          "city": "Paris",
                          "seniority": "",
                          "department": "",
                          "bio": "",
                          "profilePictureUrl": null,
                          "company": {
                            "companyId": "66f2a1b2c3d4e5f6a7b8c9e0",
                            "companyName": "Acme",
                            "companyDomain": "acme.com",
                            "websiteUrl": "https://www.acme.com",
                            "industry": "Software",
                            "companySize": "51-200",
                            "employeeCount": null,
                            "country": "",
                            "city": "",
                            "linkedinCompanyUrl": "https://www.linkedin.com/company/acme",
                            "logoUrl": "",
                            "customFields": []
                          },
                          "labels": [],
                          "emailVerified": "unknown",
                          "status": "",
                          "customFields": [
                            {
                              "technicalName": "icebreaker",
                              "displayName": "icebreaker",
                              "value": "Loved your talk at SaaStock"
                            }
                          ],
                          "createdAt": "2026-10-02T08:00:00.000Z",
                          "updatedAt": "2026-10-02T08:00:00.000Z",
                          "rowKind": "lead"
                        }
                      ],
                      "total": 1,
                      "page": 1,
                      "pageSize": 1
                    }
                  },
                  "paginated": {
                    "summary": "?page=1&pageSize=50",
                    "value": {
                      "items": [
                        {
                          "id": "66f2a1b2c3d4e5f6a7b8c9d0",
                          "rowKind": "lead",
                          "firstName": "John",
                          "lastName": "Doe",
                          "fullName": "John Doe",
                          "profilePictureUrl": null,
                          "gender": null,
                          "jobTitle": "Head of Sales",
                          "email": "john.doe@acme.com",
                          "emailVerified": "unknown",
                          "secondaryEmail": null,
                          "phone": "+33612345678",
                          "mobilePhone": null,
                          "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
                          "status": "SENT",
                          "twitterUrl": null,
                          "country": "France",
                          "city": "Paris",
                          "seniority": "",
                          "department": "",
                          "bio": "",
                          "createdAt": "2026-10-02T08:00:00.000Z",
                          "updatedAt": "2026-10-02T08:00:00.000Z",
                          "customFields": [
                            {
                              "technicalName": "icebreaker",
                              "displayName": "icebreaker",
                              "value": "Loved your talk at SaaStock"
                            }
                          ],
                          "company": {
                            "companyName": "Acme",
                            "companyDomain": "acme.com",
                            "websiteUrl": "https://www.acme.com",
                            "logoUrl": "",
                            "industry": "Software",
                            "country": "",
                            "city": "",
                            "linkedinCompanyUrl": "https://www.linkedin.com/company/acme",
                            "companySize": "51-200",
                            "employeeCount": null,
                            "customFields": []
                          }
                        }
                      ],
                      "total": 42,
                      "page": 1,
                      "pageSize": 50,
                      "customVariables": [
                        {
                          "technicalName": "icebreaker",
                          "displayName": "icebreaker",
                          "schema": "lead"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The list does not exist or belongs to another account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "List not found"
                }
              }
            }
          },
          "422": {
            "description": "`email` and `linkedinUrl` sent together, or invalid `filters`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "examples": {
                  "both": {
                    "value": {
                      "message": "email and linkedinUrl are mutually exclusive"
                    }
                  },
                  "filters": {
                    "value": {
                      "message": "unknown column \"companyRevenue\""
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      }
    },
    "/lists/list/{listId}/rows/{leadId}": {
      "delete": {
        "operationId": "listsDeleteContact",
        "summary": "Delete a contact",
        "description": "Permanently deletes a contact and its company row. The contact can live in the list or in a list linked to it. It is removed from the campaigns that reference it, the list counters are decremented and the campaigns using the list are resynchronized. This cannot be undone. A second call for the same contact returns 404.\n\nFind the `leadId` with the exact lookup of `GET /lists/list/{listId}/rows`. Company-only rows (`co:` ids) cannot be deleted with this route.",
        "tags": [
          "List contacts"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "List id.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "65f1a2b3c4d5e6f760f68df4"
          },
          {
            "name": "leadId",
            "in": "path",
            "required": true,
            "description": "Contact id.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "65f1a2b3c4d5e6f7af1c3d42"
          }
        ],
        "responses": {
          "200": {
            "description": "Contact deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsSuccess"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid list id or contact id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Invalid id"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "List not found (or not yours), or contact not found in this list or its linked lists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "examples": {
                  "list": {
                    "value": {
                      "message": "List not found"
                    }
                  },
                  "contact": {
                    "value": {
                      "message": "Contact not found"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      }
    },
    "/lists/list/{listId}/contacts": {
      "post": {
        "operationId": "listsAddContact",
        "summary": "Add a contact to a list (add or update)",
        "description": "Adds ONE contact to a list from a flat JSON object: field names as keys, scalar values (string, number or boolean). This is the endpoint for automations (Zapier, Make, n8n, scripts) and the standard way to feed a running campaign: if campaigns in RUNNING, PAUSED or DRAFT status use the list, the contact is propagated to them asynchronously (usually within seconds, at the latest by the nightly reconciliation). Adding contacts consumes no credits. For more than one contact, use the batch route.\n\n### How keys are resolved\nEach key is resolved in this order: (1) case-insensitively against the recognized names of the standard fields (`First Name`, `Prénom`, `email_address`, `Company`...); (2) against the standard field keys, ignoring case, spaces, `_` and `-` (`firstName`, `first_name` and `FIRSTNAME` all work), and also as a prefix or suffix of a field key when the key has 3 characters or more; (3) against your existing custom variables (technical or display name); (4) a key whose name ends in `id` is never matched by its value and becomes a custom variable; (5) otherwise the VALUE is inspected: an email-looking value goes to `email`, 6 digits or more go to `phone`, a `linkedin.com/in/` URL goes to `linkedinUrlProfile`, any other `http(s)://` URL goes to the company `websiteUrl`. Only when nothing matched is a new lead custom variable created (display name = the key, technical name derived from it) and reported in `createdCustomVariables`.\n\n### Pitfalls\n- Use the exact keys listed below. Short or generic keys are fuzzy-matched: `job` lands in `jobStartDate` (a date, the request fails), `last` in `lastContacted` (the value is lost), `year` in `yearsOfExperience`, `date` in `birthDate`, `note` in the company `notes`, `tag` in the company `tags`.\n- An unknown key whose value looks like a phone number (company registration number, order number, a date such as `2024-01-15`) is stored in `phone`, not in a new custom variable. Declare the custom variable first with `POST /lists/custom-variable`: an existing variable is matched before the value is inspected.\n- `mobile` maps to `phone`. Use `mobilePhone` for a mobile number.\n- A standard field takes at most one key per contact. When two keys target the same field, the second one moves to the next matching field (a second email key can end up in `emailVerified` and be lost).\n- Numeric fields (`age`, `yearsOfExperience`, `employeeCount`, `foundedYear`, `annualRevenue`) silently drop non-numeric values. Date fields (`birthDate`, `jobStartDate`) must be valid dates (use ISO 8601, for example `2021-03-01`): an unparsable date makes the request fail.\n- `null` and empty strings are ignored: this endpoint cannot clear a field. Arrays and objects are rejected (422).\n- Every unknown key creates an account-wide custom variable that cannot be deleted through the API.\n\n### Standard keys\nContact: `firstName`, `lastName`, `fullName`, `email`, `secondaryEmail`, `phone`, `mobilePhone`, `linkedinUrlProfile`, `twitterUrl`, `jobTitle`, `seniority`, `department`, `gender`, `age`, `birthDate`, `language`, `country`, `region`, `city`, `postalCode`, `address`, `timezone`, `jobStartDate`, `yearsOfExperience`, `education`, `skills` (list separated by commas, semicolons or pipes), `bio`, `profilePictureUrl`.\nCompany: `companyName`, `companyDomain`, `companyEmail`, `websiteUrl`, `linkedinCompanyUrl`, `twitterCompanyUrl`, `logoUrl`, `industry`, `companySize`, `companyType`, `employeeCount`, `foundedYear`, `annualRevenue`, `notes`, `tags`, `techStack`. The keys `country`, `city`, `region`, `postalCode`, `address` and `timezone` go to the contact: use `Company Country`, `Company City` or `Company Address` to fill the company ones.\n\n### Duplicates and idempotency\nBefore creating, the service looks in this list and the lists linked to it for a contact with the same `email` (exact comparison, case-sensitive), then with the same `linkedinUrlProfile` (exact string). If one is found, nothing is created: the response returns its `leadId` with `duplicate: true`. With `updateIfExists=true` the payload is applied to that contact instead (sent fields overwrite, absent fields are kept, identity fields included) and the response has `updated: true`. A contact without email and without LinkedIn URL is never deduplicated: retrying such a call creates a new contact each time. Send emails in lowercase and LinkedIn URLs in one canonical form.\n\nThe status code is 200 for a creation, an ignored duplicate and an update alike: read `duplicate` and `updated`.",
        "tags": [
          "List contacts"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "List id.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "65f1a2b3c4d5e6f760f68df4"
          },
          {
            "name": "updateIfExists",
            "in": "query",
            "required": false,
            "description": "Only the exact string `true` enables it. When a duplicate is found, apply the payload to the existing contact instead of ignoring it. Query parameter, never a body key (a body key would be stored as a contact field).",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "default": "false"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListsContactFields"
              },
              "example": {
                "firstName": "John",
                "lastName": "Doe",
                "email": "john.doe@acme.com",
                "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
                "jobTitle": "Head of Sales",
                "companyName": "Acme",
                "websiteUrl": "https://www.acme.com",
                "icebreaker": "Loved your talk at SaaStock"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact created, duplicate ignored, or duplicate updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAddContactResult"
                },
                "examples": {
                  "created": {
                    "value": {
                      "success": true,
                      "leadId": "66f2a1b2c3d4e5f6a7b8c9d0",
                      "companyId": "66f2a1b2c3d4e5f6a7b8c9e0",
                      "duplicate": false,
                      "updated": false,
                      "createdCustomVariables": [
                        {
                          "technicalName": "icebreaker",
                          "displayName": "icebreaker",
                          "schema": "lead"
                        }
                      ]
                    }
                  },
                  "duplicate": {
                    "value": {
                      "success": true,
                      "leadId": "66f2a1b2c3d4e5f6a7b8c9d0",
                      "companyId": "66f2a1b2c3d4e5f6a7b8c9e0",
                      "duplicate": true,
                      "updated": false,
                      "createdCustomVariables": []
                    }
                  },
                  "updated": {
                    "value": {
                      "success": true,
                      "leadId": "66f2a1b2c3d4e5f6a7b8c9d0",
                      "companyId": "66f2a1b2c3d4e5f6a7b8c9e0",
                      "duplicate": true,
                      "updated": true,
                      "createdCustomVariables": []
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The list id is not a valid ObjectId.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Invalid list id"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The list does not exist or belongs to another account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "List not found"
                }
              }
            }
          },
          "413": {
            "description": "JSON body larger than 5 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Request body too large: the limit is 5mb",
                  "code": "PAYLOAD_TOO_LARGE",
                  "params": {
                    "limit": "5mb"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Invalid body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "examples": {
                  "notObject": {
                    "value": {
                      "message": "Body must be a JSON object of contact fields"
                    }
                  },
                  "nonScalar": {
                    "value": {
                      "message": "Field \"tags\" must be a string, number or boolean"
                    }
                  },
                  "empty": {
                    "value": {
                      "message": "Contact has no usable field"
                    }
                  },
                  "badKey": {
                    "value": {
                      "message": "Cannot derive a variable name from field \"???\""
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error, for example an invalid date in `birthDate` or `jobStartDate`. The message is the raw internal message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                }
              }
            }
          }
        }
      }
    },
    "/lists/list/{listId}/contacts/batch": {
      "post": {
        "operationId": "listsAddContactsBatch",
        "summary": "Add up to 100 contacts to a list",
        "description": "Adds 1 to 100 contacts in one call. Each item of `contacts` is the same flat object as in `POST /lists/list/{listId}/contacts`, with the same key resolution, custom variable creation, duplicate rules and `updateIfExists` behaviour (read that endpoint first).\n\nRows are processed one after the other and independently: a failing row is reported in `results` with its `index` and an `error` message and does not stop the others. A row repeating an email or LinkedIn URL already seen earlier in the same batch is counted as a duplicate of that earlier row and is never updated, even with `updateIfExists=true`.\n\nCounters: `created + duplicates + failed = total`, and `updated` is a subset of `duplicates`. The body must be an object with a `contacts` array, not a bare array. Split larger loads into several calls of 100.",
        "tags": [
          "List contacts"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "List id.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "65f1a2b3c4d5e6f760f68df4"
          },
          {
            "name": "updateIfExists",
            "in": "query",
            "required": false,
            "description": "Only the exact string `true` enables it. Applies to every row.",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "default": "false"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "contacts"
                ],
                "properties": {
                  "contacts": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 100,
                    "items": {
                      "$ref": "#/components/schemas/ListsContactFields"
                    }
                  }
                }
              },
              "example": {
                "contacts": [
                  {
                    "firstName": "John",
                    "lastName": "Doe",
                    "email": "john.doe@acme.com",
                    "companyName": "Acme"
                  },
                  {
                    "firstName": "Jane",
                    "lastName": "Roe",
                    "email": "jane.roe@acme.com",
                    "companyName": "Acme",
                    "icebreaker": "Congrats on the new office"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch processed. Read `results` for the outcome of each row.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsBatchResult"
                },
                "example": {
                  "success": true,
                  "total": 3,
                  "created": 1,
                  "duplicates": 1,
                  "updated": 0,
                  "failed": 1,
                  "results": [
                    {
                      "index": 0,
                      "leadId": "66f2a1b2c3d4e5f6a7b8c9d0",
                      "companyId": "66f2a1b2c3d4e5f6a7b8c9e0",
                      "duplicate": true,
                      "updated": false
                    },
                    {
                      "index": 1,
                      "leadId": "66f2a1b2c3d4e5f6a7b8c9d1",
                      "companyId": "66f2a1b2c3d4e5f6a7b8c9e1",
                      "duplicate": false,
                      "updated": false
                    },
                    {
                      "index": 2,
                      "error": "Field \"tags\" must be a string, number or boolean"
                    }
                  ],
                  "createdCustomVariables": []
                }
              }
            }
          },
          "400": {
            "description": "The list id is not a valid ObjectId.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Invalid list id"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The list does not exist or belongs to another account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "List not found"
                }
              }
            }
          },
          "413": {
            "description": "JSON body larger than 5 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Request body too large: the limit is 5mb",
                  "code": "PAYLOAD_TOO_LARGE",
                  "params": {
                    "limit": "5mb"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`contacts` missing, not an array, empty or longer than 100.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "examples": {
                  "notArray": {
                    "value": {
                      "message": "contacts must be an array"
                    }
                  },
                  "size": {
                    "value": {
                      "message": "contacts must contain between 1 and 100 items"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      }
    },
    "/lists/contacts/{contactId}": {
      "patch": {
        "operationId": "listsUpdateContact",
        "summary": "Update a contact",
        "description": "Updates a contact identified by its id only (its list is derived from the contact). The body is the same flat object as `POST /lists/list/{listId}/contacts`, with the same key resolution and the same pitfalls: unknown keys create custom variables, `null` and empty strings are ignored (a field cannot be cleared), sent fields overwrite, absent fields stay untouched. A company row is created if the contact has none and company fields are sent.\n\nNo duplicate check is made: changing the email to one already present in the list creates a duplicate. List counters are not updated. The campaigns using the list are resynchronized.",
        "tags": [
          "List contacts"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "required": true,
            "description": "Contact id (`leadId`).",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "65f1a2b3c4d5e6f74db757ce"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListsContactFields"
              },
              "example": {
                "jobTitle": "VP Sales",
                "phone": "+33612345678",
                "icebreaker": "Saw your post about the Series A"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsUpdateContactResult"
                },
                "example": {
                  "success": true,
                  "leadId": "66f2a1b2c3d4e5f6a7b8c9d0",
                  "companyId": "66f2a1b2c3d4e5f6a7b8c9e0",
                  "createdCustomVariables": []
                }
              }
            }
          },
          "400": {
            "description": "The contact id is not a valid ObjectId.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Invalid contact id"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Contact not found, deleted, or not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Contact not found"
                }
              }
            }
          },
          "413": {
            "description": "JSON body larger than 5 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Request body too large: the limit is 5mb",
                  "code": "PAYLOAD_TOO_LARGE",
                  "params": {
                    "limit": "5mb"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Invalid body (same messages as the add route).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Contact has no usable field"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      }
    },
    "/lists/list/export": {
      "post": {
        "operationId": "listsExportLists",
        "summary": "Export lists as CSV",
        "description": "Exports one list as a CSV file, or several lists as a ZIP archive with one CSV per list.\n\nCSV format: UTF-8 with BOM, comma separator, CRLF line endings. Columns: `First Name`, `Last Name`, `Full Name`, `Email`, `Phone`, `Mobile Phone`, `Job Title`, `LinkedIn URL`, `Bio`, `Company`, `Website`, `LinkedIn Page`, `Company Size`, `Industry`, `Country`, `Region`, `City`, `Status`, `Handle`, then one column per custom variable of the account (display name), then `Lists` and `Campaigns` (one `Campaign name - STATUS` line per campaign). Company-only lists use a shorter header (`Company`, `Website`, `LinkedIn Page`, `Company Size`, `Industry`, custom variables, `Lists`).\n\n`leadIds`, `search`, `statuses` and `filters` only apply when exactly one list is exported; they are ignored for a multi-list ZIP. List ids that are not yours are silently skipped.\n\n**Large exports**: send `async: true`. If the export covers more than 5,000 contacts, the response is `202` with a `jobId`: poll `GET /lists/list/export/jobs/{jobId}` until `status` is `done`, then download the file. At or under 5,000 contacts the file is returned directly (200) even with `async: true`, so handle both responses. With `async: true`, an export above 100,000 contacts is refused (422). Only one asynchronous export runs per account: while one is queued or running, a new request returns that existing job (it may concern other lists). Without `async`, the file is always streamed synchronously, whatever its size, and the connection may be cut after 120 seconds of silence.",
        "tags": [
          "Lists"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListsExportRequest"
              },
              "examples": {
                "oneList": {
                  "value": {
                    "listIds": [
                      "66f1c0ffee5a7d3b2c4e8a10"
                    ],
                    "statuses": [
                      "MAIL_REPLIED"
                    ],
                    "async": true
                  }
                },
                "severalLists": {
                  "value": {
                    "listIds": [
                      "66f1c0ffee5a7d3b2c4e8a10",
                      "66f1c0ffee5a7d3b2c4e8a11"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The file: `text/csv; charset=utf-8` named after the list for one list, `application/zip` named `emelia-lists-export.zip` for several lists. Sent as an attachment (`Content-Disposition`).",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "202": {
            "description": "Asynchronous export accepted (only with `async: true` and more than 5,000 contacts). `total` is the number of contacts of this request; if an export was already running, `jobId` and `status` are those of that running export.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsExportJobAccepted"
                },
                "example": {
                  "jobId": "0b6f3c1e-8a2d-4c5b-9e7f-1a2b3c4d5e6f",
                  "status": "queued",
                  "total": 18250
                }
              }
            }
          },
          "400": {
            "description": "No valid list id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "listIds is required"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "None of the requested lists is yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "No matching list found"
                }
              }
            }
          },
          "422": {
            "description": "Invalid `filters`, or export too large (with `async: true`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "examples": {
                  "tooLarge": {
                    "value": {
                      "message": "Export too large: at most 100000 contacts per export",
                      "code": "export_too_large",
                      "max": 100000
                    }
                  },
                  "filters": {
                    "value": {
                      "message": "too many filters (max 10)"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Export failed before the file started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Export failed",
                  "error": "List not found"
                }
              }
            }
          },
          "503": {
            "description": "Asynchronous export temporarily unavailable (job storage down). Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Async export unavailable"
                }
              }
            }
          }
        }
      }
    },
    "/lists/list/export/jobs/{jobId}": {
      "get": {
        "operationId": "listsGetExportJob",
        "summary": "Get the status of an export",
        "description": "Returns the progress of an asynchronous export started with `POST /lists/list/export` and `async: true`. Poll every few seconds until `status` is `done` (then call the download route) or `failed` (read `error`). Jobs are kept 24 hours; after that, or for a job of another account, the route returns 404. A job interrupted by a service restart ends as `failed`: start the export again.",
        "tags": [
          "List exports"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "description": "Job id returned by the export request (UUID).",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "0b6f3c1e-8a2d-4c5b-9e7f-1a2b3c4d5e6f"
          }
        ],
        "responses": {
          "200": {
            "description": "Job state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsExportJob"
                },
                "examples": {
                  "running": {
                    "value": {
                      "jobId": "0b6f3c1e-8a2d-4c5b-9e7f-1a2b3c4d5e6f",
                      "status": "running",
                      "rows": 9000,
                      "total": 18250,
                      "filename": null,
                      "error": null
                    }
                  },
                  "done": {
                    "value": {
                      "jobId": "0b6f3c1e-8a2d-4c5b-9e7f-1a2b3c4d5e6f",
                      "status": "done",
                      "rows": 18250,
                      "total": 18250,
                      "filename": "Acme prospects Q4.csv",
                      "error": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown, expired or foreign job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Export job not found"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      }
    },
    "/lists/list/export/jobs/{jobId}/download": {
      "get": {
        "operationId": "listsDownloadExportJob",
        "summary": "Download a finished export",
        "description": "Streams the file of a finished asynchronous export (CSV for one list, ZIP for several), as an attachment. Returns 409 while the job is not `done`.",
        "tags": [
          "List exports"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "description": "Job id (UUID).",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "0b6f3c1e-8a2d-4c5b-9e7f-1a2b3c4d5e6f"
          }
        ],
        "responses": {
          "200": {
            "description": "The export file.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown, expired or foreign job, or file missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "examples": {
                  "job": {
                    "value": {
                      "message": "Export job not found"
                    }
                  },
                  "file": {
                    "value": {
                      "message": "Export file not found"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The export is not finished.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Export not ready",
                  "status": "running"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      }
    },
    "/lists/list/campaign-usage": {
      "get": {
        "operationId": "listsGetCampaignUsage",
        "summary": "Find the campaigns using lists",
        "description": "For each list id, returns the non-archived campaigns that include the list in their recipients. Use it to know whether adding contacts to a list will feed a campaign. Lists used only as exclusion lists are not reported. Ids that are not valid ObjectIds are dropped; ids of lists that are not yours return an empty array.",
        "tags": [
          "Lists"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "listIds",
            "in": "query",
            "required": true,
            "description": "Comma-separated list ids.",
            "schema": {
              "type": "string"
            },
            "example": "66f1c0ffee5a7d3b2c4e8a10,66f1c0ffee5a7d3b2c4e8a11"
          }
        ],
        "responses": {
          "200": {
            "description": "Campaigns per list id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsCampaignUsage"
                },
                "example": {
                  "usage": {
                    "66f1c0ffee5a7d3b2c4e8a10": [
                      {
                        "_id": "66e0d9c8b7a6f5e4d3c2b1a0",
                        "name": "Acme Q4 outreach",
                        "status": "RUNNING"
                      }
                    ],
                    "66f1c0ffee5a7d3b2c4e8a11": []
                  }
                }
              }
            }
          },
          "400": {
            "description": "`listIds` missing or empty.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "listIds query param is required"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      }
    },
    "/lists/custom-variable": {
      "post": {
        "operationId": "listsCreateCustomVariable",
        "summary": "Create a custom variable",
        "description": "Declares a custom variable on the account, so that contact keys with this name (technical or display name) land in it instead of being guessed. Declare your variables before loading contacts. This is the only way to create a COMPANY custom variable: the contact routes only create contact (`lead`) variables.\n\nNaming rule: a technical name that is already a clean identifier (letter first, then letters, digits or `_`) is kept, with its first letter lowercased (`basile_gmb_name` stays `basile_gmb_name`). Anything else is camelCased with accents and symbols dropped (`Ice breaker` becomes `iceBreaker`). The stored name is returned.\n\nThe call is refused (422) when a variable with the same name already exists for this schema (comparison ignoring case, spaces, `_` and `-`) or when the name is a standard field of the schema: it is therefore safe to call it every time and treat 422 as already existing. Variables are shared by all your lists and cannot be renamed or deleted through the API.",
        "tags": [
          "List custom variables"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ListsCustomVariableCreateRequest"
              },
              "example": {
                "technicalName": "icebreaker",
                "schema": "lead",
                "displayName": "Icebreaker"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Variable created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsCustomVariable"
                },
                "example": {
                  "technicalName": "icebreaker",
                  "displayName": "Icebreaker",
                  "schema": "lead"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Invalid body, empty name after normalization, existing variable or standard field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "message": "\"technicalName\" is required"
                    }
                  },
                  "unknownKey": {
                    "value": {
                      "message": "\"name\" is not allowed"
                    }
                  },
                  "exists": {
                    "value": {
                      "message": "Custom variable \"icebreaker\" already exists for lead schema."
                    }
                  },
                  "standard": {
                    "value": {
                      "message": "\"email\" is already a standard lead field. Choose a different technical name."
                    }
                  },
                  "empty": {
                    "value": {
                      "message": "technicalName must contain at least one letter or digit"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      }
    },
    "/lists/custom-variable/check": {
      "get": {
        "operationId": "listsCheckCustomVariable",
        "summary": "Check whether a custom variable exists",
        "description": "Tells whether a name is already taken for a schema, either by one of your custom variables (`reason: custom_variable`) or by a standard field (`lead_field` or `company_field`). The name is normalized with the same rule as the creation route and the normalized `technicalName` is returned.",
        "tags": [
          "List custom variables"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "technicalName",
            "in": "query",
            "required": true,
            "description": "Name to check.",
            "schema": {
              "type": "string"
            },
            "example": "industryFocus"
          },
          {
            "name": "schema",
            "in": "query",
            "required": true,
            "description": "`lead` for contact variables, `company` for company variables.",
            "schema": {
              "type": "string",
              "enum": [
                "lead",
                "company"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Check result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsCustomVariableCheck"
                },
                "examples": {
                  "free": {
                    "value": {
                      "exists": false,
                      "technicalName": "icebreaker"
                    }
                  },
                  "taken": {
                    "value": {
                      "exists": true,
                      "reason": "custom_variable",
                      "technicalName": "icebreaker"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown or disabled API key, or the account of the key no longer exists. Note the body shape: `error` and `code`, not `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsAuthError"
                },
                "examples": {
                  "missing": {
                    "value": {
                      "error": "Authentication required",
                      "code": "AUTH_REQUIRED"
                    }
                  },
                  "account": {
                    "value": {
                      "error": "Account not found",
                      "code": "ACCOUNT_NOT_FOUND"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Missing or invalid query parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "\"schema\" must be one of [lead, company]"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error. `error`, when present, is a raw internal message: do not parse it. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListsError"
                },
                "example": {
                  "message": "Hmm... Something went wrong. Please try again later."
                }
              }
            }
          }
        }
      }
    },
    "/linkedin-scrappers": {
      "get": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperList",
        "summary": "List your LinkedIn scrapers",
        "description": "Returns the scrapers of your workspace, newest first, 20 per page. Deleted scrapers are not returned.\n\nUse it to show a history or to find a scraper id. To follow one extraction, poll `GET /linkedin-scrappers/{id}` instead: it is lighter and returns the same object.\n\nFor running segmented scrapers (large searches split into chunks, Grow and Scale plans), two computed fields are added: `nextRunningSegment` (when the next chunk is scheduled) and `betweenSegment` (true when no chunk is being extracted right now).\n\nPitfall: each object also contains internal fields not described here (cursors, legacy ids). Do not rely on them.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number, starting at 0. 20 scrapers per page. Missing, non numeric or negative values mean 0. An empty `scraps` array means you went past the last page.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "example": 0
          }
        ],
        "responses": {
          "200": {
            "description": "One page of scrapers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperListResponse"
                },
                "example": {
                  "success": true,
                  "scraps": [
                    {
                      "_id": "66f1a2b3c4d5e6f7a8b9c0d1",
                      "authes": [
                        "66e0f1a2b3c4d5e6f7a8b9c0",
                        "66e0f1a2b3c4d5e6f7a8b9c1"
                      ],
                      "name": "Acme CTOs France",
                      "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))))",
                      "scrapType": "people",
                      "status": "running",
                      "totalContact": 7400,
                      "scrapIndex": 5000,
                      "maxContacts": 0,
                      "segmented": true,
                      "segments": [
                        {
                          "_id": "66f1a2b3c4d5e6f7a8b9c101",
                          "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))%2C(type%3ACOMPANY_HEADCOUNT%2Cvalues%3AList((id%3AB%2Ctext%3A1-10%2CselectionType%3AINCLUDED)))))",
                          "scrapIndex": 2500,
                          "count": 2500,
                          "complete": true
                        },
                        {
                          "_id": "66f1a2b3c4d5e6f7a8b9c102",
                          "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))%2C(type%3ACOMPANY_HEADCOUNT%2Cvalues%3AList((id%3AC%2Ctext%3A11-50%2CselectionType%3AINCLUDED)))))",
                          "scrapIndex": 2500,
                          "count": 2500,
                          "complete": true
                        },
                        {
                          "_id": "66f1a2b3c4d5e6f7a8b9c103",
                          "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))%2C(type%3ACOMPANY_HEADCOUNT%2Cvalues%3AList((id%3AD%2Ctext%3A51-200%2CselectionType%3AINCLUDED)))))",
                          "scrapIndex": 0,
                          "count": 2400
                        }
                      ],
                      "estimatedEnd": "2026-10-12T13:40:00.000Z",
                      "nextRunningSegment": "2026-10-12T09:11:00.000Z",
                      "betweenSegment": true,
                      "date": "2026-10-11T08:02:41.512Z",
                      "lastUpdate": "2026-10-11T11:14:03.220Z",
                      "enrichEnabled": false,
                      "enrichIndex": 0,
                      "enrichFound": 0,
                      "enrichPhoneEnabled": false,
                      "enrichPhoneIndex": 0,
                      "enrichPhoneFound": 0,
                      "webhooks": [
                        {
                          "_id": "66f1a2b3c4d5e6f7a8b9c0f1",
                          "webhookUrl": "https://hooks.acme.example/emelia/scrapper",
                          "events": [
                            "FINISHED",
                            "ERROR"
                          ]
                        }
                      ],
                      "convertedListId": null
                    },
                    {
                      "_id": "66fd0e1f2a3b4c5d6e7f8a90",
                      "authes": [
                        "66e0f1a2b3c4d5e6f7a8b9c0"
                      ],
                      "name": "Acme marketing leads",
                      "url": "https://www.linkedin.com/sales/lists/people/7212345678901234567?sortCriteria=CREATED_TIME&sortOrder=DESCENDING",
                      "scrapType": "people",
                      "status": "done",
                      "totalContact": 1840,
                      "scrapIndex": 1840,
                      "maxContacts": 0,
                      "segmented": false,
                      "segments": [],
                      "date": "2026-10-02T14:05:09.000Z",
                      "lastUpdate": "2026-10-02T14:41:55.000Z",
                      "enrichEnabled": true,
                      "startEnrichDate": "2026-10-02T15:20:11.000Z",
                      "enrichIndex": 1840,
                      "enrichFound": 1022,
                      "enrichPhoneEnabled": false,
                      "enrichPhoneIndex": 0,
                      "enrichPhoneFound": 0,
                      "webhooks": [],
                      "convertedListId": "66f2b3c4d5e6f7a8b9c0d1e2"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperCreate",
        "summary": "Create a LinkedIn scraper (start an extraction)",
        "description": "Starts extracting the people (or companies) of a LinkedIn search into a CSV. The extraction runs in the background on Emelia servers: this call only creates the scraper and returns its id (`scrapId`). Follow it with `GET /linkedin-scrappers/{id}` or with a webhook (`POST /linkedin-scrappers/{id}/webhook`).\n\n**Accepted URLs and accounts**\n- Sales Navigator people search (`https://www.linkedin.com/sales/search/people...`), lead list (`.../sales/lists/people/...`) or account search (`.../sales/search/company...`, companies instead of people). `authes` must contain ids of LinkedIn accounts connected for scraping (the `authId` returned by `POST /linkedin-scrappers/authes`).\n- Classic LinkedIn people search (`https://www.linkedin.com/search/results/people/...`). It runs on a LinkedIn account connected for campaigns: `authes` must contain ids of LinkedIn accounts connected for campaigns, not of accounts connected for scraping.\n- Any other `https://linkedin.com/sales/...` URL is accepted here but the scraper then stops with the error `BAD_SALES_NAV_URL`.\n\n**Volume limits** (applied during the extraction, not at creation)\n- Sales Navigator never returns more than 2,500 results per search (100 pages of 25). Lead lists and saved searches are capped at 2,500, searches filtered on your network (`CONNECTION_OF`) at 1,000, account searches at 1,000 companies, classic searches at 1,000 people.\n- Grow and Scale plans: a search with 3,000 results or more that contains filters is split automatically into chunks of at most 2,500 (`segmented: true`) and extracted over several days, at 2,500 contacts per day per LinkedIn account (5,000 for an account with a Sales Navigator Advanced seat). Give several accounts in `authes` to go faster. Above 80,000 results the scraper stops with `TOO_MUCH_RESULT`.\n- Start plan and free use: capped at 2,500, no automatic split.\n- `max_contacts` stops the extraction early. On Sales Navigator it is applied per page of 25 (you can get up to 25 more) and it is ignored for segmented searches. On classic searches it is exact.\n\n**Rules checked by this call**: the name is unique among your scrapers; with a single account and no `plannedStart`, that account must not already run a scraper; every account exists, belongs to you and is valid; `plannedStart` is at least one hour ahead; your workspace has an active subscription, or has not used its free extraction yet, or still has credits (without subscription, extraction costs about 1 credit per 2 contacts and pauses when credits run out). Fields not listed in the schema are rejected.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScrapperCreateRequest"
              },
              "examples": {
                "salesNavigator": {
                  "summary": "Sales Navigator search, starts now",
                  "value": {
                    "name": "Acme CTOs France",
                    "authes": [
                      "66e0f1a2b3c4d5e6f7a8b9c0"
                    ],
                    "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))))"
                  }
                },
                "classicSearch": {
                  "summary": "Classic LinkedIn search on a campaign account, 300 contacts max",
                  "value": {
                    "name": "Acme classic search",
                    "authes": [
                      "66d0e1f2a3b4c5d6e7f8a9b0"
                    ],
                    "url": "https://www.linkedin.com/search/results/people/?keywords=acme%20cto",
                    "max_contacts": 300
                  }
                },
                "scheduled": {
                  "summary": "Scheduled start on two accounts",
                  "value": {
                    "name": "Acme CTOs France (Monday)",
                    "authes": [
                      "66e0f1a2b3c4d5e6f7a8b9c0",
                      "66e0f1a2b3c4d5e6f7a8b9c1"
                    ],
                    "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))))",
                    "plannedStart": "2026-10-12T07:00:00.000Z"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Scrapper created. `status` is `running` (or `planified` with `plannedStart`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperCreateResponse"
                },
                "example": {
                  "success": true,
                  "scrapId": "66f1a2b3c4d5e6f7a8b9c0d1"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request or rule not met. Validation errors carry `code: INVALID_REQUEST`; business errors only have `error`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "validation": {
                    "summary": "Validation error",
                    "value": {
                      "error": "\"authes\" must contain at least 1 items",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "unknownField": {
                    "summary": "Field not allowed",
                    "value": {
                      "error": "\"maxContacts\" is not allowed",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "nameTaken": {
                    "summary": "Name already used",
                    "value": {
                      "error": "A scrap with this name already exists"
                    }
                  },
                  "badUrl": {
                    "summary": "URL neither Sales Navigator nor classic people search",
                    "value": {
                      "error": "Invalid Sales Navigator URL"
                    }
                  },
                  "alreadyRunning": {
                    "summary": "The account already runs a scrapper",
                    "value": {
                      "error": "You already have a running scrap"
                    }
                  },
                  "invalidAccount": {
                    "summary": "Unknown, foreign, duplicated or invalid account id",
                    "value": {
                      "error": "Invalid credentials. Some on these cookies may be invalid"
                    }
                  },
                  "noSubscription": {
                    "summary": "No subscription, free extraction used and no credits",
                    "value": {
                      "error": "You must have an active subscription to use the scrapper"
                    }
                  },
                  "tooEarly": {
                    "summary": "plannedStart less than one hour ahead",
                    "value": {
                      "error": "You must at least schedule the scrap 1 hour in advance"
                    }
                  },
                  "badAccountId": {
                    "summary": "Malformed account id",
                    "value": {
                      "error": "Cast to ObjectId failed for value \"abc\" at path \"_id\" for model \"LinkedinAuthScrap\""
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin-scrappers/{id}": {
      "get": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperGet",
        "summary": "Get one LinkedIn scraper (poll its progress)",
        "description": "Returns one scraper with its progress. This is the call to poll while an extraction or an enrichment runs (every 30 to 60 seconds is enough).\n\n**Reading the progress**\n- `status`: see `ScrapperStatus`. The extraction is over when `status` is `done`; `error` gives the reason when `status` is `error`.\n- `scrapIndex` contacts extracted out of `totalContact` (0 until LinkedIn reports the result count, then capped by the plan rules).\n- Segmented searches: `segments`, `estimatedEnd`, `nextRunningSegment`, `betweenSegment`.\n- After `POST /{id}/enrich`: `status` is `enriching`, then `done` again. `enrichIndex` and `enrichFound` are only filled at the end of the enrichment.\n- `convertedListId`: id of the Emelia list automatically created from a finished scraper (usually within a few minutes after `done`), `null` before.\n\nUnlike the list route, this response also contains `userId`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Scrapper id (`_id` returned by `POST /linkedin-scrappers` or `GET /linkedin-scrappers`). 24 hexadecimal characters.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "66f1a2b3c4d5e6f7a8b9c0d1"
          }
        ],
        "responses": {
          "200": {
            "description": "The scraper.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperGetResponse"
                },
                "example": {
                  "success": true,
                  "scrap": {
                    "_id": "66f1a2b3c4d5e6f7a8b9c0d1",
                    "userId": "65a0b1c2d3e4f5a6b7c8d9e0",
                    "authes": [
                      "66e0f1a2b3c4d5e6f7a8b9c0",
                      "66e0f1a2b3c4d5e6f7a8b9c1"
                    ],
                    "name": "Acme CTOs France",
                    "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))))",
                    "scrapType": "people",
                    "status": "running",
                    "totalContact": 7400,
                    "scrapIndex": 5000,
                    "maxContacts": 0,
                    "segmented": true,
                    "segments": [
                      {
                        "_id": "66f1a2b3c4d5e6f7a8b9c101",
                        "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))%2C(type%3ACOMPANY_HEADCOUNT%2Cvalues%3AList((id%3AB%2Ctext%3A1-10%2CselectionType%3AINCLUDED)))))",
                        "scrapIndex": 2500,
                        "count": 2500,
                        "complete": true
                      },
                      {
                        "_id": "66f1a2b3c4d5e6f7a8b9c102",
                        "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))%2C(type%3ACOMPANY_HEADCOUNT%2Cvalues%3AList((id%3AC%2Ctext%3A11-50%2CselectionType%3AINCLUDED)))))",
                        "scrapIndex": 2500,
                        "count": 2500,
                        "complete": true
                      },
                      {
                        "_id": "66f1a2b3c4d5e6f7a8b9c103",
                        "url": "https://www.linkedin.com/sales/search/people?query=(filters%3AList((type%3ACURRENT_TITLE%2Cvalues%3AList((text%3ACTO%2CselectionType%3AINCLUDED)))%2C(type%3AREGION%2Cvalues%3AList((text%3AFrance%2CselectionType%3AINCLUDED)))%2C(type%3ACOMPANY_HEADCOUNT%2Cvalues%3AList((id%3AD%2Ctext%3A51-200%2CselectionType%3AINCLUDED)))))",
                        "scrapIndex": 0,
                        "count": 2400
                      }
                    ],
                    "estimatedEnd": "2026-10-12T13:40:00.000Z",
                    "nextRunningSegment": "2026-10-12T09:11:00.000Z",
                    "betweenSegment": true,
                    "date": "2026-10-11T08:02:41.512Z",
                    "lastUpdate": "2026-10-11T11:14:03.220Z",
                    "enrichEnabled": false,
                    "enrichIndex": 0,
                    "enrichFound": 0,
                    "enrichPhoneEnabled": false,
                    "enrichPhoneIndex": 0,
                    "enrichPhoneFound": 0,
                    "webhooks": [
                      {
                        "_id": "66f1a2b3c4d5e6f7a8b9c0f1",
                        "webhookUrl": "https://hooks.acme.example/emelia/scrapper",
                        "events": [
                          "FINISHED",
                          "ERROR"
                        ]
                      }
                    ],
                    "convertedListId": null
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown or deleted scraper.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown or deleted scrapper (current behavior)",
                    "value": {
                      "error": "Cannot read properties of null (reading 'status')"
                    },
                    "description": "Known issue: the server crashes on a missing scrapper and relays the raw JavaScript error instead of a not found message."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "404": {
            "description": "The id is not 24 hexadecimal characters: no route matches and the server answers with its default HTML page (not JSON).",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                },
                "example": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot GET /linkedin-scrappers/abc</pre>\n</body>\n</html>\n"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperDelete",
        "summary": "Delete a LinkedIn scraper",
        "description": "Deletes a scraper. It disappears from the API and from the app. A Sales Navigator extraction in progress is stopped and the next chunks of a segmented extraction do not start.\n\nPitfalls: the deletion is logical (Emelia keeps the extracted file). A classic LinkedIn search that is already extracting is not stopped: it runs to its end (and still sends its FINISHED webhook). The Emelia list created from the scraper (`convertedListId`) is not deleted.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Scrapper id (`_id` returned by `POST /linkedin-scrappers` or `GET /linkedin-scrappers`). 24 hexadecimal characters.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "66f1a2b3c4d5e6f7a8b9c0d1"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperSuccessResponse"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Unknown scraper or malformed id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown or deleted scrapper",
                    "value": {
                      "error": "Scrap not found"
                    }
                  },
                  "malformedId": {
                    "summary": "Malformed id",
                    "value": {
                      "error": "Cast to ObjectId failed for value \"abc\" at path \"_id\" for model \"Scrap\""
                    },
                    "description": "Returned when the id in the path is not a valid ObjectId."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin-scrappers/{id}/pause": {
      "put": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperPause",
        "summary": "Pause a segmented extraction",
        "description": "Pauses a large extraction split into chunks (`segmented: true`, Grow and Scale plans). Only allowed while `status` is `running`. Other scrapers cannot be paused.\n\nThe pause is not immediate: the chunk being extracted goes to its end, only the next chunks are unscheduled. `status` becomes `paused`. Restart with `PUT /linkedin-scrappers/{id}/resume`.\n\nA scraper can also be paused by Emelia when it runs on credits (no subscription) and the credits run out.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Scrapper id (`_id` returned by `POST /linkedin-scrappers` or `GET /linkedin-scrappers`). 24 hexadecimal characters.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "66f1a2b3c4d5e6f7a8b9c0d1"
          }
        ],
        "responses": {
          "200": {
            "description": "Paused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperSuccessResponse"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The scraper cannot be paused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown or deleted scrapper",
                    "value": {
                      "error": "Scrap not found"
                    }
                  },
                  "notRunning": {
                    "summary": "Status is not running",
                    "value": {
                      "error": "Scrap is not running"
                    }
                  },
                  "notSegmented": {
                    "summary": "Not a segmented extraction",
                    "value": {
                      "error": "Your scrap must be segmented to be paused"
                    }
                  },
                  "malformedId": {
                    "summary": "Malformed id",
                    "value": {
                      "error": "Cast to ObjectId failed for value \"abc\" at path \"_id\" for model \"Scrap\""
                    },
                    "description": "Returned when the id in the path is not a valid ObjectId."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin-scrappers/{id}/resume": {
      "put": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperResume",
        "summary": "Resume a paused, scheduled or failed scraper",
        "description": "Restarts a scraper that is:\n- `paused` (by you, or automatically when credits ran out);\n- `planified`: it starts now instead of at `plannedStart`;\n- in `error` with `INVALID_COOKIE` or `RATE_LIMIT`, once its LinkedIn account is valid again (refresh it first with `PUT /linkedin-scrappers/authes/{id}`, or reconnect the campaign account for a classic search).\n\nOther error codes are final: create a new scraper. A segmented extraction restarts with its remaining chunks. For Sales Navigator, a `RATE_LIMIT` stop is retried automatically about 12 hours later.\n\nChecks: an active subscription or remaining credits; no other running scraper on the same account.\n\nKnown issues: the running scraper check can refuse the resume with `You already have a running scrap` as soon as any other scraper of the workspace is running, even on another account. An unknown id is reported as `This scrap is in invalid state` (or as the running scraper message), not as not found.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Scrapper id (`_id` returned by `POST /linkedin-scrappers` or `GET /linkedin-scrappers`). 24 hexadecimal characters.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "66f1a2b3c4d5e6f7a8b9c0d1"
          }
        ],
        "responses": {
          "200": {
            "description": "Restarted (`status` is `running`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperSuccessResponse"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "The scraper cannot be resumed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "noSubscription": {
                    "summary": "No active subscription and no credits",
                    "value": {
                      "error": "You must have an active subscription to use the scrapper"
                    }
                  },
                  "alreadyRunning": {
                    "summary": "Another scrapper is running",
                    "value": {
                      "error": "You already have a running scrap"
                    }
                  },
                  "invalidAccount": {
                    "summary": "INVALID_COOKIE or RATE_LIMIT but the account is still invalid",
                    "value": {
                      "error": "You must set a valid cookie before restarting this scrap"
                    }
                  },
                  "invalidState": {
                    "summary": "Status or error code that cannot be resumed (or unknown id)",
                    "value": {
                      "error": "This scrap is in invalid state"
                    }
                  },
                  "malformedId": {
                    "summary": "Malformed id",
                    "value": {
                      "error": "Cast to ObjectId failed for value \"abc\" at path \"_id\" for model \"Scrap\""
                    },
                    "description": "Returned when the id in the path is not a valid ObjectId."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin-scrappers/{id}/split": {
      "post": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperSplit",
        "summary": "Detach the part already extracted from a segmented extraction",
        "description": "Lets you use now what a long segmented extraction has already collected, while the rest goes on.\n\nEffect: a new scraper named `<name> 1` is created with `status` `done` and the contacts extracted so far; its id is returned in `scrapId`. The original scraper keeps its id, is renamed `<name> 2`, keeps only the chunks not finished yet and restarts its counter (`scrapIndex` back to 0, `totalContact` reduced accordingly). The new part is then imported automatically into an Emelia list, like any finished scraper.\n\nTypical use: enrich or download the first contacts of an extraction that will take days (enrichment requires `status` `done`).\n\nConditions: `segmented` is true, `status` is neither `enriching` nor `done`, at least one contact was extracted, and nothing was written during the last 10 minutes (otherwise retry later).\n\nNot to be confused with the automatic split of large searches into chunks, which Emelia does by itself on Grow and Scale plans.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Scrapper id (`_id` returned by `POST /linkedin-scrappers` or `GET /linkedin-scrappers`). 24 hexadecimal characters.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "66f1a2b3c4d5e6f7a8b9c0d1"
          }
        ],
        "responses": {
          "200": {
            "description": "Split done. `scrapId` is the NEW finished scraper.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperSplitResponse"
                },
                "example": {
                  "success": true,
                  "scrapId": "66f1a2b3c4d5e6f7a8b9c0d2"
                }
              }
            }
          },
          "400": {
            "description": "The scraper cannot be split.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown or deleted scrapper",
                    "value": {
                      "error": "Scrap not found"
                    }
                  },
                  "notSegmented": {
                    "summary": "Not a segmented extraction (misleading message)",
                    "value": {
                      "error": "This scrap is not running"
                    }
                  },
                  "invalidState": {
                    "summary": "Status enriching or done, or nothing extracted yet",
                    "value": {
                      "error": "This scrap is in invalid state"
                    }
                  },
                  "busy": {
                    "summary": "A chunk wrote data less than 10 minutes ago",
                    "value": {
                      "error": "This scrap is currently extracting data. Please retry in a few minutes"
                    }
                  },
                  "malformedId": {
                    "summary": "Malformed id",
                    "value": {
                      "error": "Cast to ObjectId failed for value \"abc\" at path \"_id\" for model \"Scrap\""
                    },
                    "description": "Returned when the id in the path is not a valid ObjectId."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin-scrappers/{id}/rename": {
      "put": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperRename",
        "summary": "Rename a LinkedIn scraper",
        "description": "Changes the name of a scraper. 1 to 100 characters, unique among your scrapers.\n\nPitfall: renaming a scraper to its current name is refused (`A scrap with this name already exists`).",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Scrapper id (`_id` returned by `POST /linkedin-scrappers` or `GET /linkedin-scrappers`). 24 hexadecimal characters.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "66f1a2b3c4d5e6f7a8b9c0d1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScrapperRenameRequest"
              },
              "example": {
                "name": "Acme CTOs France Q4"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Renamed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperSuccessResponse"
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid name or unknown scraper.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "empty": {
                    "summary": "Empty name",
                    "value": {
                      "error": "\"name\" is not allowed to be empty",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "tooLong": {
                    "summary": "More than 100 characters",
                    "value": {
                      "error": "Scrap name is too long"
                    }
                  },
                  "notFound": {
                    "summary": "Unknown or deleted scrapper",
                    "value": {
                      "error": "Scrap not found"
                    }
                  },
                  "nameTaken": {
                    "summary": "Name already used",
                    "value": {
                      "error": "A scrap with this name already exists"
                    }
                  },
                  "malformedId": {
                    "summary": "Malformed id",
                    "value": {
                      "error": "Cast to ObjectId failed for value \"abc\" at path \"_id\" for model \"Scrap\""
                    },
                    "description": "Returned when the id in the path is not a valid ObjectId."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin-scrappers/{id}/enrich": {
      "post": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperEnrich",
        "summary": "Find the emails of a finished scraper",
        "description": "Looks for the professional email of every contact of a finished people extraction. Asynchronous: `status` becomes `enriching`, then `done` again when the emails are written into the CSV. An enrichment is closed after about one hour at most, with what was found by then.\n\n**Credits**: you need at least as many credits as `totalContact`. That amount is taken when the enrichment starts, then the credits of the contacts without an email are given back at the end: you pay 1 credit per email found. Your balance therefore drops temporarily during the enrichment.\n\n`country` is an ISO 3166-1 alpha-2 code (for example `FR`) used as a hint for the search. It is not validated.\n\nConditions: `status` is `done` and the scraper was never enriched (`enrichEnabled` false). To enrich the first part of a long segmented extraction, split it first (`POST /linkedin-scrappers/{id}/split`). Only for people searches: an account (company) search has no person to look up.\n\nAfter completion: `enrichFound` is the number of emails found. Download with `format=1` to get only the contacts with an email, and read the columns `email_emelia`, `qualification_emelia`, `gender_emelia`, `domain_emelia`. No webhook is sent at the end of an enrichment: poll `GET /linkedin-scrappers/{id}`.\n\nPhone number enrichment of a scraper is not available in this API.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Scrapper id (`_id` returned by `POST /linkedin-scrappers` or `GET /linkedin-scrappers`). 24 hexadecimal characters.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "66f1a2b3c4d5e6f7a8b9c0d1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScrapperEnrichRequest"
              },
              "example": {
                "country": "FR"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Enrichment started (`status` is now `enriching`). `status` in this response is always the boolean `true`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperEnrichResponse"
                },
                "example": {
                  "success": true,
                  "status": true
                }
              }
            }
          },
          "400": {
            "description": "The enrichment cannot start.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "notFinished": {
                    "summary": "Status is not done",
                    "value": {
                      "error": "Scrap is not finished"
                    }
                  },
                  "alreadyEnriched": {
                    "summary": "Already enriched",
                    "value": {
                      "error": "Scrap is already enriched"
                    }
                  },
                  "notEnoughCredits": {
                    "summary": "Fewer credits than totalContact",
                    "value": {
                      "error": "You don't have enough credits to enrich this scrap"
                    }
                  },
                  "noCredits": {
                    "summary": "No credits at all",
                    "value": {
                      "error": "You don't have any enrich credits. Buy more credits on Subscription page."
                    }
                  },
                  "missingCountry": {
                    "summary": "country missing",
                    "value": {
                      "error": "\"country\" is required",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "notFound": {
                    "summary": "Unknown or deleted scrapper (current behavior)",
                    "value": {
                      "error": "Cannot read properties of null (reading 'status')"
                    },
                    "description": "Known issue: the server crashes on a missing scrapper and relays the raw JavaScript error instead of a not found message."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin-scrappers/{id}/download": {
      "get": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperDownload",
        "summary": "Download the contacts of a scraper (CSV)",
        "description": "Returns the CSV of a scraper as a string in `data` (a JSON response, not a file). Separator `;`, first line = column names, UTF-8 without BOM, values quoted only when needed. Large extractions give large responses (several MB).\n\nIt can be called while the extraction runs: you get what has been collected so far.\n\n`format`: `1` = only contacts with an email (requires a finished enrichment with at least one email), `2` = only contacts without an email (all contacts if the scraper was never enriched), `3` = all contacts.\n\n**People columns** (Sales Navigator and classic searches): id, firstName, lastName, fullName, location, isPremium, openProfile, linkedinUrlProfile, summary, title, company, companyId, companyLocation, companyDescription, companySize, estimatedMinRevenue, estimatedMaxRevenue, industry, description, companyPage, companyWebsite, type. `id` is the Sales Navigator profile key and can contain commas. Fields that LinkedIn does not provide stay empty (classic searches fill fewer columns).\n\n**After an email enrichment** the file is rewritten in this order: id, email_emelia, qualification_emelia, gender_emelia, firstName, lastName, fullName, location, isPremium, openProfile, linkedinUrlProfile, phone_number_emelia, phone_qualification_emelia, phone_country_emelia, summary, title, company, companyId, companyLocation, companyDescription, companySize, estimatedMaxRevenue, estimatedMinRevenue, industry, description, companyPage, companyWebsite, domain_emelia, type.\n\n**Account search columns** (companies): id, companyName, companyPage, companyDescription, companySize, employeeCountRange, industry, companyWebsite, companyLocation, yearFounded, type, estimatedMinRevenue, estimatedMaxRevenue.\n\nOnce a scraper is `done`, its contacts are also imported automatically into an Emelia list (`convertedListId`), that you can manage with the Lists endpoints.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Scrapper id (`_id` returned by `POST /linkedin-scrappers` or `GET /linkedin-scrappers`). 24 hexadecimal characters.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "66f1a2b3c4d5e6f7a8b9c0d1"
          },
          {
            "name": "format",
            "in": "query",
            "required": true,
            "description": "1 = contacts with an email only, 2 = contacts without an email only, 3 = all contacts.",
            "schema": {
              "type": "integer",
              "enum": [
                1,
                2,
                3
              ]
            },
            "example": 3
          }
        ],
        "responses": {
          "200": {
            "description": "CSV content in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperDownloadResponse"
                },
                "examples": {
                  "allContacts": {
                    "summary": "format=3 on a scrapper that was not enriched",
                    "value": {
                      "success": true,
                      "data": "id;firstName;lastName;fullName;location;isPremium;openProfile;linkedinUrlProfile;summary;title;company;companyId;companyLocation;companyDescription;companySize;estimatedMinRevenue;estimatedMaxRevenue;industry;description;companyPage;companyWebsite;type\nACwAAA1b2c3dEFg,NAME_SEARCH,xY1z;Jane;Doe;Jane Doe;Lyon, Auvergne-Rhone-Alpes, France;true;false;https://www.linkedin.com/in/ACwAAA1b2c3dEFg;;CTO;Acme;1234567;Lyon, France;Acme builds reusable rockets.;51-200;10;50;Aerospace;;https://www.linkedin.com/company/acme-example;https://acme.example;Privately Held\nACwAAB9z8y7xWVu,NAME_SEARCH,aB2c;John;Roe;John Roe;Paris, Ile-de-France, France;false;false;https://www.linkedin.com/in/ACwAAB9z8y7xWVu;;Chief Technology Officer;Globex;7654321;Paris, France;Globex makes widgets.;11-50;1;5;Software Development;;https://www.linkedin.com/company/globex-example;https://globex.example;Privately Held"
                    }
                  },
                  "withEmail": {
                    "summary": "format=1 after an enrichment",
                    "value": {
                      "success": true,
                      "data": "id;email_emelia;qualification_emelia;gender_emelia;firstName;lastName;fullName;location;isPremium;openProfile;linkedinUrlProfile;phone_number_emelia;phone_qualification_emelia;phone_country_emelia;summary;title;company;companyId;companyLocation;companyDescription;companySize;estimatedMaxRevenue;estimatedMinRevenue;industry;description;companyPage;companyWebsite;domain_emelia;type\nACwAAA1b2c3dEFg,NAME_SEARCH,xY1z;jane.doe@acme.example;valid;female;Jane;Doe;Jane Doe;Lyon, Auvergne-Rhone-Alpes, France;true;false;https://www.linkedin.com/in/ACwAAA1b2c3dEFg;;;;;CTO;Acme;1234567;Lyon, France;Acme builds reusable rockets.;51-200;50;10;Aerospace;;https://www.linkedin.com/company/acme-example;https://acme.example;acme.example;Privately Held"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid format, unknown scraper or nothing to export.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "badFormat": {
                    "summary": "format outside 1, 2, 3",
                    "value": {
                      "error": "\"format\" must be one of [1, 2, 3]",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "missingFormat": {
                    "summary": "format missing or not a number",
                    "value": {
                      "error": "\"format\" must be a number",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "notFound": {
                    "summary": "Unknown or deleted scrapper",
                    "value": {
                      "error": "Scrap not found"
                    }
                  },
                  "notEnriched": {
                    "summary": "format=1 on a scrapper never enriched",
                    "value": {
                      "error": "Scrap is not enriched"
                    }
                  },
                  "noEmail": {
                    "summary": "format=1 with no email found (or enrichment still running)",
                    "value": {
                      "error": "Scrap does not contain any contact with email"
                    }
                  },
                  "nothingYet": {
                    "summary": "Nothing extracted yet",
                    "value": {
                      "error": "The specified key does not exist."
                    }
                  },
                  "malformedId": {
                    "summary": "Malformed id",
                    "value": {
                      "error": "Cast to ObjectId failed for value \"abc\" at path \"_id\" for model \"Scrap\""
                    },
                    "description": "Returned when the id in the path is not a valid ObjectId."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin-scrappers/{id}/webhook": {
      "post": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperAddWebhook",
        "summary": "Register a webhook on a scraper",
        "description": "Registers a URL that Emelia calls when this scraper finishes or stops on an error. Register it right after creating the scraper. Several URLs can be registered on the same scraper; the same URL cannot be added twice.\n\n**What is sent**: a `POST` with a JSON body `{ \"event\", \"id\", \"name\", \"error\" }` (see `ScrapperWebhookEvent`). `FINISHED` is sent when the extraction ends (`status` `done`, all chunks included). `ERROR` is sent when the scraper stops with an error code, at most once per hour for the same scraper and code.\n\n**Current behavior to know**: every registered URL receives every event, whatever its `events` list; `ERROR` is only sent if at least one registered URL asked for `ERROR`. One attempt only: no retry, no signature, no custom header, so check the scraper with `GET /linkedin-scrappers/{id}` when you receive a call. Nothing is sent at the end of an enrichment.\n\n`events` must contain `FINISHED` or `ERROR`; other values are accepted and ignored.\n\nKnown issue: the success response is currently an empty JSON object `{}` (no `success` field). Treat HTTP 200 as success.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Scrapper id (`_id` returned by `POST /linkedin-scrappers` or `GET /linkedin-scrappers`). 24 hexadecimal characters.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "66f1a2b3c4d5e6f7a8b9c0d1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScrapperWebhookRequest"
              },
              "example": {
                "webhookUrl": "https://hooks.acme.example/emelia/scrapper",
                "events": [
                  "FINISHED",
                  "ERROR"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Registered. Current body: `{}`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperEmptyResponse"
                },
                "example": {}
              }
            }
          },
          "400": {
            "description": "Invalid webhook or unknown scraper.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalidUrl": {
                    "summary": "Not a URI",
                    "value": {
                      "error": "\"webhookUrl\" must be a valid uri",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "invalidEvent": {
                    "summary": "No FINISHED nor ERROR in events",
                    "value": {
                      "error": "Invalid webhook event"
                    }
                  },
                  "exists": {
                    "summary": "URL already registered on this scrapper",
                    "value": {
                      "error": "Webhook already exists"
                    }
                  },
                  "notFound": {
                    "summary": "Unknown or deleted scrapper",
                    "value": {
                      "error": "Scrap not found"
                    }
                  },
                  "malformedId": {
                    "summary": "Malformed id",
                    "value": {
                      "error": "Cast to ObjectId failed for value \"abc\" at path \"_id\" for model \"Scrap\""
                    },
                    "description": "Returned when the id in the path is not a valid ObjectId."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        },
        "callbacks": {
          "scrapperEvent": {
            "{$request.body#/webhookUrl}": {
              "post": {
                "summary": "Scrapper event sent to your URL",
                "description": "Sent by Emelia scraping servers with `Content-Type: application/json`. The response is ignored and nothing is retried.",
                "requestBody": {
                  "required": true,
                  "content": {
                    "application/json": {
                      "schema": {
                        "$ref": "#/components/schemas/ScrapperWebhookEvent"
                      },
                      "examples": {
                        "finished": {
                          "summary": "Extraction finished",
                          "value": {
                            "event": "FINISHED",
                            "id": "66f1a2b3c4d5e6f7a8b9c0d1",
                            "name": "Acme CTOs France"
                          }
                        },
                        "error": {
                          "summary": "Extraction stopped on an error",
                          "value": {
                            "event": "ERROR",
                            "id": "66f1a2b3c4d5e6f7a8b9c0d1",
                            "name": "Acme CTOs France",
                            "error": "INVALID_COOKIE"
                          }
                        }
                      }
                    }
                  }
                },
                "responses": {
                  "200": {
                    "description": "Any answer is accepted and ignored."
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperRemoveWebhook",
        "summary": "Remove a webhook from a scraper",
        "description": "Unregisters a webhook URL (exact match). The URL goes in the JSON body of the `DELETE` request: make sure your HTTP client sends a body with `DELETE`. Removing a URL that is not registered is not an error.\n\nKnown issue: the success response is currently an empty JSON object `{}`.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Scrapper id (`_id` returned by `POST /linkedin-scrappers` or `GET /linkedin-scrappers`). 24 hexadecimal characters.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "66f1a2b3c4d5e6f7a8b9c0d1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScrapperWebhookDeleteRequest"
              },
              "example": {
                "webhookUrl": "https://hooks.acme.example/emelia/scrapper"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Removed (or was not registered). Current body: `{}`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperEmptyResponse"
                },
                "example": {}
              }
            }
          },
          "400": {
            "description": "Invalid body or unknown scraper.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missingUrl": {
                    "summary": "No body or no webhookUrl",
                    "value": {
                      "error": "\"webhookUrl\" is required",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "notFound": {
                    "summary": "Unknown or deleted scrapper",
                    "value": {
                      "error": "Scrap not found"
                    }
                  },
                  "malformedId": {
                    "summary": "Malformed id",
                    "value": {
                      "error": "Cast to ObjectId failed for value \"abc\" at path \"_id\" for model \"Scrap\""
                    },
                    "description": "Returned when the id in the path is not a valid ObjectId."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin-scrappers/{id}/filter": {
      "post": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperRunFilter",
        "summary": "Remove contacts already extracted by your other scrapers",
        "description": "Removes from this scraper the LinkedIn profiles that already appear in your 10 most recent finished scrapers (comparison on the profile `id` column). Run it on a finished scraper before enriching it: fewer contacts, fewer credits.\n\n`filters` is a bit field. Only `1` exists today (remove profiles found in your other scrapers); use `1`.\n\nAsynchronous: the call returns at once and sets `processing` to true on the scraper. When the filter ends, `processing` is false again and `totalContact` and `scrapIndex` give the new count. Large files can take several minutes.\n\nPitfalls: the CSV is rewritten and the previous version cannot be restored through the API. The filter is not restricted to finished scrapers: do not run it while an extraction is in progress. `filters: 0` is accepted but nothing runs and `processing` stays true.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Scrapper id (`_id` returned by `POST /linkedin-scrappers` or `GET /linkedin-scrappers`). 24 hexadecimal characters.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "66f1a2b3c4d5e6f7a8b9c0d1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScrapperFilterRequest"
              },
              "example": {
                "filters": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Filter started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperFilterResponse"
                },
                "example": {
                  "success": true,
                  "data": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid body or unknown scraper.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalidFilters": {
                    "summary": "filters not an integer",
                    "value": {
                      "error": "\"filters\" must be an integer",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "notFound": {
                    "summary": "Unknown or deleted scrapper",
                    "value": {
                      "error": "Scrap not found"
                    }
                  },
                  "malformedId": {
                    "summary": "Malformed id",
                    "value": {
                      "error": "Cast to ObjectId failed for value \"abc\" at path \"_id\" for model \"Scrap\""
                    },
                    "description": "Returned when the id in the path is not a valid ObjectId."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin-scrappers/authes": {
      "post": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperCreateAuth",
        "summary": "Connect a LinkedIn account for scraping (session cookies)",
        "description": "Connects a LinkedIn account for Sales Navigator scraping by sending the cookies of a browser session where the user is logged in to LinkedIn and Sales Navigator. No password is involved. The Emelia browser extension does this for you; use this route to automate it.\n\n`li_at` is required. Strongly recommended: `li_a` (Sales Navigator session), `ua` (user agent of the browser the cookies come from), `additionnalsCookie` (the other linkedin.com cookies, as objects `{ name, value, domain }`), `viewport` (that browser window size) and `country` (ISO alpha-2 code: Emelia browses LinkedIn through a proxy located in this country; default the workspace country, then FR).\n\nReturns `authId`, to use in `authes` of `POST /linkedin-scrappers`. The same `li_at` cannot be registered twice. When a session expires, update the existing account with `PUT /linkedin-scrappers/authes/{id}` instead of creating a new one.\n\nEmelia stores these cookies and refreshes them after each scraping session. There is currently no API route to remove a connected scraping account.\n\nPitfalls: `jsessionid` is accepted but ignored (send `JSESSIONID` inside `additionnalsCookie`). Cookies sent as `\"name=value\"` strings are accepted but not used by the Sales Navigator scraper: send objects. Fields not described here are reserved for Emelia apps; any other field is rejected.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScrapperAuthCookies"
              },
              "example": {
                "li_at": "AQEDAFakeLiAtCookieForDocsOnly0000000000",
                "li_a": "AQJ2FakeLiACookieForDocsOnly0000000000",
                "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
                "additionnalsCookie": [
                  {
                    "name": "JSESSIONID",
                    "value": "\"ajax:0000000000000000000\"",
                    "domain": ".www.linkedin.com"
                  },
                  {
                    "name": "lang",
                    "value": "v=2&lang=fr-fr",
                    "domain": ".linkedin.com"
                  },
                  {
                    "name": "bcookie",
                    "value": "\"v=2&00000000-fake-0000-0000-000000000000\"",
                    "domain": ".linkedin.com"
                  }
                ],
                "viewport": {
                  "width": 1440,
                  "height": 900
                },
                "country": "FR"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Account connected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperAuthIdResponse"
                },
                "example": {
                  "success": true,
                  "authId": "66e0f1a2b3c4d5e6f7a8b9c0"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body or cookie already registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missingLiAt": {
                    "summary": "li_at missing",
                    "value": {
                      "error": "\"li_at\" is required",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "badViewport": {
                    "summary": "Incomplete viewport",
                    "value": {
                      "error": "\"viewport.height\" is required",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "unknownField": {
                    "summary": "Field not allowed",
                    "value": {
                      "error": "\"password\" is not allowed",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "duplicate": {
                    "summary": "Same li_at already registered",
                    "value": {
                      "error": "You already set up an account with this cookie"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin-scrappers/authes/{id}": {
      "put": {
        "tags": [
          "LinkedIn scraper"
        ],
        "operationId": "scrapperUpdateAuth",
        "summary": "Refresh the cookies of a connected scraping account",
        "description": "Replaces the session cookies of a scraping account (its `authId`), typically after a scraper stopped with `INVALID_COOKIE` or when the account shows `status: invalid`. `status` is set back to `valid` (the cookies are not tested at this moment). Then restart the stopped scraper with `PUT /linkedin-scrappers/{scrapperId}/resume`.\n\nSend the complete set every time: `li_a`, `ua`, `viewport`, `country` and `additionnalsCookie` that you omit are cleared, and so is the LinkedIn profile shown in the app.\n\nReturns the same account id. Only scraping accounts can be updated here (not campaign accounts).",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Account id (the `authId` returned by `POST /linkedin-scrappers/authes`). 24 hexadecimal characters.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "example": "66e0f1a2b3c4d5e6f7a8b9c0"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScrapperAuthCookies"
              },
              "example": {
                "li_at": "AQEDAFakeLiAtCookieForDocsOnly0000000000",
                "li_a": "AQJ2FakeLiACookieForDocsOnly0000000000",
                "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
                "additionnalsCookie": [
                  {
                    "name": "JSESSIONID",
                    "value": "\"ajax:0000000000000000000\"",
                    "domain": ".www.linkedin.com"
                  },
                  {
                    "name": "lang",
                    "value": "v=2&lang=fr-fr",
                    "domain": ".linkedin.com"
                  },
                  {
                    "name": "bcookie",
                    "value": "\"v=2&00000000-fake-0000-0000-000000000000\"",
                    "domain": ".linkedin.com"
                  }
                ],
                "viewport": {
                  "width": 1440,
                  "height": 900
                },
                "country": "FR"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cookies replaced, account valid again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrapperAuthIdResponse"
                },
                "example": {
                  "success": true,
                  "authId": "66e0f1a2b3c4d5e6f7a8b9c0"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body or unknown account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "missingLiAt": {
                    "summary": "li_at missing",
                    "value": {
                      "error": "\"li_at\" is required",
                      "code": "INVALID_REQUEST"
                    }
                  },
                  "notFound": {
                    "summary": "Unknown account or account of another workspace",
                    "value": {
                      "error": "Invalid cookie data"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`Authorization` header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Authentication required"
                }
              }
            }
          },
          "404": {
            "description": "The id is not 24 hexadecimal characters: no route matches and the server answers with its default HTML page (not JSON).",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                },
                "example": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot PUT /linkedin-scrappers/authes/abc</pre>\n</body>\n</html>\n"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.\n\nCurrent behavior: also returned when the per-minute API quota of the workspace is exceeded (30 requests per minute without subscription, 100 on Start, 300 on Grow, 1,000 on Scale). A 429 would be expected; retry after a minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Internal server error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/reply": {
      "post": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyReplyToEmail",
        "deprecated": true,
        "summary": "Send a reply from a connected mailbox",
        "description": "Sends an e-mail from one of your connected providers. At least one of providerId or senderEmail is required to pick the mailbox. With messageId (from the merged inbox) the message is threaded (In-Reply-To, References, quoted history appended); without it you must supply to and subject yourself. content is HTML (use <br> for line breaks). scheduledAt (ISO) schedules the send instead of sending now. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "content"
                ],
                "properties": {
                  "senderEmail": {
                    "type": "string",
                    "format": "email",
                    "description": "Sender mailbox address (alternative to providerId)."
                  },
                  "providerId": {
                    "type": "string",
                    "description": "Provider ObjectId (alternative to senderEmail)."
                  },
                  "messageId": {
                    "type": "string",
                    "description": "Message-ID of the email to reply to. Threads the reply. If Emelia cannot find it, the call fails with 'Original message not found'."
                  },
                  "subject": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "content": {
                    "type": "string",
                    "description": "HTML body. Use <br> for line breaks."
                  },
                  "scheduledAt": {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO date to schedule the send."
                  },
                  "attachments": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "url": {
                          "type": "string",
                          "format": "uri"
                        }
                      }
                    }
                  },
                  "to": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "cc": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string"
                    }
                  },
                  "bcc": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "oneOf": [
                  {
                    "required": [
                      "senderEmail"
                    ]
                  },
                  {
                    "required": [
                      "providerId"
                    ]
                  }
                ]
              },
              "example": {
                "providerId": "5f3a1b2c4d5e6f0011223344",
                "to": [
                  "jane.roe@acme.com"
                ],
                "subject": "Re: your question",
                "content": "Hello Jane,<br><br>Thanks for your reply."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reply sent (or scheduled).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/campaign/contacts": {
      "post": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyAddContactToEmailCampaign",
        "deprecated": true,
        "summary": "Add a contact to an email campaign",
        "description": "Adds one contact to the campaign (id). The contact is appended to a dedicated list named '<campaign name> from Zapier/Integromat/n8n Hooks'. Any extra key in contact beyond email/firstName/lastName/phoneNumber is stored as a custom variable. Side effects: if the campaign is FINISHED it is restarted (status RUNNING); if RUNNING the contact is scheduled immediately. Fails if the email is already in the campaign. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "contact"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Campaign ObjectId."
                  },
                  "contact": {
                    "type": "object",
                    "required": [
                      "email"
                    ],
                    "properties": {
                      "email": {
                        "type": "string",
                        "format": "email"
                      },
                      "firstName": {
                        "type": "string"
                      },
                      "lastName": {
                        "type": "string"
                      },
                      "phoneNumber": {
                        "type": "string"
                      }
                    },
                    "additionalProperties": {
                      "type": "string",
                      "description": "Any other key is stored as a custom variable on the contact."
                    }
                  }
                }
              },
              "example": {
                "id": "5f3a1b2c4d5e6f0011223344",
                "contact": {
                  "email": "john.doe@acme.com",
                  "firstName": "John",
                  "lastName": "Doe",
                  "company": "Acme"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact added.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyDeleteContactFromEmailCampaign",
        "deprecated": true,
        "summary": "Remove a contact from an email campaign",
        "description": "Removes the contact whose email matches from the campaign (id). Use id='ALL_CAMPAIGNS' to remove the email from every non-archived campaign. Note: the body field is 'email' (not 'contact'). New integrations should use the multichannel /advanced campaign endpoints instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "email"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Campaign ObjectId, or 'ALL_CAMPAIGNS'."
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              },
              "example": {
                "id": "5f3a1b2c4d5e6f0011223344",
                "email": "john.doe@acme.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact removed (always success:true, even if no contact matched).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyListEmailCampaignContacts",
        "deprecated": true,
        "summary": "List email campaign contacts (paginated)",
        "description": "Returns a page of the campaign recipients. Pagination is 0-based (page=0 is the first page). perPage defaults to 20, values below 10 are forced to 20, and values above 10000 are capped at 10000. The response envelope is { success, list, count } (NOT { contacts, total }). event filters by contact status and MUST be an array (e.g. event[]=REPLIED); a bare string is rejected (contrast with the LinkedIn variant which also accepts a string). New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Campaign ObjectId.",
            "example": "65f1a2b3c4d5e6f73478765e"
          },
          {
            "name": "page",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "description": "0-based page index (sent as a string).",
            "example": 0
          },
          {
            "name": "perPage",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Page size. Default 20, min 10 (else forced to 20), max 10000."
          },
          {
            "name": "event",
            "in": "query",
            "required": false,
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "SENT",
                  "OPENED",
                  "CLICKED",
                  "REPLIED",
                  "BOUNCED",
                  "UNSUBSCRIBED",
                  "INTERESTED",
                  "NO_ACTIVITY"
                ]
              }
            },
            "description": "Filter by contact status. Must be an array."
          },
          {
            "name": "query",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Full email match, or space-separated tokens matched against email/firstName/lastName."
          }
        ],
        "responses": {
          "200": {
            "description": "One page of contacts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "list": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LegacyContact"
                      }
                    },
                    "count": {
                      "type": "integer",
                      "description": "Total number of matching contacts."
                    }
                  },
                  "required": [
                    "success",
                    "list",
                    "count"
                  ]
                },
                "example": {
                  "success": true,
                  "count": 2,
                  "list": [
                    {
                      "_id": "5f3a1b2c4d5e6f00112233aa",
                      "email": "john.doe@acme.com",
                      "firstName": "John",
                      "lastName": "Doe",
                      "status": "SENT",
                      "mailsSent": 1,
                      "interested": "-"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/lists/contacts": {
      "post": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyAddContactToEmailList",
        "deprecated": true,
        "summary": "Add a contact to an email list",
        "description": "Adds one contact to an existing contact list (id = list ObjectId). Any extra key in contact is stored as a custom variable. Fails if the email is already in the list. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "contact"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Contact list ObjectId."
                  },
                  "contact": {
                    "type": "object",
                    "required": [
                      "email"
                    ],
                    "properties": {
                      "email": {
                        "type": "string",
                        "format": "email"
                      },
                      "firstName": {
                        "type": "string"
                      },
                      "lastName": {
                        "type": "string"
                      },
                      "phoneNumber": {
                        "type": "string"
                      }
                    },
                    "additionalProperties": {
                      "type": "string",
                      "description": "Any other key is stored as a custom variable on the contact."
                    }
                  }
                }
              },
              "example": {
                "id": "5f3a1b2c4d5e6f0011223366",
                "contact": {
                  "email": "john.doe@acme.com",
                  "firstName": "John"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact added.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/campaigns": {
      "post": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyCreateEmailCampaign",
        "deprecated": true,
        "summary": "Create an email campaign",
        "description": "Creates an empty email (single-channel) campaign in DRAFT status with one empty first step. name must be unique in the workspace. Returns the full created campaign document. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "Q1 cold outreach"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campaign created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/LegacyEmailCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "campaign"
                  ]
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d8002dbcafa1",
                    "userId": "6650f1a2b839d800e9d7b619",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "provider": "6650f1a2b839d800c323fd2f",
                    "useManyProviders": true,
                    "providersUsed": [
                      "6650f1a2b839d800d6a7ea7d"
                    ],
                    "schedule": {
                      "bcc": "",
                      "dailyContact": 35,
                      "dailyLimit": 100,
                      "minInterval": 60,
                      "maxInterval": 180,
                      "blacklistUnsub": true,
                      "trackLinks": true,
                      "trackOpens": true,
                      "timeZone": "Europe/Paris",
                      "days": [
                        1
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "eventToStopMails": []
                    },
                    "steps": [
                      {
                        "_id": "6650f1a2b839d800244e963c",
                        "delay": {
                          "amount": 1,
                          "unit": "DAYS"
                        },
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800e7acf55b",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": [],
                            "options": {}
                          }
                        ]
                      }
                    ],
                    "recipients": {
                      "lists": [
                        {}
                      ],
                      "contacts": [
                        "6650f1a2b839d800e2b43cb7"
                      ],
                      "processing": true
                    },
                    "zapierData": [
                      {
                        "webhookUrl": "https://hooks.yourcompany.com/emelia",
                        "events": []
                      }
                    ],
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyListEmailCampaigns",
        "deprecated": true,
        "summary": "List email campaigns",
        "description": "Returns non-archived email campaigns with a reduced field set (_id, name, status, createdAt, startAt, useManyProviders, providersUsed, plannedStart) and the populated provider. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "responses": {
          "200": {
            "description": "Campaigns.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "campaigns": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LegacyEmailCampaignListItem"
                      }
                    }
                  },
                  "required": [
                    "success",
                    "campaigns"
                  ]
                },
                "example": {
                  "success": true,
                  "campaigns": [
                    {
                      "_id": "6650f1a2b839d80019331cde",
                      "name": "Q4 outreach: SaaS founders",
                      "status": "RUNNING",
                      "createdAt": "2026-10-06T09:30:00.000Z",
                      "startAt": "2026-10-06T09:30:00.000Z",
                      "plannedStart": "2026-10-06T09:30:00.000Z",
                      "useManyProviders": true,
                      "providersUsed": [
                        "6650f1a2b839d8006bc15fb2"
                      ],
                      "provider": {}
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/campaigns/{campaignId}": {
      "get": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyGetEmailCampaign",
        "deprecated": true,
        "summary": "Get email campaign details",
        "description": "Returns the full campaign document (minus __v) with recipients.lists populated to { _id, name }. Errors with 'Campaign not found' when absent. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mongo ObjectId of the campaign.",
            "example": "65f1a2b3c4d5e6f73478765e"
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/LegacyEmailCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "campaign"
                  ]
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d8000d605583",
                    "userId": "6650f1a2b839d8002a1aea4b",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "provider": "6650f1a2b839d800996c5c21",
                    "useManyProviders": true,
                    "providersUsed": [
                      "6650f1a2b839d800edcfb6df"
                    ],
                    "schedule": {
                      "bcc": "",
                      "dailyContact": 35,
                      "dailyLimit": 100,
                      "minInterval": 60,
                      "maxInterval": 180,
                      "blacklistUnsub": true,
                      "trackLinks": true,
                      "trackOpens": true,
                      "timeZone": "Europe/Paris",
                      "days": [
                        1
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "eventToStopMails": []
                    },
                    "steps": [
                      {
                        "_id": "6650f1a2b839d80009ac08d2",
                        "delay": {
                          "amount": 1,
                          "unit": "DAYS"
                        },
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800c9e2b951",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": [],
                            "options": {}
                          }
                        ]
                      }
                    ],
                    "recipients": {
                      "lists": [
                        {}
                      ],
                      "contacts": [
                        "6650f1a2b839d8006d5945c9"
                      ],
                      "processing": true
                    },
                    "zapierData": [
                      {
                        "webhookUrl": "https://hooks.yourcompany.com/emelia",
                        "events": []
                      }
                    ],
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/campaigns/{campaignId}/settings/global": {
      "patch": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyUpdateEmailCampaignSchedule",
        "deprecated": true,
        "summary": "Update email campaign schedule (global settings)",
        "description": "Replaces the campaign schedule (sending window, pace, tracking). The campaign must not be RUNNING or ARCHIVED. Extra validation beyond the schema: minInterval <= maxInterval, start <= end, dailyLimit >= dailyContact. Changing days/start/end/tracking rewrites each step version's per-day schedule. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mongo ObjectId of the campaign.",
            "example": "65f1a2b3c4d5e6f73478765e"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "schedule"
                ],
                "properties": {
                  "schedule": {
                    "type": "object",
                    "required": [
                      "dailyContact",
                      "dailyLimit",
                      "minInterval",
                      "maxInterval",
                      "blacklistUnsub",
                      "trackLinks",
                      "trackOpens",
                      "timeZone",
                      "days",
                      "start",
                      "end",
                      "eventToStopMails"
                    ],
                    "properties": {
                      "bcc": {
                        "type": "string",
                        "format": "email",
                        "description": "Optional BCC address for every email."
                      },
                      "dailyContact": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 500,
                        "description": "New contacts reached per day. Must be >= 2 to start a campaign."
                      },
                      "dailyLimit": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 1000,
                        "description": "Max emails sent per day (all steps). Must be >= dailyContact."
                      },
                      "minInterval": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 500,
                        "description": "Min delay between two sends, in SECONDS. Must be <= maxInterval."
                      },
                      "maxInterval": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 600,
                        "description": "Max delay between two sends, in SECONDS."
                      },
                      "blacklistUnsub": {
                        "type": "boolean"
                      },
                      "trackLinks": {
                        "type": "boolean"
                      },
                      "trackOpens": {
                        "type": "boolean"
                      },
                      "timeZone": {
                        "type": "string",
                        "description": "IANA timezone name (moment-timezone), e.g. 'Europe/Paris'."
                      },
                      "days": {
                        "type": "array",
                        "items": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 6
                        },
                        "uniqueItems": true,
                        "description": "Working days. 0 = Monday ... 6 = Sunday (back convention)."
                      },
                      "start": {
                        "type": "string",
                        "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                        "example": "09:00",
                        "description": "Daily window start HH:MM. Must be <= end."
                      },
                      "end": {
                        "type": "string",
                        "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                        "example": "18:00"
                      },
                      "eventToStopMails": {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "enum": [
                            "REPLIED",
                            "CLICKED",
                            "OPENED"
                          ]
                        },
                        "description": "Events that stop the sequence for a contact."
                      }
                    }
                  }
                }
              },
              "example": {
                "schedule": {
                  "dailyContact": 50,
                  "dailyLimit": 200,
                  "minInterval": 60,
                  "maxInterval": 300,
                  "blacklistUnsub": true,
                  "trackLinks": true,
                  "trackOpens": true,
                  "timeZone": "Europe/Paris",
                  "days": [
                    0,
                    1,
                    2,
                    3,
                    4
                  ],
                  "start": "09:00",
                  "end": "18:00",
                  "eventToStopMails": [
                    "REPLIED"
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated campaign.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/LegacyEmailCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "campaign"
                  ]
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d800244fe91d",
                    "userId": "6650f1a2b839d800da35bbc5",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "provider": "6650f1a2b839d80045d6927b",
                    "useManyProviders": true,
                    "providersUsed": [
                      "6650f1a2b839d8002184f429"
                    ],
                    "schedule": {
                      "bcc": "",
                      "dailyContact": 35,
                      "dailyLimit": 100,
                      "minInterval": 60,
                      "maxInterval": 180,
                      "blacklistUnsub": true,
                      "trackLinks": true,
                      "trackOpens": true,
                      "timeZone": "Europe/Paris",
                      "days": [
                        1
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "eventToStopMails": []
                    },
                    "steps": [
                      {
                        "_id": "6650f1a2b839d8001e904670",
                        "delay": {
                          "amount": 1,
                          "unit": "DAYS"
                        },
                        "versions": [
                          {
                            "_id": "6650f1a2b839d8008207665f",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": [],
                            "options": {}
                          }
                        ]
                      }
                    ],
                    "recipients": {
                      "lists": [
                        {}
                      ],
                      "contacts": [
                        "6650f1a2b839d8008ddef4f3"
                      ],
                      "processing": true
                    },
                    "zapierData": [
                      {
                        "webhookUrl": "https://hooks.yourcompany.com/emelia",
                        "events": []
                      }
                    ],
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/campaigns/{campaignId}/settings/providers": {
      "patch": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyUpdateEmailCampaignProviders",
        "deprecated": true,
        "summary": "Update email campaign sending providers",
        "description": "Sets the sending account(s). Either provide provider.id for a single provider, or useManyProviders=true with providersUsed=[...] for rotation. At least one of provider or useManyProviders is required. The campaign must not be RUNNING or ARCHIVED. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mongo ObjectId of the campaign.",
            "example": "65f1a2b3c4d5e6f73478765e"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "provider": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      }
                    },
                    "description": "Single provider (when useManyProviders is false)."
                  },
                  "useManyProviders": {
                    "type": "boolean"
                  },
                  "providersUsed": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Provider ObjectIds used when useManyProviders is true."
                  }
                }
              },
              "example": {
                "useManyProviders": true,
                "providersUsed": [
                  "5f3a1b2c4d5e6f0011223344",
                  "5f3a1b2c4d5e6f0011223345"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated campaign.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/LegacyEmailCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "campaign"
                  ]
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d800690d060e",
                    "userId": "6650f1a2b839d800ac974802",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "provider": "6650f1a2b839d80037a61d04",
                    "useManyProviders": true,
                    "providersUsed": [
                      "6650f1a2b839d8005867cca2"
                    ],
                    "schedule": {
                      "bcc": "",
                      "dailyContact": 35,
                      "dailyLimit": 100,
                      "minInterval": 60,
                      "maxInterval": 180,
                      "blacklistUnsub": true,
                      "trackLinks": true,
                      "trackOpens": true,
                      "timeZone": "Europe/Paris",
                      "days": [
                        1
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "eventToStopMails": []
                    },
                    "steps": [
                      {
                        "_id": "6650f1a2b839d800be3d96c3",
                        "delay": {
                          "amount": 1,
                          "unit": "DAYS"
                        },
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800332199f6",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": [],
                            "options": {}
                          }
                        ]
                      }
                    ],
                    "recipients": {
                      "lists": [
                        {}
                      ],
                      "contacts": [
                        "6650f1a2b839d80003b0c26e"
                      ],
                      "processing": true
                    },
                    "zapierData": [
                      {
                        "webhookUrl": "https://hooks.yourcompany.com/emelia",
                        "events": []
                      }
                    ],
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/campaigns/{campaignId}/name": {
      "patch": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyUpdateEmailCampaignName",
        "deprecated": true,
        "summary": "Rename an email campaign",
        "description": "Renames the campaign. The name must be unique in the workspace. The campaign must not be RUNNING or ARCHIVED. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mongo ObjectId of the campaign.",
            "example": "65f1a2b3c4d5e6f73478765e"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "Q1 cold outreach (v2)"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated campaign.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/LegacyEmailCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "campaign"
                  ]
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d800529baa8b",
                    "userId": "6650f1a2b839d800609af433",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "provider": "6650f1a2b839d800d1502d49",
                    "useManyProviders": true,
                    "providersUsed": [
                      "6650f1a2b839d80034816367"
                    ],
                    "schedule": {
                      "bcc": "",
                      "dailyContact": 35,
                      "dailyLimit": 100,
                      "minInterval": 60,
                      "maxInterval": 180,
                      "blacklistUnsub": true,
                      "trackLinks": true,
                      "trackOpens": true,
                      "timeZone": "Europe/Paris",
                      "days": [
                        1
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "eventToStopMails": []
                    },
                    "steps": [
                      {
                        "_id": "6650f1a2b839d8005885e18a",
                        "delay": {
                          "amount": 1,
                          "unit": "DAYS"
                        },
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800746ebf19",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": [],
                            "options": {}
                          }
                        ]
                      }
                    ],
                    "recipients": {
                      "lists": [
                        {}
                      ],
                      "contacts": [
                        "6650f1a2b839d800990ca821"
                      ],
                      "processing": true
                    },
                    "zapierData": [
                      {
                        "webhookUrl": "https://hooks.yourcompany.com/emelia",
                        "events": []
                      }
                    ],
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/campaigns/{campaignId}/steps": {
      "patch": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyUpdateEmailCampaignSteps",
        "deprecated": true,
        "summary": "Replace email campaign steps",
        "description": "Replaces the full ordered list of steps (the sequence). The campaign must not be RUNNING or ARCHIVED. Steps after the first must have delay.amount > 0, and a step may have at most one disabled version. Changing the step count resets the cached stats. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mongo ObjectId of the campaign.",
            "example": "65f1a2b3c4d5e6f73478765e"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "steps"
                ],
                "properties": {
                  "steps": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "delay",
                        "versions"
                      ],
                      "properties": {
                        "delay": {
                          "type": "object",
                          "required": [
                            "unit"
                          ],
                          "properties": {
                            "amount": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 10000,
                              "description": "Must be > 0 for steps after the first."
                            },
                            "unit": {
                              "type": "string",
                              "enum": [
                                "MINUTES",
                                "HOURS",
                                "DAYS"
                              ]
                            }
                          }
                        },
                        "versions": {
                          "type": "array",
                          "description": "A/B versions. At most one may be disabled.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "subject": {
                                "type": "string"
                              },
                              "disabled": {
                                "type": "boolean"
                              },
                              "message": {
                                "type": "string",
                                "description": "HTML body. Variables use {{firstName}}, {{custom_field}}, {{UNSUBSCRIBE_LINK}}, {{SENDER}}, {{UNIQUE_ID}}."
                              },
                              "rawHtml": {
                                "type": "boolean"
                              },
                              "attachments": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "required": [
                                    "name",
                                    "url"
                                  ],
                                  "properties": {
                                    "name": {
                                      "type": "string"
                                    },
                                    "url": {
                                      "type": "string",
                                      "format": "uri"
                                    }
                                  }
                                }
                              },
                              "options": {
                                "type": "object",
                                "description": "Per-version overrides (per-day schedule, tracking, provider). Usually omitted; derived from the campaign schedule."
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "steps": [
                  {
                    "delay": {
                      "amount": 0,
                      "unit": "DAYS"
                    },
                    "versions": [
                      {
                        "subject": "Quick question, {{firstName}}",
                        "message": "<p>Hi {{firstName}},</p>"
                      }
                    ]
                  },
                  {
                    "delay": {
                      "amount": 2,
                      "unit": "DAYS"
                    },
                    "versions": [
                      {
                        "subject": "",
                        "message": "<p>Following up.</p>"
                      }
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated campaign.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/LegacyEmailCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "campaign"
                  ]
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d8000b13890b",
                    "userId": "6650f1a2b839d800555fdeb3",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "provider": "6650f1a2b839d800d6193fc9",
                    "useManyProviders": true,
                    "providersUsed": [
                      "6650f1a2b839d800c15749e7"
                    ],
                    "schedule": {
                      "bcc": "",
                      "dailyContact": 35,
                      "dailyLimit": 100,
                      "minInterval": 60,
                      "maxInterval": 180,
                      "blacklistUnsub": true,
                      "trackLinks": true,
                      "trackOpens": true,
                      "timeZone": "Europe/Paris",
                      "days": [
                        1
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "eventToStopMails": []
                    },
                    "steps": [
                      {
                        "_id": "6650f1a2b839d8004d4acc0a",
                        "delay": {
                          "amount": 1,
                          "unit": "DAYS"
                        },
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800ef395699",
                            "subject": "Quick question about Nexora",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "rawHtml": true,
                            "attachments": [],
                            "options": {}
                          }
                        ]
                      }
                    ],
                    "recipients": {
                      "lists": [
                        {}
                      ],
                      "contacts": [
                        "6650f1a2b839d800cdc683a1"
                      ],
                      "processing": true
                    },
                    "zapierData": [
                      {
                        "webhookUrl": "https://hooks.yourcompany.com/emelia",
                        "events": []
                      }
                    ],
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/campaigns/{campaignId}/start": {
      "post": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyStartEmailCampaign",
        "deprecated": true,
        "summary": "Start an email campaign",
        "description": "Starts the campaign (status RUNNING). Requires an active subscription and passes start checks: at least one provider connected, a schedule, at least one step, at least one recipient, dailyContact >= 2, and at most 30000 recipients (higher for beta accounts). Validation failures return a stable error code (e.g. EMAIL_PROVIDER_MISSING, NO_RECIPIENTS, TOO_MANY_RECIPIENTS, DAILY_CONTACT_TOO_LOW). New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mongo ObjectId of the campaign.",
            "example": "65f1a2b3c4d5e6f73478765e"
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign started.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Start refused (missing provider/steps/recipients, quota, etc.). Body carries a stable code and params.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/campaigns/{campaignId}/pause": {
      "post": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyPauseEmailCampaign",
        "deprecated": true,
        "summary": "Pause an email campaign",
        "description": "Pauses a RUNNING campaign (status PAUSED) and clears its scheduled jobs. Errors if the campaign is not RUNNING. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mongo ObjectId of the campaign.",
            "example": "65f1a2b3c4d5e6f73478765e"
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign paused.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/contacts": {
      "patch": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacySetEmailContactCustomField",
        "deprecated": true,
        "summary": "Set a custom field on an email contact",
        "description": "Sets or updates one field on the contact identified by email inside the campaign (campaignId). Known keys (firstName, lastName, phoneNumber...) update the field; any other name is stored under custom. Spaces in fieldName are removed. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "campaignId",
                  "email",
                  "fieldName",
                  "fieldValue"
                ],
                "properties": {
                  "campaignId": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "fieldName": {
                    "type": "string"
                  },
                  "fieldValue": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "campaignId": "5f3a1b2c4d5e6f0011223344",
                "email": "john.doe@acme.com",
                "fieldName": "company",
                "fieldValue": "Acme Inc"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Field set.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/blacklists/contact": {
      "post": {
        "tags": [
          "Blacklist"
        ],
        "operationId": "legacyAddToBlacklist",
        "summary": "Add an email or domain to the account blacklist",
        "description": "Adds an address, or a bare domain (domain.ltd), to the workspace blacklist. The blacklist is account-wide and applies to every current and future campaign.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "description": "Email address or bare domain (e.g. acme.com)."
                  }
                }
              },
              "example": {
                "email": "john.doe@acme.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Added to blacklist.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Blacklist"
        ],
        "operationId": "legacyRemoveFromBlacklist",
        "summary": "Remove an email or domain from the account blacklist",
        "description": "Removes an address (or bare domain) from the workspace blacklist.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "email": "john.doe@acme.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Removed from blacklist.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/blacklists": {
      "get": {
        "tags": [
          "Blacklist"
        ],
        "operationId": "legacyGetBlacklists",
        "summary": "Get the account blacklists",
        "description": "Returns the workspace blacklist documents. WARNING: the response is a RAW ARRAY (not wrapped in { success }). Each item carries blacklistContent (email/domain blacklist, newline-separated) and linkedinBlacklistContent (LinkedIn handles, newline-separated).",
        "responses": {
          "200": {
            "description": "Array of blacklist documents.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LegacyBlacklist"
                  }
                },
                "example": [
                  {
                    "_id": "5f3a1b2c4d5e6f00112233cc",
                    "name": "blacklist",
                    "blacklistContent": "john.doe@acme.com\nacme.com",
                    "linkedinBlacklistContent": "john-doe"
                  }
                ]
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/blacklists/linkedin": {
      "post": {
        "tags": [
          "Blacklist"
        ],
        "operationId": "legacyAddLinkedinToBlacklist",
        "summary": "Add a LinkedIn profile to the blacklist",
        "description": "Adds a LinkedIn profile to the workspace blacklist. value is a full profile URL (https://www.linkedin.com/in/...) or a bare handle; it is normalised to the handle. Despite living under /emails, this manages the LinkedIn blacklist.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "value"
                ],
                "properties": {
                  "value": {
                    "type": "string",
                    "description": "LinkedIn profile URL or bare handle."
                  }
                }
              },
              "example": {
                "value": "https://www.linkedin.com/in/john-doe/"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Added.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Blacklist"
        ],
        "operationId": "legacyRemoveLinkedinFromBlacklist",
        "summary": "Remove a LinkedIn profile from the blacklist",
        "description": "Removes a LinkedIn profile (URL or handle) from the workspace blacklist.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "value"
                ],
                "properties": {
                  "value": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "value": "john-doe"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Removed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/campaigns/{campaignId}/activities": {
      "get": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyGetEmailCampaignActivities",
        "deprecated": true,
        "summary": "Get email campaign activities",
        "description": "Returns a page of activity events for the campaign (30 per page, page is 0-based). RE_REPLY and (internally) OPENED are excluded. Filter with type (single event) or types (comma-separated list), by contactId or versionId, and search (tokens on firstName/lastName/email) or query (exact email). New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mongo ObjectId of the campaign.",
            "example": "65f1a2b3c4d5e6f73478765e"
          },
          {
            "name": "contactId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "versionId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "SENT",
                "FIRST_OPEN",
                "OPENED",
                "CLICKED",
                "REPLIED",
                "BOUNCED",
                "UNSUBSCRIBED"
              ]
            }
          },
          {
            "name": "types",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[A-Z_]+(,[A-Z_]+)*$"
            },
            "description": "Comma-separated list of events."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "description": "0-based page index. 30 items per page."
          },
          {
            "name": "query",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Exact email match."
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Token search on firstName/lastName/email."
          }
        ],
        "responses": {
          "200": {
            "description": "Activities (note: envelope is { activities }, no success flag).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "activities": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LegacyActivity"
                      }
                    }
                  },
                  "required": [
                    "activities"
                  ]
                },
                "example": {
                  "activities": [
                    {
                      "_id": "6650f1a2b839d800bac4cdd2",
                      "contact": "6650f1a2b839d8003c2df81e",
                      "event": "SENT",
                      "date": "2026-10-06T09:30:00.000Z",
                      "step": 0,
                      "version": 0,
                      "customData": {}
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/emails/test": {
      "post": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacySendTestEmail",
        "deprecated": true,
        "summary": "Send a test email",
        "description": "Sends a test of one step/version to an address. Target the step/version either by 0-based indices (step, version) or by stepId/versionId. At least one of step, stepId or versionId is required; version defaults to 0. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "campaignId",
                  "email"
                ],
                "properties": {
                  "campaignId": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "step": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "0-based step index."
                  },
                  "version": {
                    "type": "integer",
                    "minimum": 0,
                    "default": 0,
                    "description": "0-based A/B version index."
                  },
                  "stepId": {
                    "type": "string"
                  },
                  "versionId": {
                    "type": "string"
                  }
                },
                "anyOf": [
                  {
                    "required": [
                      "step"
                    ]
                  },
                  {
                    "required": [
                      "stepId"
                    ]
                  },
                  {
                    "required": [
                      "versionId"
                    ]
                  }
                ]
              },
              "example": {
                "campaignId": "5f3a1b2c4d5e6f0011223344",
                "email": "qa@acme.com",
                "step": 0,
                "version": 0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Test sent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stats": {
      "get": {
        "tags": [
          "Email campaigns (legacy)"
        ],
        "operationId": "legacyGetEmailStats",
        "deprecated": true,
        "summary": "Get email campaign statistics",
        "description": "Returns aggregated statistics for one email campaign. By default (detailed omitted or not 'true') a flat summary is returned; with detailed=true a { global, steps } breakdown is returned. Statistics are computed on demand and cached. PITFALLS: when the campaign is not found the route answers 401 (not 404) with { error: 'Campaign not found' }; ANY internal error also answers 401 { error: 'You must be logged in' }. detailed is read as the string 'true'; any other value (including the boolean true serialised elsewhere) is treated as the summary. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "65f1a2b3c4d5e6f73478765e"
          },
          {
            "name": "detailed",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Pass true for the per-step breakdown."
          },
          {
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "ISO start date filter."
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "providerId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Restrict stats to one provider."
          }
        ],
        "responses": {
          "200": {
            "description": "Summary (default) or detailed stats.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/LegacyEmailStatsSummary"
                    },
                    {
                      "$ref": "#/components/schemas/LegacyStatsDetailed"
                    }
                  ]
                },
                "example": {
                  "mailsSent": 1200,
                  "uniqueOpensPercent": 48.5,
                  "opens": 1700,
                  "linkClickedPercent": 12.3,
                  "repliedPercent": 6.1,
                  "bouncedPercent": 1.2,
                  "unsubscribePercent": 0.4,
                  "progressPercent": 73
                }
              }
            }
          },
          "401": {
            "description": "Campaign not found, or any internal/auth error (this route returns 401 for both).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Campaign not found"
                }
              }
            }
          },
          "503": {
            "description": "Statistics are still being computed (only for accounts under single-flight), retry after the Retry-After delay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Statistics are being computed, please try again in a few moments",
                  "code": "STATS_COMPUTING"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin/campaign/contacts": {
      "post": {
        "tags": [
          "LinkedIn campaigns (legacy)"
        ],
        "operationId": "legacyAddContactToLinkedinCampaign",
        "deprecated": true,
        "summary": "Add a contact to a LinkedIn campaign",
        "description": "Adds one profile to the LinkedIn campaign (id). The profile is appended to a dedicated list '<campaign name> contacts from API'. Side effects: a FINISHED campaign is restarted; a RUNNING campaign schedules the new contact. Fails if the profile is already in that list. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "contact"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "LinkedIn campaign ObjectId."
                  },
                  "contact": {
                    "type": "object",
                    "required": [
                      "linkedinUrlProfile"
                    ],
                    "properties": {
                      "linkedinUrlProfile": {
                        "type": "string",
                        "description": "Profile URL (linkedin.com/in/..., or a Sales Navigator lead/people URL)."
                      },
                      "firstName": {
                        "type": "string"
                      },
                      "lastName": {
                        "type": "string"
                      }
                    },
                    "additionalProperties": {
                      "type": "string",
                      "description": "Any other key is stored as a custom variable."
                    }
                  }
                }
              },
              "example": {
                "id": "5f3a1b2c4d5e6f0011223390",
                "contact": {
                  "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe/",
                  "firstName": "John",
                  "lastName": "Doe"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contact added.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "LinkedIn campaigns (legacy)"
        ],
        "operationId": "legacyListLinkedinCampaignContacts",
        "deprecated": true,
        "summary": "List LinkedIn campaign contacts (paginated)",
        "description": "Returns a page of the campaign recipients. Pagination is 0-based; perPage defaults to 20 (min 10, max 10000). The response envelope is { success, list, count } (NOT { contacts, total }). event filters by status and accepts either an array or a single string (unlike the email variant which requires an array). New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "65f1a2b3c4d5e6f73478765e"
          },
          {
            "name": "page",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "0-based page index.",
            "example": 0
          },
          {
            "name": "perPage",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Default 20, max 10000."
          },
          {
            "name": "event",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "VISITED",
                  "INVITED",
                  "ACCEPTED",
                  "MESSAGE_SENT",
                  "INMAIL_SENT",
                  "REPLIED",
                  "NO_ACTIVITY"
                ]
              }
            },
            "description": "Status filter; a single string is also accepted."
          },
          {
            "name": "query",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Token search on handle/firstName/lastName."
          }
        ],
        "responses": {
          "200": {
            "description": "One page of contacts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "list": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LegacyLKContact"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "success",
                    "list",
                    "count"
                  ]
                },
                "example": {
                  "success": true,
                  "list": [
                    {
                      "_id": "6650f1a2b839d800be1b6ee1",
                      "url": "https://www.linkedin.com/in/claire-fontaine",
                      "handle": "claire-fontaine",
                      "firstName": "Claire",
                      "lastName": "Fontaine",
                      "custom": {},
                      "status": "INVITED",
                      "lastContacted": "2026-10-06T09:30:00.000Z",
                      "lastReplied": "2026-10-06T09:30:00.000Z"
                    }
                  ],
                  "count": 250
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "LinkedIn campaigns (legacy)"
        ],
        "operationId": "legacyDeleteContactFromLinkedinCampaign",
        "deprecated": true,
        "summary": "Remove a contact from a LinkedIn campaign",
        "description": "Removes the contact whose profile URL matches from the campaign (id). Use id='ALL_CAMPAIGNS' to remove from every non-archived LinkedIn campaign. Body field is 'url'. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "url"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Campaign ObjectId or 'ALL_CAMPAIGNS'."
                  },
                  "url": {
                    "type": "string",
                    "description": "LinkedIn profile URL."
                  }
                }
              },
              "example": {
                "id": "5f3a1b2c4d5e6f0011223390",
                "url": "https://www.linkedin.com/in/john-doe/"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Removed (always success:true).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "example": {
                  "success": true
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin/campaigns": {
      "post": {
        "tags": [
          "LinkedIn campaigns (legacy)"
        ],
        "operationId": "legacyCreateLinkedinCampaign",
        "deprecated": true,
        "summary": "Create a LinkedIn campaign",
        "description": "Creates an empty LinkedIn campaign in DRAFT with one VISIT step. name must be unique. Returns the full created campaign. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "LinkedIn Q1 outreach"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campaign created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/LegacyLinkedinCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "campaign"
                  ]
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d80006f33988",
                    "userId": "6650f1a2b839d80009687764",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "account": "6650f1a2b839d80057878a84",
                    "schedule": {
                      "dailyContact": 35,
                      "timeZone": "Europe/Paris",
                      "days": [
                        1
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "excludeAlreadyMessaged": true,
                      "exclude1stConn": true,
                      "excludeNoPictures": true
                    },
                    "steps": [
                      {
                        "_id": "6650f1a2b839d8007455fc19",
                        "stepType": "VISIT",
                        "delay": {
                          "amount": 1,
                          "unit": "DAYS"
                        },
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800ccca7024",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "attachments": []
                          }
                        ]
                      }
                    ],
                    "recipients": {
                      "lists": [
                        {}
                      ],
                      "contacts": [
                        "6650f1a2b839d80009bc84a0"
                      ],
                      "processing": true
                    },
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "LinkedIn campaigns (legacy)"
        ],
        "operationId": "legacyListLinkedinCampaigns",
        "deprecated": true,
        "summary": "List LinkedIn campaigns",
        "description": "Returns non-archived LinkedIn campaigns with a reduced field set (_id, name, status, createdAt, startAt, plannedStart, account) and the account populated to { _id, metadata }. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "responses": {
          "200": {
            "description": "Campaigns.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "campaigns": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LegacyLinkedinCampaignListItem"
                      }
                    }
                  },
                  "required": [
                    "success",
                    "campaigns"
                  ]
                },
                "example": {
                  "success": true,
                  "campaigns": [
                    {
                      "_id": "6650f1a2b839d800cc3c9381",
                      "name": "Q4 outreach: SaaS founders",
                      "status": "RUNNING",
                      "createdAt": "2026-10-06T09:30:00.000Z",
                      "startAt": "2026-10-06T09:30:00.000Z",
                      "plannedStart": "2026-10-06T09:30:00.000Z",
                      "account": {}
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin/campaigns/{campaignId}/settings/global": {
      "patch": {
        "tags": [
          "LinkedIn campaigns (legacy)"
        ],
        "operationId": "legacyUpdateLinkedinCampaignSchedule",
        "deprecated": true,
        "summary": "Update LinkedIn campaign schedule (global settings)",
        "description": "Updates the LinkedIn schedule. The campaign must not be RUNNING or ARCHIVED. Fields are written one by one: omitting a field leaves it unchanged (so the V3 front can skip exclude1stConn / excludeNoPictures). start <= end and 1 <= dailyContact <= 80. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mongo ObjectId of the campaign.",
            "example": "65f1a2b3c4d5e6f73478765e"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "schedule"
                ],
                "properties": {
                  "schedule": {
                    "type": "object",
                    "required": [
                      "dailyContact",
                      "timeZone",
                      "days",
                      "start",
                      "end"
                    ],
                    "properties": {
                      "dailyContact": {
                        "type": "integer",
                        "description": "Prospects reached per day. Enforced 1..80 here; the start check caps it at 40 when a CONNECTION step exists, else 80."
                      },
                      "timeZone": {
                        "type": "string",
                        "description": "IANA timezone name."
                      },
                      "days": {
                        "type": "array",
                        "items": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 6
                        },
                        "uniqueItems": true,
                        "description": "0 = Monday ... 6 = Sunday."
                      },
                      "start": {
                        "type": "string",
                        "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                        "example": "09:00"
                      },
                      "end": {
                        "type": "string",
                        "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
                        "example": "18:00"
                      },
                      "excludeAlreadyMessaged": {
                        "type": "boolean"
                      },
                      "exclude1stConn": {
                        "type": "boolean"
                      },
                      "excludeNoPictures": {
                        "type": "boolean"
                      }
                    }
                  }
                }
              },
              "example": {
                "schedule": {
                  "dailyContact": 25,
                  "timeZone": "Europe/Paris",
                  "days": [
                    0,
                    1,
                    2,
                    3,
                    4
                  ],
                  "start": "09:00",
                  "end": "18:00",
                  "excludeAlreadyMessaged": true
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated campaign.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/LegacyLinkedinCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "campaign"
                  ]
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d800b55a762a",
                    "userId": "6650f1a2b839d800cd3b1c2e",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "account": "6650f1a2b839d800e4ec0ae6",
                    "schedule": {
                      "dailyContact": 35,
                      "timeZone": "Europe/Paris",
                      "days": [
                        1
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "excludeAlreadyMessaged": true,
                      "exclude1stConn": true,
                      "excludeNoPictures": true
                    },
                    "steps": [
                      {
                        "_id": "6650f1a2b839d800a5cf17c7",
                        "stepType": "VISIT",
                        "delay": {
                          "amount": 1,
                          "unit": "DAYS"
                        },
                        "versions": [
                          {
                            "_id": "6650f1a2b839d80055fb9fba",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "attachments": []
                          }
                        ]
                      }
                    ],
                    "recipients": {
                      "lists": [
                        {}
                      ],
                      "contacts": [
                        "6650f1a2b839d800b5bce4fa"
                      ],
                      "processing": true
                    },
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin/campaigns/{campaignId}/name": {
      "patch": {
        "tags": [
          "LinkedIn campaigns (legacy)"
        ],
        "operationId": "legacyUpdateLinkedinCampaignName",
        "deprecated": true,
        "summary": "Rename a LinkedIn campaign",
        "description": "Renames the campaign (name must be unique). The campaign must not be RUNNING or ARCHIVED. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mongo ObjectId of the campaign.",
            "example": "65f1a2b3c4d5e6f73478765e"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "LinkedIn Q1 (v2)"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated campaign.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/LegacyLinkedinCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "campaign"
                  ]
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d800945fe8f2",
                    "userId": "6650f1a2b839d8008cb52536",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "account": "6650f1a2b839d8002aad97ee",
                    "schedule": {
                      "dailyContact": 35,
                      "timeZone": "Europe/Paris",
                      "days": [
                        1
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "excludeAlreadyMessaged": true,
                      "exclude1stConn": true,
                      "excludeNoPictures": true
                    },
                    "steps": [
                      {
                        "_id": "6650f1a2b839d80094bcf85f",
                        "stepType": "VISIT",
                        "delay": {
                          "amount": 1,
                          "unit": "DAYS"
                        },
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800e3549022",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "attachments": []
                          }
                        ]
                      }
                    ],
                    "recipients": {
                      "lists": [
                        {}
                      ],
                      "contacts": [
                        "6650f1a2b839d80070a03472"
                      ],
                      "processing": true
                    },
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin/campaigns/{campaignId}/steps": {
      "patch": {
        "tags": [
          "LinkedIn campaigns (legacy)"
        ],
        "operationId": "legacyUpdateLinkedinCampaignSteps",
        "deprecated": true,
        "summary": "Replace LinkedIn campaign steps",
        "description": "Replaces the full ordered list of LinkedIn steps. The campaign must not be RUNNING or ARCHIVED. Steps after the first need delay.amount > 0; at most one disabled version per step. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mongo ObjectId of the campaign.",
            "example": "65f1a2b3c4d5e6f73478765e"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "steps"
                ],
                "properties": {
                  "steps": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "stepType",
                        "delay",
                        "versions"
                      ],
                      "properties": {
                        "stepType": {
                          "type": "string",
                          "enum": [
                            "VISIT",
                            "CONNECTION",
                            "MESSAGE",
                            "LIKE",
                            "FOLLOW",
                            "INMAIL"
                          ],
                          "description": "`FOLLOW` is accepted when saved, but nothing runs it: do not use it."
                        },
                        "delay": {
                          "type": "object",
                          "required": [
                            "unit"
                          ],
                          "properties": {
                            "amount": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 10000,
                              "description": "Must be > 0 for steps after the first."
                            },
                            "unit": {
                              "type": "string",
                              "enum": [
                                "MINUTES",
                                "HOURS",
                                "DAYS"
                              ]
                            }
                          }
                        },
                        "versions": {
                          "type": "array",
                          "description": "At most one disabled version per step. VISIT/LIKE/FOLLOW steps can carry an empty versions array.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "disabled": {
                                "type": "boolean"
                              },
                              "message": {
                                "type": "string"
                              },
                              "attachments": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "name": {
                                      "type": "string"
                                    },
                                    "url": {
                                      "type": "string",
                                      "format": "uri"
                                    },
                                    "mediaType": {
                                      "type": "string",
                                      "enum": [
                                        "MEDIA",
                                        "ATTACHMENT",
                                        "AUDIO"
                                      ]
                                    },
                                    "duration": {
                                      "type": "number"
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "steps": [
                  {
                    "stepType": "VISIT",
                    "delay": {
                      "amount": 0,
                      "unit": "DAYS"
                    },
                    "versions": []
                  },
                  {
                    "stepType": "CONNECTION",
                    "delay": {
                      "amount": 1,
                      "unit": "DAYS"
                    },
                    "versions": [
                      {
                        "message": "Hi {{firstName}}, happy to connect."
                      }
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated campaign.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "campaign": {
                      "$ref": "#/components/schemas/LegacyLinkedinCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "campaign"
                  ]
                },
                "example": {
                  "success": true,
                  "campaign": {
                    "_id": "6650f1a2b839d800941eec2c",
                    "userId": "6650f1a2b839d800d0a32198",
                    "name": "Q4 outreach: SaaS founders",
                    "status": "DRAFT",
                    "account": "6650f1a2b839d800463fada8",
                    "schedule": {
                      "dailyContact": 35,
                      "timeZone": "Europe/Paris",
                      "days": [
                        1
                      ],
                      "start": "08:00",
                      "end": "17:00",
                      "excludeAlreadyMessaged": true,
                      "exclude1stConn": true,
                      "excludeNoPictures": true
                    },
                    "steps": [
                      {
                        "_id": "6650f1a2b839d80036a70635",
                        "stepType": "VISIT",
                        "delay": {
                          "amount": 1,
                          "unit": "DAYS"
                        },
                        "versions": [
                          {
                            "_id": "6650f1a2b839d800f3a30d30",
                            "message": "Hi Claire, thanks for your reply. Are you free for a call on Thursday?",
                            "disabled": false,
                            "attachments": []
                          }
                        ]
                      }
                    ],
                    "recipients": {
                      "lists": [
                        {}
                      ],
                      "contacts": [
                        "6650f1a2b839d800fd48d7a4"
                      ],
                      "processing": true
                    },
                    "createdAt": "2026-10-06T09:30:00.000Z",
                    "startAt": "2026-10-06T09:30:00.000Z",
                    "estimatedEnd": "2026-10-06T09:30:00.000Z",
                    "plannedStart": "2026-10-06T09:30:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/linkedin/campaigns/{campaignId}/activities": {
      "get": {
        "tags": [
          "LinkedIn campaigns (legacy)"
        ],
        "operationId": "legacyGetLinkedinCampaignActivities",
        "deprecated": true,
        "summary": "Get LinkedIn campaign activities",
        "description": "Returns a page of LinkedIn activity events (30 per page, 0-based). RE_REPLY and FAILED_ACTION are excluded; type=MESSAGE_SENT also returns INMAIL_SENT. Filter by contactId, versionId, type or types (comma list), and search (tokens on firstName/lastName/url). New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Mongo ObjectId of the campaign.",
            "example": "65f1a2b3c4d5e6f73478765e"
          },
          {
            "name": "contactId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "versionId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "VISITED",
                "INVITED",
                "ACCEPTED",
                "MESSAGE_SENT",
                "INMAIL_SENT",
                "REPLIED",
                "FOLLOWED",
                "LIKED"
              ]
            }
          },
          {
            "name": "types",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[A-Z_]+(,[A-Z_]+)*$"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "description": "0-based, 30 per page."
          },
          {
            "name": "query",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Activities (envelope is { activities }, no success flag).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "activities": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LegacyLKActivity"
                      }
                    }
                  },
                  "required": [
                    "activities"
                  ]
                },
                "example": {
                  "activities": [
                    {
                      "_id": "6650f1a2b839d8003f9f699f",
                      "contact": "6650f1a2b839d8005b0244b3",
                      "event": "VISITED",
                      "date": "2026-10-06T09:30:00.000Z",
                      "step": 0,
                      "version": 0,
                      "customData": {}
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Business or validation error. 400 by default; Joi validation errors carry code INVALID_REQUEST.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stats/linkedin": {
      "get": {
        "tags": [
          "LinkedIn campaigns (legacy)"
        ],
        "operationId": "legacyGetLinkedinStats",
        "deprecated": true,
        "summary": "Get LinkedIn campaign statistics",
        "description": "Returns aggregated statistics for one LinkedIn campaign. Summary by default; detailed=true gives { global, steps }. Computed on demand and cached. PITFALLS: a missing campaign answers 401 { error: 'Campaign not found' }, and any internal error answers 401 { error: 'You must be logged in' }. New integrations should use the multichannel /advanced campaign endpoints instead.",
        "parameters": [
          {
            "name": "campaignId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "65f1a2b3c4d5e6f73478765e"
          },
          {
            "name": "detailed",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "start",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "accountId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Restrict stats to one LinkedIn account."
          }
        ],
        "responses": {
          "200": {
            "description": "Summary (default) or detailed stats.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/LegacyLinkedinStatsSummary"
                    },
                    {
                      "$ref": "#/components/schemas/LegacyStatsDetailed"
                    }
                  ]
                },
                "example": {
                  "visited": 300,
                  "invited": 250,
                  "accepted": 120,
                  "message_sent": 120,
                  "replied": 35,
                  "progress_percent": 60,
                  "invite_percent": 48
                }
              }
            }
          },
          "401": {
            "description": "Campaign not found, or any internal/auth error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Campaign not found"
                }
              }
            }
          }
        }
      }
    },
    "/webhook": {
      "get": {
        "operationId": "listWebhooks",
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhooks",
        "description": "Returns the activity webhooks registered on the campaigns of the authenticated account: legacy email campaigns (`typeOfCampaign: email`), legacy LinkedIn campaigns (`linkedin`) and advanced campaigns (`advanced`), whatever their status (FINISHED and ARCHIVED included).\n\nThere is one entry per registration and per campaign: a URL registered with `ALL_CAMPAIGNS` appears once for each campaign it was copied to, and the part kept for future campaigns is not listed. A URL registered twice on a campaign appears twice. Registrations with an empty event list are skipped. `status` is always `active`.\n\n**Authentication.** API key in `Authorization` (raw or `Bearer <key>`), per key rate limit applies.\n\n**Errors.** 401 `Authentication required` for a missing or unknown key. 401 `You must be logged in` is also returned when reading the webhooks fails for any internal reason. Exceeding the per key rate limit returns 500 `Internal server error`, not 429.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Webhooks of the account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookListResponse"
                },
                "examples": {
                  "list": {
                    "value": {
                      "webhooks": [
                        {
                          "id": "6702c1d4e5f6a7b8c9d0e1f2",
                          "typeOfCampaign": "advanced",
                          "campaignId": "6651f0c2a1b2c3d4e5f60718",
                          "campaignName": "Acme Q4 multichannel",
                          "url": "https://hooks.acme.com/emelia/9f2c4e1a7b",
                          "events": [
                            "MAIL_REPLIED",
                            "LINKEDIN_REPLIED"
                          ],
                          "status": "active"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or unknown key, or internal error while reading webhooks.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unauthenticated": {
                    "value": {
                      "error": "Authentication required"
                    }
                  },
                  "internalErrorMaskedAs401": {
                    "value": {
                      "error": "You must be logged in"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Session token rate limit reached (session tokens only; API keys never get 429, see 500).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "rateLimited": {
                    "value": {
                      "error": "Too many requests"
                    }
                  }
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal error. Also returned when the per API key rate limit (30, 100, 300 or 1000 requests per minute depending on the plan) is exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "internal": {
                    "value": {
                      "error": "Internal server error"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Your 48 character API key, sent as is (a `Bearer ` prefix is tolerated). Create it at https://v3.emelia.io/settings/api."
      }
    },
    "schemas": {
      "ObjectId": {
        "type": "string",
        "pattern": "^[0-9a-fA-F]{24}$",
        "description": "MongoDB ObjectId: 24 hexadecimal characters.",
        "examples": [
          "6650f1a2b839d80011c5a0f1"
        ]
      },
      "ErrorCode": {
        "type": "string",
        "enum": [
          "INVALID_REQUEST",
          "INVALID_PARAMETERS",
          "SUBSCRIPTION_REQUIRED",
          "CAMPAIGN_NOT_FOUND",
          "CAMPAIGN_ALREADY_RUNNING",
          "SCHEDULE_MISSING",
          "STEPS_MISSING",
          "NO_RECIPIENTS",
          "TOO_MANY_RECIPIENTS",
          "PLANNED_START_INVALID",
          "PLANNED_START_IN_PAST",
          "EMAIL_PROVIDER_MISSING",
          "EMAIL_PROVIDER_DISCONNECTED",
          "EMAIL_PROVIDER_EXPIRED",
          "ONE_EMAIL_PROVIDER_EXPIRED",
          "EMAIL_ADDRESS_DISCONNECTED",
          "TRACKING_DOMAINS_REQUIRED",
          "LINKEDIN_ACCOUNT_MISSING",
          "LINKEDIN_COOKIE_EXPIRED",
          "LINKEDIN_SLOT_EXPIRED",
          "WHATSAPP_ACCOUNT_EXPIRED",
          "WHATSAPP_SLOT_EXPIRED",
          "NO_IDENTITY_SET",
          "IDENTITY_FOREIGN_ACCOUNT",
          "IDENTITY_EMAIL_PROVIDER_MISSING",
          "LINKEDIN_ACCOUNT_AMBIGUOUS",
          "WHATSAPP_ACCOUNT_AMBIGUOUS",
          "LINKEDIN_AUDIO_MISSING",
          "EMAIL_STEP_NO_ACTIVE_VERSION",
          "EMAIL_VERSION_NO_CONTENT",
          "EMAIL_VERSION_NO_SUBJECT",
          "RECIPIENTS_NO_EMAIL",
          "RECIPIENTS_NO_LINKEDIN",
          "MC_LINKEDIN_DAILY_LIMIT_CONNECTION",
          "MC_LINKEDIN_DAILY_LIMIT",
          "DAILY_CONTACT_TOO_LOW",
          "LINKEDIN_DAILY_CONTACT_LIMIT",
          "VERIFY_EMAIL_TOKEN_INVALID",
          "VERIFY_EMAIL_TOKEN_EXPIRED",
          "FREE_CREDITS_DOMAIN_ALREADY_CLAIMED",
          "FREE_CREDITS_ALREADY_GRANTED",
          "FREE_CREDITS_NOT_ELIGIBLE",
          "RATE_LIMITED",
          "STATS_COMPUTING",
          "WARMUP_PROVIDER_UNSUPPORTED",
          "ACCOUNT_DISCONNECTED"
        ],
        "description": "Stable machine readable error code. Present only on catalogued errors (mainly the campaign start checks), on validation errors (`INVALID_REQUEST`) and on some LinkedIn provider errors (`ACCOUNT_DISCONNECTED`, `RATE_LIMITED`). Most other errors have no code: rely on the HTTP status and on `error`. Codes are never renamed once deployed."
      },
      "Error": {
        "type": "object",
        "description": "Error body of the Emelia API. `error` is a human readable English message (sometimes French for a few legacy messages). `code` and `params` are only present for catalogued errors. Most business errors, including \"not found\", are returned with HTTP 400.",
        "properties": {
          "error": {
            "description": "Human readable message. A few legacy routes return an empty object `{}` here instead of a string.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "object"
              }
            ]
          },
          "code": {
            "$ref": "#/components/schemas/ErrorCode"
          },
          "params": {
            "type": "object",
            "additionalProperties": {
              "type": [
                "string",
                "number"
              ]
            },
            "description": "Values interpolated in the message, for example `{\"max\": 30000}`."
          }
        },
        "required": [
          "error"
        ]
      },
      "Success": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          }
        },
        "required": [
          "success"
        ]
      },
      "ActivityEvent": {
        "type": "string",
        "enum": [
          "VISITED",
          "INVITED",
          "ACCEPTED",
          "MESSAGE_SENT",
          "INMAIL_SENT",
          "LINKEDIN_REPLIED",
          "MAIL_REPLIED",
          "RE_REPLY_EMAIL",
          "RE_REPLY_LINKEDIN",
          "FOLLOWED",
          "LIKED",
          "SENT",
          "BOUNCED",
          "RE_REPLY",
          "OPENED",
          "UNSUBSCRIBED",
          "CLICKED",
          "WHATSAPP_MESSAGE_SENT",
          "WHATSAPP_REPLIED",
          "TASK_COMPLETED",
          "TASK_FAILED",
          "FAILED_ACTION",
          "EXCLUDED"
        ],
        "description": "Activity event of a multichannel campaign. `SENT`, `OPENED`, `CLICKED`, `BOUNCED`, `UNSUBSCRIBED`, `MAIL_REPLIED` are email events; `VISITED`, `INVITED`, `ACCEPTED`, `MESSAGE_SENT`, `INMAIL_SENT`, `LIKED`, `FOLLOWED`, `LINKEDIN_REPLIED` are LinkedIn events. `RE_REPLY` and `FAILED_ACTION` are never returned by the activities route."
      },
      "StepDelay": {
        "type": "object",
        "required": [
          "amount",
          "unit"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "minimum": 0
          },
          "unit": {
            "type": "string",
            "enum": [
              "MINUTES",
              "HOURS",
              "DAYS"
            ]
          }
        },
        "description": "Wait time after the previous action before this step runs. The delay of the first action of the sequence is ignored (new contacts enter according to the daily quotas). With `DAYS`, days that are not in `schedule.days` are skipped. On a `CONDITION` step the delay is the observation window."
      },
      "StepAttachment": {
        "type": "object",
        "required": [
          "name",
          "url"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Public http(s) URL, for example the `url` returned by `POST /advanced/attachments`. Non http(s) URLs make test emails fail."
          },
          "mediaType": {
            "type": "string",
            "enum": [
              "MEDIA",
              "ATTACHMENT",
              "AUDIO"
            ],
            "description": "LinkedIn only."
          },
          "duration": {
            "type": "number",
            "description": "Audio notes only. `POST /advanced/linkedin/audio` returns it in milliseconds."
          }
        }
      },
      "StepVersion": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Version identifier. Generated by the server (ObjectId string) when missing. Keep existing ids when you update a campaign that already ran: statistics and history are attached to them."
          },
          "subject": {
            "type": "string",
            "description": "EMAIL: subject. Leave it empty on a follow up email to reply in the same thread (\"Re: previous subject\"). INMAIL: subject. TASK: the task label shown in manual tasks."
          },
          "message": {
            "type": "string",
            "description": "EMAIL: HTML body. MESSAGE, INMAIL, CONNECTION (invitation note) and WHATSAPP: plain text. Supports `{{variable}}` placeholders and `{a|b|c}` spintax."
          },
          "disabled": {
            "type": "boolean",
            "description": "Excludes the version from A/B rotation (EMAIL)."
          },
          "rawHtml": {
            "type": "boolean",
            "description": "Stored but not read by the multichannel sending path."
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StepAttachment"
            }
          }
        }
      },
      "StepCondition": {
        "type": "object",
        "required": [
          "field",
          "operator",
          "value"
        ],
        "properties": {
          "field": {
            "type": "string",
            "description": "Either an activity condition in UPPER CASE (`ACCEPTED`, `LINKEDIN_REPLIED`, `MAIL_REPLIED`, `OPENED`, `CLICKED`, `UNSUBSCRIBED`, `BOUNCED`, `TASK_COMPLETED`), evaluated within the step delay window, or a contact field prefixed with `$` (`$firstName`, `$jobTitle`, `$myCustomVariable`), evaluated immediately."
          },
          "operator": {
            "type": "string",
            "enum": [
              "EQUAL",
              "DOES_NOT_EQUAL",
              "GREATER",
              "LOWER",
              "CONTAINS",
              "DOES_NOT_CONTAIN",
              "IS_EMPTY",
              "IS_NOT_EMPTY"
            ],
            "description": "Only these values are evaluated by the scheduler; any other value (for example `NOT_EQUAL` or `GREATER_THAN`) always evaluates to false. For activity conditions use `EQUAL` with value `\"true\"`."
          },
          "value": {
            "type": "string"
          }
        }
      },
      "CampaignStep": {
        "type": "object",
        "required": [
          "stepType",
          "delay"
        ],
        "description": "One node of the sequence tree. The root is always a `START` step. Each node points to the next one with `next`; a `CONDITION` node branches with `yes` and `no` (a missing branch ends the sequence for the contacts that fall into it). `END_OF_CAMPAIGN` stops the sequence. EMAIL versions are A/B variants (one version per contact); MESSAGE and WHATSAPP versions are successive messages sent in order. A MESSAGE step placed after a CONNECTION step waits for the invitation to be accepted.",
        "properties": {
          "_id": {
            "type": "string",
            "description": "Step identifier, generated by the server (ObjectId string) when missing; the root is `\"START\"`. Any unique string is accepted. Never regenerate the ids of a campaign that already ran."
          },
          "stepType": {
            "type": "string",
            "enum": [
              "START",
              "EMAIL",
              "CONNECTION",
              "MESSAGE",
              "INMAIL",
              "VISIT",
              "LIKE",
              "FOLLOW",
              "AUDIO",
              "WHATSAPP",
              "TASK",
              "CONDITION",
              "API_CALL",
              "END_OF_CAMPAIGN"
            ],
            "description": "`FOLLOW`, `AUDIO` and `API_CALL` are accepted when saved, but nothing runs them: no action happens and the step is never marked as done. Do not use them. A LinkedIn voice note is a `MESSAGE` step whose first version carries an attachment with `mediaType: \"AUDIO\"`, uploaded with `POST /advanced/linkedin/audio`."
          },
          "delay": {
            "$ref": "#/components/schemas/StepDelay"
          },
          "identities": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Legacy field, not read when sending. Send `[]`. Sending accounts are chosen automatically among the campaign identities."
          },
          "versions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StepVersion"
            },
            "description": "Content of EMAIL, CONNECTION, MESSAGE, INMAIL, WHATSAPP and TASK steps."
          },
          "cc": {
            "type": "string",
            "description": "EMAIL only: carbon copy address, may be a variable such as `{{assistantEmail}}`; ignored when the resolved value is not a valid email."
          },
          "conditions": {
            "$ref": "#/components/schemas/StepCondition"
          },
          "next": {
            "$ref": "#/components/schemas/CampaignStep"
          },
          "yes": {
            "$ref": "#/components/schemas/CampaignStep"
          },
          "no": {
            "$ref": "#/components/schemas/CampaignStep"
          }
        }
      },
      "CampaignSchedule": {
        "type": "object",
        "description": "Sending settings of a multichannel campaign. Defaults of a new campaign are shown.",
        "properties": {
          "dailyEmailAdded": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500,
            "default": 35,
            "description": "New contacts entering the email part of the sequence per day."
          },
          "dailyEmailLimit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500,
            "default": 100,
            "description": "Maximum emails sent per day. Must be greater than or equal to `dailyEmailAdded`."
          },
          "dailyLinkedinAdded": {
            "type": "integer",
            "minimum": 1,
            "maximum": 80,
            "default": 20,
            "description": "New contacts per day on LinkedIn. Real ceiling: 40 when the sequence contains a CONNECTION step, 80 otherwise."
          },
          "dailyWhatsappAdded": {
            "type": "integer",
            "minimum": 1,
            "maximum": 30,
            "default": 20
          },
          "bcc": {
            "type": "string",
            "description": "Blind carbon copy address added to every email (empty string to disable)."
          },
          "trackLinks": {
            "type": "boolean",
            "default": false,
            "description": "Required for `CLICKED` conditions and click statistics."
          },
          "trackOpens": {
            "type": "boolean",
            "default": false,
            "description": "Required for `OPENED` conditions and open statistics."
          },
          "timeZone": {
            "type": "string",
            "default": "Europe/Brussels",
            "description": "IANA time zone, for example `Europe/Paris`."
          },
          "days": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 0,
              "maximum": 6
            },
            "uniqueItems": true,
            "default": [
              0,
              1,
              2,
              3,
              4
            ],
            "description": "Sending days, 0 = Monday ... 6 = Sunday."
          },
          "start": {
            "type": "string",
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "default": "08:00"
          },
          "end": {
            "type": "string",
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "default": "17:00"
          },
          "blacklistUnsub": {
            "type": "boolean",
            "default": false,
            "description": "Adds contacts who unsubscribe to your blacklist."
          },
          "excludeAlreadyMessaged": {
            "type": "boolean"
          },
          "eventToStop": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ActivityEvent"
            },
            "description": "A contact leaves the sequence as soon as one of these events happens. New campaigns start with `[\"MAIL_REPLIED\", \"LINKEDIN_REPLIED\", \"WHATSAPP_REPLIED\"]`. Bounced and unsubscribed contacts always stop."
          },
          "alwaysOn": {
            "type": "boolean",
            "default": false,
            "description": "Keeps the campaign running when all contacts are done, so that contacts added later are processed."
          },
          "ignoreAutoReplies": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "CampaignSettingsInput": {
        "type": "object",
        "required": [
          "trackLinks",
          "trackOpens",
          "blacklistUnsub",
          "start",
          "end"
        ],
        "description": "Settings to merge into the campaign schedule. `trackLinks`, `trackOpens` and `blacklistUnsub` are required by validation; `start` and `end` are optional in the validator but the handler crashes without them, so always send them. Send the full object you read with `GET /advanced/campaigns/{campaignId}` (field `schedule`).",
        "properties": {
          "dailyEmailAdded": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500,
            "default": 35,
            "description": "New contacts entering the email part of the sequence per day."
          },
          "dailyEmailLimit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500,
            "default": 100,
            "description": "Maximum emails sent per day. Must be greater than or equal to `dailyEmailAdded`."
          },
          "dailyLinkedinAdded": {
            "type": "integer",
            "minimum": 1,
            "maximum": 80,
            "default": 20,
            "description": "New contacts per day on LinkedIn. Real ceiling: 40 when the sequence contains a CONNECTION step, 80 otherwise."
          },
          "dailyWhatsappAdded": {
            "type": "integer",
            "minimum": 1,
            "maximum": 30,
            "default": 20
          },
          "bcc": {
            "type": "string",
            "description": "Blind carbon copy address added to every email (empty string to disable)."
          },
          "trackLinks": {
            "type": "boolean",
            "default": false,
            "description": "Required for `CLICKED` conditions and click statistics."
          },
          "trackOpens": {
            "type": "boolean",
            "default": false,
            "description": "Required for `OPENED` conditions and open statistics."
          },
          "timeZone": {
            "type": "string",
            "default": "Europe/Brussels",
            "description": "IANA time zone, for example `Europe/Paris`."
          },
          "days": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 0,
              "maximum": 6
            },
            "uniqueItems": true,
            "default": [
              0,
              1,
              2,
              3,
              4
            ],
            "description": "Sending days, 0 = Monday ... 6 = Sunday."
          },
          "start": {
            "type": "string",
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "default": "08:00"
          },
          "end": {
            "type": "string",
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
            "default": "17:00"
          },
          "blacklistUnsub": {
            "type": "boolean",
            "default": false,
            "description": "Adds contacts who unsubscribe to your blacklist."
          },
          "excludeAlreadyMessaged": {
            "type": "boolean"
          },
          "eventToStop": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ActivityEvent"
            },
            "description": "A contact leaves the sequence as soon as one of these events happens. New campaigns start with `[\"MAIL_REPLIED\", \"LINKEDIN_REPLIED\", \"WHATSAPP_REPLIED\"]`. Bounced and unsubscribed contacts always stop."
          },
          "alwaysOn": {
            "type": "boolean",
            "default": false,
            "description": "Keeps the campaign running when all contacts are done, so that contacts added later are processed."
          },
          "ignoreAutoReplies": {
            "type": "boolean",
            "default": false
          },
          "name": {
            "type": "string",
            "description": "Optional new name."
          }
        }
      },
      "Identity": {
        "type": "object",
        "description": "Sender identity: a name plus the accounts used on each channel.",
        "properties": {
          "_id": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "userId": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "description": "Email provider id (see `GET /email-providers`).",
            "$ref": "#/components/schemas/ObjectId"
          },
          "linkedin": {
            "description": "LinkedIn account id.",
            "$ref": "#/components/schemas/ObjectId"
          },
          "whatsapp": {
            "description": "WhatsApp account id.",
            "$ref": "#/components/schemas/ObjectId"
          },
          "__v": {
            "type": "integer"
          }
        }
      },
      "IdentityInput": {
        "type": "object",
        "required": [
          "name"
        ],
        "description": "`name` is required and at least one of `email`, `linkedin`, `whatsapp`. Duplicated accounts are silently dropped. Accounts that do not belong to you are rejected.",
        "properties": {
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "description": "Email provider id (`_id` from `GET /email-providers`)."
          },
          "linkedin": {
            "type": "string",
            "description": "LinkedIn account id."
          },
          "whatsapp": {
            "type": "string",
            "description": "WhatsApp account id."
          }
        },
        "anyOf": [
          {
            "required": [
              "email"
            ]
          },
          {
            "required": [
              "linkedin"
            ]
          },
          {
            "required": [
              "whatsapp"
            ]
          }
        ]
      },
      "CampaignStatus": {
        "type": "string",
        "enum": [
          "DRAFT",
          "RUNNING",
          "PAUSED",
          "FINISHED",
          "ARCHIVED"
        ]
      },
      "CampaignSummary": {
        "type": "object",
        "properties": {
          "_id": {
            "description": "Campaign id. With `all=true` the first item is a synthetic entry whose `_id` is `\"ALL_CAMPAIGNS\"`.",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/CampaignStatus"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startAt": {
            "type": "string",
            "format": "date-time"
          },
          "plannedStart": {
            "type": "string",
            "format": "date-time"
          },
          "identities": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "_id": {
                  "$ref": "#/components/schemas/ObjectId"
                },
                "email": {
                  "$ref": "#/components/schemas/ObjectId"
                },
                "linkedin": {
                  "$ref": "#/components/schemas/ObjectId"
                }
              }
            }
          },
          "hasDisconnectedLinkedin": {
            "type": "boolean"
          },
          "hasDisconnectedEmail": {
            "type": "boolean"
          }
        }
      },
      "ListRef": {
        "type": "object",
        "properties": {
          "_id": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "name": {
            "type": "string"
          },
          "contactCount": {
            "type": "integer"
          }
        }
      },
      "Campaign": {
        "type": "object",
        "description": "Full multichannel campaign as stored (internal fields such as `__v`, `hubspotIntegration`, `pipedriveIntegration` and `zapierData` are returned too).",
        "properties": {
          "_id": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/CampaignStatus"
          },
          "tagIds": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ObjectId"
            }
          },
          "identities": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Identity"
            }
          },
          "schedule": {
            "$ref": "#/components/schemas/CampaignSchedule"
          },
          "steps": {
            "$ref": "#/components/schemas/CampaignStep"
          },
          "recipients": {
            "type": "object",
            "properties": {
              "lists": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ListRef"
                }
              },
              "excludedLists": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ListRef"
                }
              },
              "processing": {
                "type": "boolean"
              }
            }
          },
          "zapierData": {
            "type": "array",
            "description": "Webhooks registered on this campaign.",
            "items": {
              "type": "object",
              "properties": {
                "_id": {
                  "$ref": "#/components/schemas/ObjectId"
                },
                "webhookUrl": {
                  "type": "string"
                },
                "events": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "hubspotIntegration": {
            "type": "object"
          },
          "pipedriveIntegration": {
            "type": "object"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startAt": {
            "type": "string",
            "format": "date-time"
          },
          "estimatedEnd": {
            "type": "string",
            "format": "date-time"
          },
          "plannedStart": {
            "type": "string",
            "format": "date-time"
          },
          "__v": {
            "type": "integer"
          }
        }
      },
      "ContactInput": {
        "type": "object",
        "description": "Contact to add. At least one of `email` or `linkedinUrlProfile`. Only `email`, `linkedinUrlProfile`, `phone`, `firstName` and `lastName` are stored as native fields; every other key becomes a custom variable usable as `{{key}}` in messages. ALL VALUES MUST BE STRINGS: a number, an object or an array makes the whole request fail with a 400 error. Empty strings are ignored.",
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "linkedinUrlProfile": {
            "type": "string",
            "description": "https://www.linkedin.com/in/<handle>. Sales Navigator lead URLs (`/sales/lead/` or `/sales/people/`) are converted when possible; other URLs are rejected."
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          }
        },
        "additionalProperties": {
          "type": "string"
        }
      },
      "CampaignContact": {
        "type": "object",
        "properties": {
          "_id": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "email": {
            "type": "string"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "linkedinUrlProfile": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "Status of the contact in this campaign (last meaningful activity event, for example `SENT`, `MAIL_REPLIED`), or `\"-\"` when nothing happened yet."
          },
          "lastContacted": {
            "type": "string",
            "format": "date-time"
          },
          "lastReplied": {
            "type": "string",
            "format": "date-time"
          },
          "integrationContactId": {
            "type": "string"
          },
          "sentiment": {
            "type": "string"
          },
          "custom": {
            "type": "object",
            "additionalProperties": true,
            "description": "Custom variables (legacy inline copy)."
          }
        }
      },
      "Activity": {
        "type": "object",
        "properties": {
          "_id": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "contact": {
            "type": "object",
            "description": "The full contact document (all stored fields except `__v`).",
            "additionalProperties": true
          },
          "event": {
            "$ref": "#/components/schemas/ActivityEvent"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "customData": {
            "type": "object",
            "additionalProperties": true,
            "description": "Event specific data (for example the clicked link), internal keys removed."
          },
          "step": {
            "type": "integer",
            "description": "Depth of the step in the sequence tree (1 = first step after START)."
          },
          "version": {
            "type": "integer",
            "description": "Index of the A/B version (0 = A) when the step has several versions, otherwise -1."
          },
          "reply": {
            "type": "object",
            "description": "Only for `MAIL_REPLIED` and `LINKEDIN_REPLIED`.",
            "properties": {
              "content": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "HTML content of the reply."
              },
              "text": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "subject": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "senderName": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "sender": {
            "type": "object",
            "description": "Account that performed the action, when known.",
            "additionalProperties": true
          }
        }
      },
      "ManualTask": {
        "type": "object",
        "description": "A pending manual task: the contact fields plus the task.",
        "properties": {
          "_id": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "linkedinUrlProfile": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "taskId": {
            "type": "string",
            "description": "Id of the TASK step. Send it back with `POST .../manual-tasks/status`."
          },
          "taskName": {
            "type": "string",
            "description": "`versions[0].subject` of the TASK step."
          },
          "date": {
            "type": "string",
            "format": "date-time",
            "description": "When the task became pending."
          },
          "multipleChoice": {
            "type": "boolean",
            "description": "True when the task is followed by a `TASK_COMPLETED` condition (ok and ko lead to different branches)."
          }
        }
      },
      "StepStatEmail": {
        "type": "object",
        "description": "Statistics of one version of an EMAIL step.",
        "properties": {
          "sent": {
            "type": "number"
          },
          "first_open": {
            "type": "number"
          },
          "opened": {
            "type": "number"
          },
          "clicked": {
            "type": "number"
          },
          "replied": {
            "type": "number"
          },
          "bounced": {
            "type": "number"
          },
          "unsubscribed": {
            "type": "number"
          },
          "first_open_percent": {
            "type": "number"
          },
          "opened_percent": {
            "type": "number"
          },
          "clicked_percent": {
            "type": "number"
          },
          "replied_percent": {
            "type": "number"
          },
          "bounced_percent": {
            "type": "number"
          },
          "unsubscribed_percent": {
            "type": "number"
          },
          "_id": {
            "type": "string"
          },
          "stepId": {
            "type": "string"
          },
          "stepType": {
            "type": "string",
            "const": "EMAIL"
          },
          "disabled": {
            "type": "boolean"
          }
        }
      },
      "StepStatOther": {
        "type": "object",
        "description": "Statistics of a non email step.",
        "properties": {
          "visited": {
            "type": "number"
          },
          "invited": {
            "type": "number"
          },
          "accepted": {
            "type": "number"
          },
          "message_sent": {
            "type": "number"
          },
          "inmail_sent": {
            "type": "number"
          },
          "replied": {
            "type": "number"
          },
          "liked": {
            "type": "number"
          },
          "followed": {
            "type": "number"
          },
          "task_completed": {
            "type": "number"
          },
          "_id": {
            "type": "string"
          },
          "stepId": {
            "type": "string"
          },
          "stepType": {
            "type": "string"
          }
        }
      },
      "CampaignStats": {
        "type": "object",
        "properties": {
          "global": {
            "type": "object",
            "properties": {
              "contacts": {
                "type": "number"
              },
              "contacted": {
                "type": "number"
              },
              "replied": {
                "type": "number"
              },
              "visited": {
                "type": "number"
              },
              "invited": {
                "type": "number"
              },
              "accepted": {
                "type": "number"
              },
              "message_sent": {
                "type": "number"
              },
              "inmail_sent": {
                "type": "number"
              },
              "linkedin_replied": {
                "type": "number"
              },
              "mail_replied": {
                "type": "number"
              },
              "liked": {
                "type": "number"
              },
              "sent": {
                "type": "number"
              },
              "opened": {
                "type": "number"
              },
              "clicked": {
                "type": "number"
              },
              "bounced": {
                "type": "number"
              },
              "unsubscribed": {
                "type": "number"
              },
              "progress_percent": {
                "type": "number"
              },
              "accepted_percent": {
                "type": "number"
              },
              "opened_percent": {
                "type": "number"
              },
              "clicked_percent": {
                "type": "number"
              },
              "linkedin_replied_percent": {
                "type": "number"
              },
              "mail_replied_percent": {
                "type": "number"
              },
              "bounced_percent": {
                "type": "number"
              },
              "unsubscribed_percent": {
                "type": "number"
              }
            }
          },
          "steps": {
            "description": "One array per action step, in sequence order: EMAIL steps contain one item per version, other steps one item. For a DRAFT campaign this is an empty OBJECT `{}` instead of an array.",
            "oneOf": [
              {
                "type": "array",
                "items": {
                  "type": "array",
                  "items": {
                    "anyOf": [
                      {
                        "$ref": "#/components/schemas/StepStatEmail"
                      },
                      {
                        "$ref": "#/components/schemas/StepStatOther"
                      }
                    ]
                  }
                }
              },
              {
                "type": "object"
              }
            ]
          }
        }
      },
      "EmailFinderJob": {
        "type": "object",
        "properties": {
          "_id": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "fullname": {
            "type": "string"
          },
          "companyName": {
            "type": "string"
          },
          "companyWebsite": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "running",
              "done",
              "error"
            ]
          },
          "email": {
            "type": "string",
            "description": "Found email, absent when nothing was found."
          },
          "qualification": {
            "type": "string",
            "description": "Qualification returned by the provider for the found email. `invalid` with no email when nothing was found or after a timeout."
          },
          "fallbackFound": {
            "type": "boolean",
            "description": "True when the email was found by the second pass (company name only, used when the domain pass found nothing)."
          },
          "date": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PhoneFinderJob": {
        "type": "object",
        "properties": {
          "_id": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "linkedinUrl": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "running",
              "done",
              "error"
            ]
          },
          "qualification": {
            "type": "string",
            "description": "`found` when a number was found."
          },
          "phoneNumber": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EmailVerifierJob": {
        "type": "object",
        "properties": {
          "_id": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "email": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "running",
              "done",
              "error"
            ]
          },
          "qualification": {
            "type": "string",
            "description": "Deliverability verdict returned by the provider."
          },
          "date": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EmailProvider": {
        "type": "object",
        "description": "A connected sending mailbox. Passwords are masked with `********`.",
        "properties": {
          "_id": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "userId": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "senderName": {
            "type": "string"
          },
          "senderEmail": {
            "type": "string"
          },
          "senderPassword": {
            "type": "string",
            "const": "********"
          },
          "emailType": {
            "type": "string",
            "enum": [
              "GOOGLE",
              "OFFICE",
              "EXCHANGE",
              "SMTP",
              "GOOGLEIMAP"
            ]
          },
          "smtp": {
            "type": "object",
            "properties": {
              "login": {
                "type": "string"
              },
              "password": {
                "type": "string",
                "const": "********"
              },
              "server": {
                "type": "string"
              },
              "port": {
                "type": "integer"
              },
              "ssl": {
                "type": "boolean"
              }
            }
          },
          "imap": {
            "type": "object",
            "properties": {
              "login": {
                "type": "string"
              },
              "password": {
                "type": "string",
                "const": "********"
              },
              "server": {
                "type": "string"
              },
              "port": {
                "type": "integer"
              },
              "ssl": {
                "type": "boolean"
              }
            }
          },
          "customDomain": {
            "type": "object",
            "description": "Custom tracking domain, when set.",
            "properties": {
              "_id": {
                "$ref": "#/components/schemas/ObjectId"
              },
              "domainName": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "dateAdded": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "signature": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "warmup": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "health": {
            "$ref": "#/components/schemas/ObjectId"
          },
          "disabled": {
            "type": "boolean",
            "description": "True when the paid slot of the mailbox expired."
          },
          "disconnected": {
            "type": "boolean",
            "description": "True when the credentials or the OAuth grant stopped working: reconnect the mailbox."
          },
          "campaigns": {
            "type": "array",
            "description": "Non archived campaigns using this mailbox.",
            "items": {
              "type": "object",
              "properties": {
                "_id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "EMAIL",
                    "LINKEDIN",
                    "MULTICANAL"
                  ]
                }
              }
            }
          }
        }
      },
      "EmailProviderInput": {
        "type": "object",
        "required": [
          "senderEmail",
          "emailType"
        ],
        "description": "SMTP and IMAP credentials of the mailbox. The credentials are tested before saving. For Google and Microsoft accounts prefer the OAuth connection in the app.",
        "properties": {
          "senderName": {
            "type": "string"
          },
          "senderEmail": {
            "type": "string",
            "format": "email"
          },
          "senderPassword": {
            "type": "string",
            "description": "Used when `smtp.password` is empty."
          },
          "emailType": {
            "type": "string",
            "enum": [
              "GOOGLE",
              "OFFICE",
              "EXCHANGE",
              "SMTP",
              "GOOGLEIMAP"
            ]
          },
          "smtp": {
            "type": "object",
            "required": [
              "server",
              "port"
            ],
            "properties": {
              "login": {
                "type": "string"
              },
              "password": {
                "type": "string"
              },
              "server": {
                "type": "string"
              },
              "port": {
                "type": "integer"
              },
              "ssl": {
                "type": "boolean"
              }
            }
          },
          "imap": {
            "type": "object",
            "required": [
              "server",
              "port"
            ],
            "properties": {
              "login": {
                "type": "string"
              },
              "password": {
                "type": "string"
              },
              "server": {
                "type": "string"
              },
              "port": {
                "type": "integer"
              },
              "ssl": {
                "type": "boolean"
              }
            }
          },
          "signature": {
            "type": "string"
          },
          "domainName": {
            "type": "string",
            "description": "Optional custom tracking domain to attach."
          }
        }
      },
      "ListsError": {
        "type": "object",
        "description": "Error body of the lists service. Always has `message`. Some errors add a stable `code` and extra fields (`params`, `required`, `max`, `tool`, `status`). On some 500 responses `error` carries the raw internal message.",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string",
            "description": "English, human-readable message."
          },
          "code": {
            "type": "string",
            "description": "Stable error code, when available (for example `PAYLOAD_TOO_LARGE`, `INVALID_JSON`, `export_too_large`, `companies_only`, `enrichment_running`)."
          },
          "params": {
            "type": "object",
            "additionalProperties": true
          },
          "error": {
            "type": "string",
            "description": "Raw internal message (unexpected errors only)."
          }
        },
        "additionalProperties": true
      },
      "ListsAuthError": {
        "type": "object",
        "description": "Authentication error body (401). Unlike the other errors, the message is in `error`.",
        "required": [
          "error",
          "code"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "AUTH_REQUIRED",
              "ACCOUNT_NOT_FOUND"
            ]
          }
        }
      },
      "ListsSuccess": {
        "type": "object",
        "required": [
          "success"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "ListsEnrichmentRunLock": {
        "type": "object",
        "description": "Bulk job currently running on a list.",
        "required": [
          "tool",
          "startedAt",
          "processed",
          "total",
          "found"
        ],
        "properties": {
          "tool": {
            "type": "string",
            "enum": [
              "find-email",
              "find-phone",
              "verify-email",
              "normalize",
              "remove-emojis",
              "remove-spaces",
              "mapping",
              "crm-sync"
            ]
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "processed": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "found": {
            "type": "integer"
          }
        }
      },
      "ListsCrmIntegration": {
        "type": "object",
        "description": "Summary of a CRM synchronization (HubSpot or Pipedrive) attached to the list.",
        "properties": {
          "dynamic": {
            "type": "boolean",
            "description": "True when the list is resynchronized from the CRM."
          },
          "lastSynced": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastManualSyncAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ListsList": {
        "type": "object",
        "description": "A list as returned by `GET /lists/list`. This is the stored document: internal fields (for example `columnPreferences`, `aiActions`, `entries`, `usedIn`, full CRM integration settings) may also be present and are not part of the contract.",
        "required": [
          "_id",
          "name"
        ],
        "properties": {
          "_id": {
            "type": "string",
            "description": "List id."
          },
          "userId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "description": "Import source: `API`, `Manual`, `CSV`, `Sales Navigator`, `HubSpot`, `Pipedrive`, `Filter`, or a legacy value."
          },
          "status": {
            "type": "string",
            "description": "`mapped` when the list is usable, `pending mapping` while a file import waits for its column mapping."
          },
          "companiesOnly": {
            "type": "boolean",
            "description": "True for company-only lists (Sales Navigator account scraps): no contact, no enrichment."
          },
          "numberOfContacts": {
            "type": "integer"
          },
          "numberOfEmails": {
            "type": "integer"
          },
          "numberOfPhones": {
            "type": "integer"
          },
          "numberOfLinkedins": {
            "type": "integer",
            "description": "May be absent on old lists in the unpaginated mode."
          },
          "emailFindRunAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "phoneFindRunAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "enrichmentRun": {
            "$ref": "#/components/schemas/ListsEnrichmentRunLock",
            "description": "Present only while a bulk job is running."
          },
          "tagIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "fileId": {
            "type": [
              "string",
              "null"
            ]
          },
          "scrapId": {
            "type": [
              "string",
              "null"
            ]
          },
          "parentListId": {
            "type": [
              "string",
              "null"
            ]
          },
          "linkedLists": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "ListsListsPage": {
        "type": "object",
        "required": [
          "items",
          "total",
          "page",
          "pageSize",
          "totalLeads"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListsList"
            }
          },
          "total": {
            "type": "integer",
            "description": "Number of lists matching the filters."
          },
          "page": {
            "type": "integer"
          },
          "pageSize": {
            "type": "integer"
          },
          "totalLeads": {
            "type": "integer",
            "description": "Sum of `numberOfContacts` over all matching lists."
          }
        }
      },
      "ListsListDetail": {
        "type": "object",
        "required": [
          "_id",
          "name",
          "source",
          "status",
          "companiesOnly",
          "numberOfContacts",
          "numberOfEmails",
          "numberOfPhones",
          "enrichmentRun",
          "hubspotIntegration",
          "pipedriveIntegration",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "fileId": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "companiesOnly": {
            "type": "boolean"
          },
          "numberOfContacts": {
            "type": "integer"
          },
          "numberOfEmails": {
            "type": "integer"
          },
          "numberOfPhones": {
            "type": "integer"
          },
          "numberOfLinkedins": {
            "type": "integer"
          },
          "emailFindRunAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "phoneFindRunAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "enrichmentRun": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ListsEnrichmentRunLock"
              },
              {
                "type": "null"
              }
            ]
          },
          "hubspotIntegration": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ListsCrmIntegration"
              },
              {
                "type": "null"
              }
            ]
          },
          "pipedriveIntegration": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ListsCrmIntegration"
              },
              {
                "type": "null"
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ListsCreateListResult": {
        "type": "object",
        "required": [
          "success",
          "listId",
          "name"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "listId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "ListsRenameListResult": {
        "type": "object",
        "required": [
          "success",
          "_id",
          "name"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "ListsDeleteListResult": {
        "type": "object",
        "required": [
          "success",
          "deletedLists",
          "deletedContacts",
          "deletedCompanies",
          "campaignsUpdated"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "deletedLists": {
            "type": "integer",
            "description": "The list plus its linked lists."
          },
          "deletedContacts": {
            "type": "integer"
          },
          "deletedCompanies": {
            "type": "integer"
          },
          "campaignsUpdated": {
            "type": "integer",
            "description": "Campaigns whose recipients were updated."
          }
        }
      },
      "ListsCustomFieldValue": {
        "type": "object",
        "required": [
          "technicalName",
          "displayName",
          "value"
        ],
        "properties": {
          "technicalName": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "value": {
            "type": "string",
            "description": "Always returned as a string."
          }
        }
      },
      "ListsContactCompany": {
        "type": "object",
        "description": "Company of a contact. In the paginated listing only the visible columns are returned and `companyId` is absent.",
        "properties": {
          "companyId": {
            "type": "string"
          },
          "companyName": {
            "type": "string"
          },
          "companyDomain": {
            "type": "string"
          },
          "websiteUrl": {
            "type": "string"
          },
          "industry": {
            "type": "string"
          },
          "companySize": {
            "type": "string"
          },
          "employeeCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "country": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "linkedinCompanyUrl": {
            "type": "string"
          },
          "logoUrl": {
            "type": "string"
          },
          "customFields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListsCustomFieldValue"
            }
          }
        }
      },
      "ListsContact": {
        "type": "object",
        "description": "A contact (or a company-only row) of a list. Strings are empty (`\"\"`) or null when unknown, as specified per field. Only these fields are returned: other stored fields (region, postal code, address, timezone, language, age, birth date, education, skills, job start date, years of experience; company email, type, address, founded year, revenue, notes, tags) can be written but are not returned by the API (they appear in the export only partially).",
        "required": [
          "id",
          "rowKind"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Contact id, or `co:<companyId>` for a company-only row."
          },
          "rowKind": {
            "type": "string",
            "enum": [
              "lead",
              "company_only"
            ]
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "fullName": {
            "type": "string",
            "description": "For a company-only row: the company name, or `-`."
          },
          "jobTitle": {
            "type": "string"
          },
          "gender": {
            "type": [
              "string",
              "null"
            ],
            "description": "Free text as imported."
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "secondaryEmail": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "mobilePhone": {
            "type": [
              "string",
              "null"
            ]
          },
          "linkedinUrlProfile": {
            "type": [
              "string",
              "null"
            ]
          },
          "twitterUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "seniority": {
            "type": "string",
            "description": "Known levels are returned with spaces (for example `C LEVEL`)."
          },
          "department": {
            "type": "string"
          },
          "bio": {
            "type": "string"
          },
          "profilePictureUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "emailVerified": {
            "type": "string",
            "enum": [
              "verified",
              "unverified",
              "invalid",
              "unknown"
            ],
            "description": "Result of the last email verification; `unknown` when never verified."
          },
          "status": {
            "type": "string",
            "description": "Latest engagement status written by the campaigns (for example `SENT`, `OPENED`, `MAIL_REPLIED`, `ACCEPTED`), empty when none."
          },
          "company": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ListsContactCompany"
              },
              {
                "type": "null"
              }
            ]
          },
          "labels": {
            "type": "array",
            "description": "Always empty. Not returned in the paginated listing.",
            "items": {
              "type": "object"
            }
          },
          "customFields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListsCustomFieldValue"
            }
          },
          "createdAt": {
            "type": "string",
            "description": "ISO date, or empty string."
          },
          "updatedAt": {
            "type": "string",
            "description": "ISO date, or empty string."
          }
        }
      },
      "ListsContactsPage": {
        "type": "object",
        "required": [
          "items",
          "total",
          "page",
          "pageSize"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListsContact"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total matching rows (paginated mode) or number of matches (exact lookup)."
          },
          "page": {
            "type": "integer"
          },
          "pageSize": {
            "type": "integer"
          },
          "customVariables": {
            "type": "array",
            "description": "Custom variables shown in this list or used by the returned rows. Absent in the exact lookup mode.",
            "items": {
              "type": "object",
              "required": [
                "technicalName",
                "displayName",
                "schema"
              ],
              "properties": {
                "technicalName": {
                  "type": "string"
                },
                "displayName": {
                  "type": "string"
                },
                "schema": {
                  "type": "string",
                  "enum": [
                    "lead",
                    "company"
                  ]
                }
              }
            }
          }
        }
      },
      "ListsContactFields": {
        "type": "object",
        "description": "Flat contact object: one key per field, scalar values only. Standard keys are listed in the description of `POST /lists/list/{listId}/contacts`; any other key goes to an existing custom variable of the same name or creates a new one. Numbers and booleans are converted to strings. `null` and empty strings are ignored. Arrays and objects are rejected.",
        "minProperties": 1,
        "additionalProperties": {
          "type": [
            "string",
            "number",
            "boolean",
            "null"
          ]
        },
        "example": {
          "firstName": "John",
          "lastName": "Doe",
          "email": "john.doe@acme.com",
          "companyName": "Acme",
          "icebreaker": "Loved your talk at SaaStock"
        }
      },
      "ListsCreatedCustomVariable": {
        "type": "object",
        "required": [
          "technicalName",
          "displayName",
          "schema"
        ],
        "properties": {
          "technicalName": {
            "type": "string"
          },
          "displayName": {
            "type": "string",
            "description": "The key as sent."
          },
          "schema": {
            "type": "string",
            "enum": [
              "lead",
              "company"
            ],
            "description": "Always `lead` for variables created by the contact routes."
          }
        }
      },
      "ListsAddContactResult": {
        "type": "object",
        "required": [
          "success",
          "leadId",
          "companyId",
          "duplicate",
          "updated",
          "createdCustomVariables"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "leadId": {
            "type": "string",
            "description": "Id of the created contact, or of the existing one when `duplicate` is true."
          },
          "companyId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of the company row, null when the contact has none."
          },
          "duplicate": {
            "type": "boolean",
            "description": "True when a contact with the same email or LinkedIn URL already existed: nothing was created."
          },
          "updated": {
            "type": "boolean",
            "description": "True when that existing contact was updated (`updateIfExists=true`)."
          },
          "createdCustomVariables": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListsCreatedCustomVariable"
            }
          }
        }
      },
      "ListsBatchRowResult": {
        "type": "object",
        "required": [
          "index"
        ],
        "description": "Outcome of one row. Either `error` is set, or `leadId`, `companyId`, `duplicate` and `updated` are. For a duplicate of an earlier row of the same batch, `companyId` is null.",
        "properties": {
          "index": {
            "type": "integer",
            "description": "Position of the row in `contacts`, starting at 0."
          },
          "leadId": {
            "type": "string"
          },
          "companyId": {
            "type": [
              "string",
              "null"
            ]
          },
          "duplicate": {
            "type": "boolean"
          },
          "updated": {
            "type": "boolean"
          },
          "error": {
            "type": "string"
          }
        }
      },
      "ListsBatchResult": {
        "type": "object",
        "required": [
          "success",
          "total",
          "created",
          "duplicates",
          "updated",
          "failed",
          "results",
          "createdCustomVariables"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true,
            "description": "True even when some rows failed."
          },
          "total": {
            "type": "integer"
          },
          "created": {
            "type": "integer"
          },
          "duplicates": {
            "type": "integer",
            "description": "Includes the updated duplicates."
          },
          "updated": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "description": "One entry per input row, in input order.",
            "items": {
              "$ref": "#/components/schemas/ListsBatchRowResult"
            }
          },
          "createdCustomVariables": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListsCreatedCustomVariable"
            }
          }
        }
      },
      "ListsUpdateContactResult": {
        "type": "object",
        "required": [
          "success",
          "leadId",
          "companyId",
          "createdCustomVariables"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "leadId": {
            "type": "string"
          },
          "companyId": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdCustomVariables": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListsCreatedCustomVariable"
            }
          }
        }
      },
      "ListsFilterRule": {
        "type": "object",
        "description": "Column filter. Text comparisons are case-insensitive. `not_equals` and `not_contains` also match empty values. `emailVerified` only accepts `equals` and `not_equals` with `verified`, `unverified`, `invalid` or `unknown`. Custom variables are addressed as `custom` + technical name with an uppercase first letter (`icebreaker` becomes `customIcebreaker`). At most 10 rules; values are cut at 200 characters.",
        "required": [
          "column",
          "operator"
        ],
        "properties": {
          "column": {
            "type": "string",
            "description": "Contact columns: `firstName`, `lastName`, `fullName`, `jobTitle`, `email`, `secondaryEmail`, `phone`, `mobilePhone`, `linkedinUrlProfile`, `twitterUrl`, `country`, `city`, `seniority`, `department`, `bio`, `gender`, `emailVerified`. Company columns: `companyName`, `companyDomain`, `websiteUrl`, `industry`, `linkedinCompanyUrl`, `companyCountry`, `companyCity`. Custom variables: `custom<TechnicalName>`."
          },
          "operator": {
            "type": "string",
            "enum": [
              "equals",
              "not_equals",
              "contains",
              "not_contains",
              "empty",
              "not_empty"
            ]
          },
          "value": {
            "type": "string",
            "maxLength": 200,
            "description": "Required except for `empty` and `not_empty`."
          }
        }
      },
      "ListsExportRequest": {
        "type": "object",
        "required": [
          "listIds"
        ],
        "properties": {
          "listIds": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string"
            },
            "description": "Lists to export, in the order of the ZIP. Invalid ids and lists that are not yours are skipped."
          },
          "leadIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Export only these contacts (single-list export only)."
          },
          "search": {
            "type": "string",
            "description": "Same as `search` of the rows route (single-list export only)."
          },
          "statuses": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Engagement statuses, `NO_ACTIVITY` included (single-list export only)."
          },
          "filters": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/ListsFilterRule"
            },
            "description": "Column filters (single-list export only)."
          },
          "async": {
            "type": "boolean",
            "default": false,
            "description": "Allow an asynchronous job (202) when the export covers more than 5,000 contacts."
          }
        }
      },
      "ListsExportJobAccepted": {
        "type": "object",
        "required": [
          "jobId",
          "status",
          "total"
        ],
        "properties": {
          "jobId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "failed"
            ]
          },
          "total": {
            "type": "integer",
            "description": "Contacts covered by this request (estimate based on the list counters)."
          }
        }
      },
      "ListsExportJob": {
        "type": "object",
        "required": [
          "jobId",
          "status",
          "rows",
          "total",
          "filename",
          "error"
        ],
        "properties": {
          "jobId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "failed"
            ]
          },
          "rows": {
            "type": "integer",
            "description": "Rows written so far."
          },
          "total": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Rows to write, null until counted."
          },
          "filename": {
            "type": [
              "string",
              "null"
            ],
            "description": "File name, set when done."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Failure reason when `status` is `failed`."
          }
        }
      },
      "ListsCampaignUsage": {
        "type": "object",
        "required": [
          "usage"
        ],
        "properties": {
          "usage": {
            "type": "object",
            "description": "Keys are the requested list ids.",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "object",
                "required": [
                  "_id",
                  "name",
                  "status"
                ],
                "properties": {
                  "_id": {
                    "type": "string",
                    "description": "Campaign id."
                  },
                  "name": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "description": "Campaign status (for example `DRAFT`, `RUNNING`, `PAUSED`, `FINISHED`)."
                  }
                }
              }
            }
          }
        }
      },
      "ListsCustomVariableCreateRequest": {
        "type": "object",
        "required": [
          "technicalName",
          "schema"
        ],
        "additionalProperties": false,
        "properties": {
          "technicalName": {
            "type": "string",
            "minLength": 1,
            "description": "Name used as the contact key and in `{{variables}}`. Normalized (see the route description)."
          },
          "schema": {
            "type": "string",
            "enum": [
              "lead",
              "company"
            ],
            "description": "`lead`: stored on the contact. `company`: stored on the company row of the contact."
          },
          "displayName": {
            "type": "string",
            "description": "Label shown in the app. Defaults to the technical name."
          }
        }
      },
      "ListsCustomVariable": {
        "type": "object",
        "required": [
          "technicalName",
          "displayName",
          "schema"
        ],
        "properties": {
          "technicalName": {
            "type": "string",
            "description": "Name as stored after normalization."
          },
          "displayName": {
            "type": "string"
          },
          "schema": {
            "type": "string",
            "enum": [
              "lead",
              "company"
            ]
          }
        }
      },
      "ListsCustomVariableCheck": {
        "type": "object",
        "required": [
          "exists",
          "technicalName"
        ],
        "properties": {
          "exists": {
            "type": "boolean"
          },
          "reason": {
            "type": "string",
            "enum": [
              "custom_variable",
              "lead_field",
              "company_field"
            ],
            "description": "Present when `exists` is true."
          },
          "technicalName": {
            "type": "string",
            "description": "The normalized name that was checked."
          }
        }
      },
      "ScrapperStatus": {
        "type": "string",
        "enum": [
          "running",
          "planified",
          "paused",
          "enriching",
          "done",
          "error"
        ],
        "description": "Life cycle of a scraper:\n\n- `planified`: waiting for `plannedStart`.\n- `running`: extraction in progress. For a segmented extraction, also between two chunks (see `betweenSegment`).\n- `paused`: paused by you (segmented extractions only) or by Emelia when credits ran out. Restart with resume.\n- `done`: extraction finished; also the state after an enrichment ends.\n- `enriching`: email enrichment in progress (`POST /{id}/enrich`).\n- `error`: stopped, the reason is in `error`."
      },
      "ScrapperErrorCode": {
        "type": "string",
        "enum": [
          "INVALID_COOKIE",
          "2FA_REQUIRED",
          "BAD_SALES_NAV_URL",
          "NEED_SUBSCRIPTION",
          "TOO_MUCH_RESULT",
          "RATE_LIMIT",
          "SEGMENTATION_ERROR",
          "PROCESS_ERROR",
          "NO_AUTH_FOUND"
        ],
        "description": "Why a scraper stopped (`status: error`):\n\n- `INVALID_COOKIE`: LinkedIn rejected the session (cookie expired or user logged out; for classic searches, campaign account disconnected). Refresh the account, then resume.\n- `2FA_REQUIRED`: LinkedIn asks for a two-step verification on the account. Validate it on LinkedIn, then create a new scraper (resume is refused).\n- `BAD_SALES_NAV_URL`: the URL is not a Sales Navigator people search, lead list or account search, or LinkedIn answers 404 (deleted or expired search).\n- `NEED_SUBSCRIPTION`: free extraction refused because this LinkedIn account was already used for a free extraction by another Emelia workspace. A subscription is needed.\n- `TOO_MUCH_RESULT`: more than 80,000 results. Narrow the search.\n- `RATE_LIMIT`: LinkedIn limited the account. Sales Navigator scrapers are retried automatically about 12 hours later; you can also resume later.\n- `SEGMENTATION_ERROR`: the large search could not be planned in chunks. Contact support.\n- `PROCESS_ERROR`: unexpected technical error. Contact support.\n- `NO_AUTH_FOUND`: no LinkedIn account attached to the scraper."
      },
      "ScrapperSegment": {
        "type": "object",
        "description": "One chunk of a segmented extraction (the search plus one extra filter, at most 2,500 results).",
        "properties": {
          "_id": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{24}$",
            "description": "Chunk id."
          },
          "url": {
            "type": "string",
            "description": "Sales Navigator URL of the chunk."
          },
          "count": {
            "type": "integer",
            "description": "Contacts expected in this chunk (2,500 max)."
          },
          "scrapIndex": {
            "type": "integer",
            "description": "Contacts extracted in this chunk."
          },
          "complete": {
            "type": "boolean",
            "description": "True when the chunk is finished. Absent before."
          }
        }
      },
      "ScrapperWebhook": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{24}$",
            "description": "Webhook entry id."
          },
          "webhookUrl": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Events requested at registration."
          }
        }
      },
      "ScrapperScrap": {
        "type": "object",
        "description": "A scraper (one extraction of a LinkedIn search).",
        "properties": {
          "_id": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{24}$",
            "description": "Scrapper id."
          },
          "userId": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{24}$",
            "description": "Workspace id. Only returned by `GET /linkedin-scrappers/{id}`."
          },
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "description": "Search URL given at creation."
          },
          "scrapType": {
            "type": "string",
            "enum": [
              "people",
              "account",
              "classic"
            ],
            "description": "`people`: Sales Navigator people search or lead list. `account`: Sales Navigator account (company) search; detected when the extraction starts, so it reads `people` before. `classic`: classic LinkedIn people search."
          },
          "status": {
            "$ref": "#/components/schemas/ScrapperStatus"
          },
          "error": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ScrapperErrorCode"
              },
              {
                "type": "null"
              }
            ],
            "description": "Error code when `status` is `error`."
          },
          "errorDetail": {
            "type": "object",
            "additionalProperties": true,
            "description": "Internal diagnostic written by the scraper (free text in French). Not a stable contract: use `error`."
          },
          "totalContact": {
            "type": "integer",
            "description": "Contacts to extract: 0 until LinkedIn reports the result count, then capped by the plan rules and `maxContacts`. Final count once `done`. Reduced by a split or a filter."
          },
          "scrapIndex": {
            "type": "integer",
            "description": "Contacts extracted so far."
          },
          "maxContacts": {
            "type": "integer",
            "description": "Value of `max_contacts` at creation, 0 = no limit."
          },
          "segmented": {
            "type": "boolean",
            "description": "True when the search was split into chunks (Grow and Scale, 3,000 results or more)."
          },
          "segments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScrapperSegment"
            }
          },
          "estimatedEnd": {
            "type": "string",
            "format": "date-time",
            "description": "Estimated end of a segmented extraction."
          },
          "nextRunningSegment": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Computed for running segmented scrapers: when the next chunk is scheduled."
          },
          "betweenSegment": {
            "type": "boolean",
            "description": "Computed for running segmented scrapers: true when no chunk is being extracted right now."
          },
          "plannedStart": {
            "type": "string",
            "format": "date-time",
            "description": "Scheduled start, when given at creation."
          },
          "date": {
            "type": "string",
            "format": "date-time",
            "description": "Creation date."
          },
          "lastUpdate": {
            "type": "string",
            "format": "date-time",
            "description": "Last time the scraper wrote progress."
          },
          "enrichEnabled": {
            "type": "boolean",
            "description": "True once an email enrichment was started."
          },
          "startEnrichDate": {
            "type": "string",
            "format": "date-time",
            "description": "Start of the email enrichment."
          },
          "enrichIndex": {
            "type": "integer",
            "description": "Contacts processed by the enrichment. Only set at the end."
          },
          "enrichFound": {
            "type": "integer",
            "description": "Emails found. Only set at the end of the enrichment."
          },
          "enrichPhoneEnabled": {
            "type": "boolean",
            "description": "True when a phone enrichment was started from the app."
          },
          "enrichPhoneIndex": {
            "type": "integer"
          },
          "enrichPhoneFound": {
            "type": "integer"
          },
          "processing": {
            "type": "boolean",
            "description": "True while `POST /{id}/filter` runs."
          },
          "webhooks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScrapperWebhook"
            }
          },
          "authes": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "description": "LinkedIn account ids used by the scraper."
          },
          "auth": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{24}$",
            "description": "Legacy single account id (old scrapers only).",
            "deprecated": true
          },
          "convertedListId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Id of the Emelia list created automatically from the finished scraper, `null` before."
          }
        }
      },
      "ScrapperListResponse": {
        "type": "object",
        "required": [
          "success",
          "scraps"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "scraps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScrapperScrap"
            }
          }
        }
      },
      "ScrapperGetResponse": {
        "type": "object",
        "required": [
          "success",
          "scrap"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "scrap": {
            "$ref": "#/components/schemas/ScrapperScrap"
          }
        }
      },
      "ScrapperCreateRequest": {
        "type": "object",
        "required": [
          "name",
          "authes",
          "url"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Unique among your scrapers. Keep it at 100 characters or less (the rename limit)."
          },
          "authes": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{24}$"
            },
            "description": "LinkedIn account ids. Sales Navigator URL: ids of accounts connected for scraping (`authId` returned by `POST /linkedin-scrappers/authes`). Classic search URL: ids of the LinkedIn accounts connected to your campaigns. All must be valid and yours. Several accounts share the chunks of a segmented extraction; otherwise one of them is picked at random."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Sales Navigator people search, lead list or account search URL, or classic LinkedIn people search URL, copied from the browser address bar."
          },
          "plannedStart": {
            "type": "string",
            "format": "date-time",
            "description": "Optional ISO 8601 start date, at least one hour ahead. The scraper stays `planified` until then."
          },
          "max_contacts": {
            "type": "integer",
            "minimum": 1,
            "description": "Optional maximum number of contacts. Approximate on Sales Navigator (page of 25), ignored on segmented extractions, exact on classic searches."
          }
        }
      },
      "ScrapperCreateResponse": {
        "type": "object",
        "required": [
          "success",
          "scrapId"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "scrapId": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{24}$",
            "description": "Id of the new scraper."
          }
        }
      },
      "ScrapperSuccessResponse": {
        "type": "object",
        "required": [
          "success"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          }
        }
      },
      "ScrapperRenameRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          }
        }
      },
      "ScrapperSplitResponse": {
        "type": "object",
        "required": [
          "success",
          "scrapId"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "scrapId": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{24}$",
            "description": "Id of the NEW finished scraper (`<name> 1`) holding the contacts extracted so far. The original id keeps running as `<name> 2`."
          }
        }
      },
      "ScrapperEnrichRequest": {
        "type": "object",
        "required": [
          "country"
        ],
        "additionalProperties": false,
        "properties": {
          "country": {
            "type": "string",
            "minLength": 1,
            "description": "ISO 3166-1 alpha-2 country code of the contacts, used as a hint (for example `FR`, `US`). Not validated.",
            "example": "FR"
          }
        }
      },
      "ScrapperEnrichResponse": {
        "type": "object",
        "required": [
          "success",
          "status"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "status": {
            "type": "boolean",
            "description": "Always `true` (not a status string)."
          }
        }
      },
      "ScrapperDownloadResponse": {
        "type": "object",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "string",
            "description": "CSV content: `;` separator, header line first, UTF-8 without BOM."
          }
        }
      },
      "ScrapperWebhookRequest": {
        "type": "object",
        "required": [
          "webhookUrl",
          "events"
        ],
        "additionalProperties": false,
        "properties": {
          "webhookUrl": {
            "type": "string",
            "format": "uri",
            "description": "Public URL that accepts a POST with a JSON body."
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "FINISHED",
                "ERROR"
              ]
            },
            "description": "At least one of `FINISHED`, `ERROR`. Today every registered URL receives every event anyway."
          }
        }
      },
      "ScrapperWebhookDeleteRequest": {
        "type": "object",
        "required": [
          "webhookUrl"
        ],
        "additionalProperties": false,
        "properties": {
          "webhookUrl": {
            "type": "string",
            "format": "uri",
            "description": "URL to remove (exact match)."
          }
        }
      },
      "ScrapperEmptyResponse": {
        "type": "object",
        "maxProperties": 0,
        "description": "Current behavior of the webhook routes: an empty object. Treat HTTP 200 as success."
      },
      "ScrapperWebhookEvent": {
        "type": "object",
        "required": [
          "event",
          "id",
          "name"
        ],
        "description": "JSON body that Emelia POSTs to your webhook URL.",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "FINISHED",
              "ERROR"
            ],
            "description": "`FINISHED`: extraction over (`status` `done`). `ERROR`: scraper stopped (`status` `error`)."
          },
          "id": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{24}$",
            "description": "Scrapper id."
          },
          "name": {
            "type": "string",
            "description": "Scrapper name at the time of the event."
          },
          "error": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ScrapperErrorCode"
              }
            ],
            "description": "Error code, only with `ERROR`."
          }
        }
      },
      "ScrapperFilterRequest": {
        "type": "object",
        "required": [
          "filters"
        ],
        "additionalProperties": false,
        "properties": {
          "filters": {
            "type": "integer",
            "minimum": 1,
            "description": "Bit field. `1` = remove the profiles found in your 10 most recent finished scrapers. No other bit is implemented."
          }
        }
      },
      "ScrapperFilterResponse": {
        "type": "object",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "boolean",
            "description": "Always `true`: the filter was started."
          }
        }
      },
      "ScrapperViewport": {
        "type": "object",
        "required": [
          "width",
          "height"
        ],
        "description": "Window size of the browser the cookies come from. Both values required when the object is sent.",
        "properties": {
          "width": {
            "type": "number"
          },
          "height": {
            "type": "number"
          }
        }
      },
      "ScrapperCookie": {
        "type": "object",
        "required": [
          "name",
          "value"
        ],
        "additionalProperties": true,
        "description": "A browser cookie as exported by the browser (extra attributes such as `path`, `expires`, `secure` are accepted). Without `domain`, `.www.linkedin.com` is used.",
        "properties": {
          "name": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "domain": {
            "type": "string"
          }
        }
      },
      "ScrapperAuthCookies": {
        "type": "object",
        "required": [
          "li_at"
        ],
        "description": "Session cookies of a LinkedIn account logged in to Sales Navigator.",
        "properties": {
          "li_at": {
            "type": "string",
            "minLength": 1,
            "description": "Value of the `li_at` cookie (LinkedIn session). Required."
          },
          "li_a": {
            "type": "string",
            "description": "Value of the `li_a` cookie (Sales Navigator session)."
          },
          "jsessionid": {
            "type": "string",
            "deprecated": true,
            "description": "Accepted but ignored. Send `JSESSIONID` in `additionnalsCookie`."
          },
          "ua": {
            "type": "string",
            "description": "User agent of the browser the cookies come from."
          },
          "additionnalsCookie": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/ScrapperCookie"
                },
                {
                  "type": "string",
                  "description": "`name=value` string: accepted, but not used by the Sales Navigator scraper."
                }
              ]
            },
            "description": "Other linkedin.com cookies of the same session (spelling with two n is the real field name). `li_at`, `li_a` and `JSESSIONID` entries are dropped (the latter is then lost)."
          },
          "viewport": {
            "$ref": "#/components/schemas/ScrapperViewport"
          },
          "country": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 code of the country the account is used from (for example `FR`). Emelia browses LinkedIn through a proxy located in this country."
          }
        }
      },
      "ScrapperAuthIdResponse": {
        "type": "object",
        "required": [
          "success",
          "authId"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "authId": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{24}$",
            "description": "Account id."
          }
        }
      },
      "LegacyContact": {
        "type": "object",
        "description": "An email contact.",
        "properties": {
          "_id": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "phoneNumber": {
            "type": "string"
          },
          "custom": {
            "type": "object",
            "additionalProperties": true,
            "description": "Custom variables."
          },
          "status": {
            "type": "string",
            "description": "Latest status, e.g. SENT, OPENED, CLICKED, REPLIED, BOUNCED, UNSUBSCRIBED, BLACKLISTED. '-' when none."
          },
          "mailsSent": {
            "type": "integer"
          },
          "lastContacted": {
            "type": "string",
            "format": "date-time"
          },
          "lastOpen": {
            "type": "string",
            "format": "date-time"
          },
          "lastReplied": {
            "type": "string",
            "format": "date-time"
          },
          "interested": {
            "type": "string",
            "description": "Legacy field, always \"-\" in campaign contact listings."
          }
        }
      },
      "LegacyLKContact": {
        "type": "object",
        "description": "A LinkedIn contact.",
        "properties": {
          "_id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "handle": {
            "type": "string"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "custom": {
            "type": "object",
            "additionalProperties": true
          },
          "status": {
            "type": "string",
            "description": "e.g. VISITED, INVITED, ACCEPTED, MESSAGE_SENT, INMAIL_SENT, REPLIED, EXCLUDED, CANT_CONNECT. '-' when none."
          },
          "lastContacted": {
            "type": "string",
            "format": "date-time"
          },
          "lastReplied": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LegacyActivity": {
        "type": "object",
        "description": "One email activity event (as shaped by the list endpoints).",
        "properties": {
          "_id": {
            "type": "string"
          },
          "contact": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "$ref": "#/components/schemas/LegacyContact"
              }
            ],
            "description": "Contact id, or the populated contact."
          },
          "event": {
            "type": "string",
            "enum": [
              "SENT",
              "FIRST_OPEN",
              "OPENED",
              "CLICKED",
              "REPLIED",
              "BOUNCED",
              "UNSUBSCRIBED"
            ]
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "step": {
            "type": "integer",
            "description": "0-based step index, -1 if unresolved."
          },
          "version": {
            "type": "integer",
            "description": "0-based version index, -1 when the step has a single version."
          },
          "customData": {
            "type": "object",
            "additionalProperties": true,
            "description": "Event metadata (internal keys are stripped)."
          }
        }
      },
      "LegacyLKActivity": {
        "type": "object",
        "description": "One LinkedIn activity event.",
        "properties": {
          "_id": {
            "type": "string"
          },
          "contact": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "$ref": "#/components/schemas/LegacyLKContact"
              }
            ]
          },
          "event": {
            "type": "string",
            "enum": [
              "VISITED",
              "INVITED",
              "ACCEPTED",
              "MESSAGE_SENT",
              "INMAIL_SENT",
              "REPLIED",
              "FOLLOWED",
              "LIKED"
            ]
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "step": {
            "type": "integer"
          },
          "version": {
            "type": "integer"
          },
          "customData": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "LegacyBlacklist": {
        "type": "object",
        "properties": {
          "_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "blacklistContent": {
            "type": [
              "string",
              "null"
            ],
            "description": "Newline-separated emails/domains."
          },
          "linkedinBlacklistContent": {
            "type": [
              "string",
              "null"
            ],
            "description": "Newline-separated LinkedIn handles."
          }
        }
      },
      "LegacyEmailScheduleOut": {
        "type": "object",
        "properties": {
          "bcc": {
            "type": "string"
          },
          "dailyContact": {
            "type": "integer"
          },
          "dailyLimit": {
            "type": "integer"
          },
          "minInterval": {
            "type": "integer"
          },
          "maxInterval": {
            "type": "integer"
          },
          "blacklistUnsub": {
            "type": "boolean"
          },
          "trackLinks": {
            "type": "boolean"
          },
          "trackOpens": {
            "type": "boolean"
          },
          "timeZone": {
            "type": "string"
          },
          "days": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "start": {
            "type": "string"
          },
          "end": {
            "type": "string"
          },
          "eventToStopMails": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "LegacyEmailCampaign": {
        "type": "object",
        "description": "A full email campaign document (as returned by create/get/update).",
        "properties": {
          "_id": {
            "type": "string"
          },
          "userId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "RUNNING",
              "PAUSED",
              "FINISHED",
              "ARCHIVED"
            ]
          },
          "provider": {
            "type": [
              "string",
              "null"
            ]
          },
          "useManyProviders": {
            "type": "boolean"
          },
          "providersUsed": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "schedule": {
            "$ref": "#/components/schemas/LegacyEmailScheduleOut"
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "_id": {
                  "type": "string"
                },
                "delay": {
                  "type": "object",
                  "properties": {
                    "amount": {
                      "type": "integer"
                    },
                    "unit": {
                      "type": "string"
                    }
                  }
                },
                "versions": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "_id": {
                        "type": "string"
                      },
                      "subject": {
                        "type": "string"
                      },
                      "message": {
                        "type": "string"
                      },
                      "disabled": {
                        "type": "boolean"
                      },
                      "rawHtml": {
                        "type": "boolean"
                      },
                      "attachments": {
                        "type": "array",
                        "items": {
                          "type": "object"
                        }
                      },
                      "options": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "recipients": {
            "type": "object",
            "properties": {
              "lists": {
                "type": "array",
                "items": {
                  "type": "object",
                  "description": "Populated to { _id, name } on GET; array of ObjectId strings otherwise."
                }
              },
              "contacts": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "processing": {
                "type": "boolean"
              }
            }
          },
          "zapierData": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "webhookUrl": {
                  "type": "string"
                },
                "events": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startAt": {
            "type": "string",
            "format": "date-time"
          },
          "estimatedEnd": {
            "type": "string",
            "format": "date-time"
          },
          "plannedStart": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LegacyEmailCampaignListItem": {
        "type": "object",
        "description": "Reduced campaign shape returned by the list endpoint.",
        "properties": {
          "_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startAt": {
            "type": "string",
            "format": "date-time"
          },
          "plannedStart": {
            "type": "string",
            "format": "date-time"
          },
          "useManyProviders": {
            "type": "boolean"
          },
          "providersUsed": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "provider": {
            "type": [
              "object",
              "null"
            ],
            "description": "Populated email provider."
          }
        }
      },
      "LegacyLinkedinScheduleOut": {
        "type": "object",
        "properties": {
          "dailyContact": {
            "type": "integer"
          },
          "timeZone": {
            "type": "string"
          },
          "days": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "start": {
            "type": "string"
          },
          "end": {
            "type": "string"
          },
          "excludeAlreadyMessaged": {
            "type": "boolean"
          },
          "exclude1stConn": {
            "type": "boolean"
          },
          "excludeNoPictures": {
            "type": "boolean"
          }
        }
      },
      "LegacyLinkedinCampaign": {
        "type": "object",
        "description": "A full LinkedIn campaign document.",
        "properties": {
          "_id": {
            "type": "string"
          },
          "userId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "RUNNING",
              "PAUSED",
              "FINISHED",
              "ARCHIVED"
            ]
          },
          "account": {
            "type": [
              "string",
              "null"
            ],
            "description": "LinkedIn account (authe) id."
          },
          "schedule": {
            "$ref": "#/components/schemas/LegacyLinkedinScheduleOut"
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "_id": {
                  "type": "string"
                },
                "stepType": {
                  "type": "string",
                  "enum": [
                    "VISIT",
                    "CONNECTION",
                    "MESSAGE",
                    "LIKE",
                    "FOLLOW",
                    "INMAIL"
                  ]
                },
                "delay": {
                  "type": "object",
                  "properties": {
                    "amount": {
                      "type": "integer"
                    },
                    "unit": {
                      "type": "string"
                    }
                  }
                },
                "versions": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "_id": {
                        "type": "string"
                      },
                      "message": {
                        "type": "string"
                      },
                      "disabled": {
                        "type": "boolean"
                      },
                      "attachments": {
                        "type": "array",
                        "items": {
                          "type": "object"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "recipients": {
            "type": "object",
            "properties": {
              "lists": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "contacts": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "processing": {
                "type": "boolean"
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startAt": {
            "type": "string",
            "format": "date-time"
          },
          "estimatedEnd": {
            "type": "string",
            "format": "date-time"
          },
          "plannedStart": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LegacyLinkedinCampaignListItem": {
        "type": "object",
        "description": "Reduced LinkedIn campaign shape returned by the list endpoint.",
        "properties": {
          "_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "startAt": {
            "type": "string",
            "format": "date-time"
          },
          "plannedStart": {
            "type": "string",
            "format": "date-time"
          },
          "account": {
            "type": [
              "object",
              "null"
            ],
            "description": "Populated to { _id, metadata }."
          }
        }
      },
      "LegacyEmailStatsSummary": {
        "type": "object",
        "description": "Flat email stats summary (detailed omitted).",
        "properties": {
          "mailsSent": {
            "type": "integer"
          },
          "uniqueOpensPercent": {
            "type": "number"
          },
          "opens": {
            "type": "integer"
          },
          "linkClickedPercent": {
            "type": "number"
          },
          "repliedPercent": {
            "type": "number"
          },
          "bouncedPercent": {
            "type": "number"
          },
          "unsubscribePercent": {
            "type": "number"
          },
          "progressPercent": {
            "type": "number"
          }
        }
      },
      "LegacyLinkedinStatsSummary": {
        "type": "object",
        "description": "Flat LinkedIn stats summary (detailed omitted).",
        "properties": {
          "visited": {
            "type": "integer"
          },
          "invited": {
            "type": "integer"
          },
          "accepted": {
            "type": "integer"
          },
          "message_sent": {
            "type": "integer"
          },
          "replied": {
            "type": "integer"
          },
          "progress_percent": {
            "type": "number"
          },
          "invite_percent": {
            "type": "number"
          }
        }
      },
      "LegacyStatsDetailed": {
        "type": "object",
        "description": "Detailed stats (detailed=true): global totals plus a per-step, per-version breakdown.",
        "properties": {
          "global": {
            "type": "object",
            "additionalProperties": true
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "WebhookCampaignType": {
        "type": "string",
        "enum": [
          "email",
          "linkedin",
          "advanced"
        ],
        "description": "Campaign family. `advanced` = current multichannel campaigns (the default campaigns of the app and of the Campaigns endpoints). `email` = legacy email only campaigns. `linkedin` = legacy LinkedIn only campaigns."
      },
      "WebhookEmailEvent": {
        "type": "string",
        "description": "Events delivered for legacy email campaigns (`type: email`). `CAMPAIGN_ENDING` is accepted but never delivered for email campaigns in the current code.",
        "enum": [
          "SENT",
          "OPENED",
          "CLICKED",
          "REPLIED",
          "BOUNCED",
          "UNSUBSCRIBED",
          "INTERESTED",
          "CAMPAIGN_ENDING"
        ]
      },
      "WebhookLinkedinEvent": {
        "type": "string",
        "description": "Events delivered for legacy LinkedIn campaigns (`type: linkedin`). `LIKED`, `FOLLOWED` and `CAMPAIGN_ENDING` are never delivered for these campaigns.",
        "enum": [
          "VISITED",
          "INVITED",
          "ACCEPTED",
          "MESSAGE_SENT",
          "INMAIL_SENT",
          "REPLIED"
        ]
      },
      "WebhookAdvancedEvent": {
        "type": "string",
        "description": "Events delivered for advanced (multichannel) campaigns (`type: advanced`). The email sent event is `EMAIL_SENT` (not `SENT`, not `MAIL_SENT`). `CAMPAIGN_ENDING` is delivered with a different body (see the campaignEnding webhook).",
        "enum": [
          "EMAIL_SENT",
          "OPENED",
          "CLICKED",
          "MAIL_REPLIED",
          "BOUNCED",
          "UNSUBSCRIBED",
          "VISITED",
          "INVITED",
          "ACCEPTED",
          "MESSAGE_SENT",
          "INMAIL_SENT",
          "LIKED",
          "LINKEDIN_REPLIED",
          "WHATSAPP_MESSAGE_SENT",
          "WHATSAPP_REPLIED",
          "CAMPAIGN_ENDING"
        ]
      },
      "WebhookRegistration": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Registration ID (use it with the test endpoints).",
            "example": "6702c1d4e5f6a7b8c9d0e1f2"
          },
          "typeOfCampaign": {
            "$ref": "#/components/schemas/WebhookCampaignType"
          },
          "campaignId": {
            "type": "string",
            "example": "6651f0c2a1b2c3d4e5f60718"
          },
          "campaignName": {
            "type": "string",
            "example": "Acme Q4 multichannel"
          },
          "url": {
            "type": "string",
            "example": "https://hooks.acme.com/emelia/9f2c4e1a7b"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "MAIL_REPLIED",
              "LINKEDIN_REPLIED"
            ]
          },
          "status": {
            "type": "string",
            "const": "active",
            "description": "Always `active`: webhooks are never disabled automatically."
          }
        }
      },
      "WebhookListResponse": {
        "type": "object",
        "required": [
          "webhooks"
        ],
        "properties": {
          "webhooks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookRegistration"
            }
          }
        }
      },
      "WebhookSentiment": {
        "type": "object",
        "description": "AI classification of a reply. Present only when reply classification is enabled on the account and AI credits remain. Classified by Jev, with Gemini as fallback; only the Gemini fallback can add `additionalData`.",
        "properties": {
          "classification": {
            "type": "string",
            "enum": [
              "INTERESTED",
              "NOT_INTERESTED",
              "NEUTRAL",
              "OUT_OF_OFFICE",
              "HOSTILE",
              "DELEGATION",
              "INCOMPREHENSION",
              "QUESTION_ASK"
            ]
          },
          "score": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Confidence of the classification."
          },
          "additionalData": {
            "type": "object",
            "description": "Gemini fallback only: `delegationEmail` for DELEGATION, `returnDate` (DD/MM/YYYY) for OUT_OF_OFFICE.",
            "properties": {
              "delegationEmail": {
                "type": "string"
              },
              "returnDate": {
                "type": "string"
              }
            }
          }
        }
      },
      "WebhookEmailContact": {
        "type": "object",
        "description": "Contact of a legacy email campaign. Starts with every custom field of the contact (any key), then the standard fields, which win on name clashes. `company` is the value of the first custom field whose name contains `company` (case insensitive), absent otherwise. No contact ID.",
        "properties": {
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "phoneNumber": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "company": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "WebhookLinkedinContact": {
        "type": "object",
        "description": "Contact of a legacy LinkedIn campaign: custom fields first, then the standard fields. `id` (Emelia contact ID) and `email` are only present when the event was detected in real time by the API server (replies and acceptances); events from the LinkedIn worker never have them.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Real time replies and acceptances only."
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "description": "Real time replies and acceptances only."
          },
          "linkedinUrlProfile": {
            "type": "string",
            "format": "uri"
          },
          "linkedinId": {
            "type": "string",
            "description": "LinkedIn member ID."
          },
          "company": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "WebhookAdvancedContact": {
        "type": "object",
        "description": "Contact of an advanced campaign: legacy custom fields (`custom`) first, then the standard fields. The set depends on the emitter: most events carry `id`, `firstName`, `lastName`, `email`, `company`; OPENED, CLICKED and UNSUBSCRIBED carry `linkedinUrlProfile` instead of `id`; replies and acceptances detected in real time add `linkedinUrlProfile` and `linkedinId`. No phone number. `company` comes only from a custom field whose name contains `company`, not from the lead company name.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Absent on OPENED, CLICKED and UNSUBSCRIBED."
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "linkedinUrlProfile": {
            "type": "string",
            "description": "Only on OPENED, CLICKED, UNSUBSCRIBED and real time LinkedIn replies or acceptances."
          },
          "linkedinId": {
            "type": "string",
            "description": "Only on real time LinkedIn replies or acceptances."
          },
          "company": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "WebhookEmailActivityPayload": {
        "type": "object",
        "required": [
          "event",
          "contact",
          "date",
          "step"
        ],
        "properties": {
          "event": {
            "$ref": "#/components/schemas/WebhookEmailEvent"
          },
          "campaign": {
            "type": "string",
            "description": "Campaign name. Missing on BOUNCED (bug)."
          },
          "campaignId": {
            "type": "string",
            "description": "REPLIED only."
          },
          "sender": {
            "type": "string",
            "description": "Address of the Emelia sending mailbox (the one that received the reply). REPLIED, OPENED and CLICKED only."
          },
          "senderName": {
            "type": "string",
            "description": "REPLIED only."
          },
          "contact": {
            "$ref": "#/components/schemas/WebhookEmailContact"
          },
          "date": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 UTC. Time at which Emelia processed the event: send time, detection time for replies (not the reply's own date for email), processing time 30 to 60 seconds after the open or click for OPENED and CLICKED."
          },
          "step": {
            "type": "integer",
            "description": "0 based index of the sequence step. Always 0 for BOUNCED and INTERESTED. Can be -1 when the step cannot be resolved."
          },
          "reply": {
            "type": "string",
            "description": "REPLIED only. Plain text of the reply (HTML when no text part), usually including the quoted thread."
          },
          "sentiment": {
            "$ref": "#/components/schemas/WebhookSentiment"
          },
          "messageId": {
            "type": "string",
            "description": "REPLIED only. Message-ID of the reply, usable to answer in the same thread."
          },
          "link": {
            "type": "string",
            "description": "CLICKED only. Original URL of the clicked link."
          }
        }
      },
      "WebhookLinkedinActivityPayload": {
        "type": "object",
        "required": [
          "event",
          "campaign",
          "contact",
          "date",
          "step"
        ],
        "properties": {
          "event": {
            "$ref": "#/components/schemas/WebhookLinkedinEvent"
          },
          "campaign": {
            "type": "string",
            "description": "Campaign name."
          },
          "campaignId": {
            "type": "string",
            "description": "Only on replies and acceptances detected in real time."
          },
          "contact": {
            "$ref": "#/components/schemas/WebhookLinkedinContact"
          },
          "date": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 UTC. Action time, or message time for replies."
          },
          "step": {
            "type": "integer",
            "description": "0 based index of the sequence step (-1 when not resolved)."
          },
          "reply": {
            "type": "string",
            "description": "REPLIED only."
          },
          "sentiment": {
            "$ref": "#/components/schemas/WebhookSentiment"
          },
          "content": {
            "type": "string",
            "description": "MESSAGE_SENT only: text of the message sent."
          }
        }
      },
      "WebhookAdvancedActivityPayload": {
        "type": "object",
        "required": [
          "event",
          "contact",
          "date",
          "step"
        ],
        "properties": {
          "event": {
            "$ref": "#/components/schemas/WebhookAdvancedEvent"
          },
          "campaign": {
            "type": "string",
            "description": "Campaign name. Missing on BOUNCED (bug)."
          },
          "campaignId": {
            "type": "string",
            "description": "Only on MAIL_REPLIED and on LinkedIn replies or acceptances detected in real time."
          },
          "senderName": {
            "type": "string",
            "description": "MAIL_REPLIED only."
          },
          "sender": {
            "type": "string",
            "description": "MAIL_REPLIED only: address of the Emelia mailbox that received the reply."
          },
          "contact": {
            "$ref": "#/components/schemas/WebhookAdvancedContact"
          },
          "date": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 UTC. Processing time for most events; message time for LinkedIn and WhatsApp replies; 30 to 60 seconds after the fact for OPENED, CLICKED and UNSUBSCRIBED."
          },
          "step": {
            "type": "integer",
            "description": "Indicative only. Actions: position of the step in its branch (0 for the first step after the start). OPENED, CLICKED, UNSUBSCRIBED: same but counting the start node (EMAIL_SENT 0 then OPENED 1 for the same email). MAIL_REPLIED: index among the email steps. BOUNCED, WHATSAPP_REPLIED, LINKEDIN_REPLIED and most ACCEPTED: 0."
          },
          "reply": {
            "type": "string",
            "description": "MAIL_REPLIED, LINKEDIN_REPLIED, WHATSAPP_REPLIED."
          },
          "sentiment": {
            "$ref": "#/components/schemas/WebhookSentiment"
          },
          "content": {
            "type": "string",
            "description": "MESSAGE_SENT only: text of the LinkedIn message sent."
          },
          "messageId": {
            "type": "string",
            "description": "MAIL_REPLIED only."
          },
          "link": {
            "type": "string",
            "description": "CLICKED only."
          }
        }
      },
      "WebhookCampaignEndingPayload": {
        "type": "object",
        "required": [
          "date",
          "campaign",
          "id"
        ],
        "properties": {
          "date": {
            "type": "string",
            "format": "date-time",
            "description": "Time of the automatic closing (daily job at 04:00 server time)."
          },
          "campaign": {
            "type": "string",
            "description": "Campaign name."
          },
          "id": {
            "type": "string",
            "description": "Campaign ID."
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, malformed, unknown or disabled API key. The key must be exactly 48 characters, otherwise it is treated as a session token and rejected the same way.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Authentication required"
            }
          }
        }
      },
      "ServerErrorOrRateLimited": {
        "description": "Unexpected error. IMPORTANT: this is also what you receive today when the per minute quota of your account is exceeded (the quota error is not mapped to 429 for API keys). No `Retry-After` header is sent. Wait until the next minute window before retrying.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Internal server error"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid request or business error (the API also uses 400 for \"not found\").",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Campaign not found"
            }
          }
        }
      }
    },
    "parameters": {}
  },
  "webhooks": {
    "emailCampaignActivity": {
      "post": {
        "operationId": "onEmailCampaignActivity",
        "tags": [
          "Webhooks"
        ],
        "summary": "Activity on a legacy email campaign",
        "description": "Sent to URLs registered with `type: email` (or without `type`).\n\n- `SENT`: right after a sequence email is sent (test sends excluded). Network errors retried up to 5 times; the send waits for your endpoint.\n- `OPENED`: tracking pixel loaded, at most once per email and contact, delivered 30 to 60 seconds later. Needs open tracking.\n- `CLICKED`: tracked link clicked, at most once per link and email, delivered 30 to 60 seconds later; two clicks of the same contact on the same step queued within that delay can be merged into the last one. With open tracking on, a click is ignored until an open was recorded.\n- `REPLIED`: first reply of the contact detected in the mailbox (later replies are not sent), with reply text, AI classification, sending mailbox and Message-ID. Network errors retried up to 5 times.\n- `BOUNCED`: bounce detected (non delivery report or refusal at send time). No `campaign` field and `step` always 0. Network errors retried up to 5 times.\n- `UNSUBSCRIBED`: the contact confirms on the unsubscribe page (or a one click unsubscribe POST is received); once per contact.\n- `INTERESTED`: contact marked as interested by a user in the legacy web app (not through the REST API).\n- `CAMPAIGN_ENDING`: accepted at registration, never sent for email campaigns.\n\n**Delivery.** One `POST` per event and per registered URL, header `Content-Type: application/json`, JSON body, no signature or secret header, default User-Agent of the HTTP library. No timeout is set. Your response (status and body) is ignored: an HTTP error is never retried. Network errors are retried only for some events (see each list). No event ID, no ordering guarantee, no replay; a URL registered twice receives everything twice. Absent fields are omitted, never null.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEmailActivityPayload"
              },
              "examples": {
                "SENT": {
                  "summary": "SENT",
                  "value": {
                    "event": "SENT",
                    "campaign": "Acme Q4 outbound",
                    "contact": {
                      "companyName": "Acme",
                      "industry": "Software",
                      "firstName": "John",
                      "lastName": "Doe",
                      "phoneNumber": "+33612345678",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-12T08:15:02.123Z",
                    "step": 0
                  }
                },
                "OPENED": {
                  "summary": "OPENED (sender = sending mailbox)",
                  "value": {
                    "event": "OPENED",
                    "campaign": "Acme Q4 outbound",
                    "sender": "jane@acme-mail.com",
                    "contact": {
                      "companyName": "Acme",
                      "industry": "Software",
                      "firstName": "John",
                      "lastName": "Doe",
                      "phoneNumber": "+33612345678",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-12T09:01:45.000Z",
                    "step": 0
                  }
                },
                "CLICKED": {
                  "summary": "CLICKED",
                  "value": {
                    "event": "CLICKED",
                    "campaign": "Acme Q4 outbound",
                    "sender": "jane@acme-mail.com",
                    "contact": {
                      "companyName": "Acme",
                      "industry": "Software",
                      "firstName": "John",
                      "lastName": "Doe",
                      "phoneNumber": "+33612345678",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-12T09:03:10.000Z",
                    "step": 0,
                    "link": "https://www.acme.com/pricing"
                  }
                },
                "REPLIED": {
                  "summary": "REPLIED",
                  "value": {
                    "event": "REPLIED",
                    "campaign": "Acme Q4 outbound",
                    "campaignId": "6650aa10b2c3d4e5f6071829",
                    "sender": "jane@acme-mail.com",
                    "senderName": "Jane Smith",
                    "contact": {
                      "companyName": "Acme",
                      "industry": "Software",
                      "firstName": "John",
                      "lastName": "Doe",
                      "phoneNumber": "+33612345678",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-13T10:20:00.000Z",
                    "step": 1,
                    "reply": "Hi Jane, sounds interesting. Can we talk on Tuesday?",
                    "sentiment": {
                      "classification": "INTERESTED",
                      "score": 0.92
                    },
                    "messageId": "<CAF7x2y3z@mail.acme.com>"
                  }
                },
                "BOUNCED": {
                  "summary": "BOUNCED (no campaign name, step always 0)",
                  "value": {
                    "event": "BOUNCED",
                    "contact": {
                      "companyName": "Acme",
                      "industry": "Software",
                      "firstName": "John",
                      "lastName": "Doe",
                      "phoneNumber": "+33612345678",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-12T08:16:30.000Z",
                    "step": 0
                  }
                },
                "UNSUBSCRIBED": {
                  "summary": "UNSUBSCRIBED",
                  "value": {
                    "event": "UNSUBSCRIBED",
                    "campaign": "Acme Q4 outbound",
                    "contact": {
                      "companyName": "Acme",
                      "industry": "Software",
                      "firstName": "John",
                      "lastName": "Doe",
                      "phoneNumber": "+33612345678",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-12T11:00:00.000Z",
                    "step": 0
                  }
                },
                "INTERESTED": {
                  "summary": "INTERESTED (marked by a user)",
                  "value": {
                    "event": "INTERESTED",
                    "campaign": "Acme Q4 outbound",
                    "contact": {
                      "companyName": "Acme",
                      "industry": "Software",
                      "firstName": "John",
                      "lastName": "Doe",
                      "phoneNumber": "+33612345678",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-13T14:00:00.000Z",
                    "step": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Any response is accepted. Emelia ignores the status and the body."
          },
          "default": {
            "description": "Ignored too: errors are not retried and the webhook stays active."
          }
        }
      }
    },
    "linkedinCampaignActivity": {
      "post": {
        "operationId": "onLinkedinCampaignActivity",
        "tags": [
          "Webhooks"
        ],
        "summary": "Activity on a legacy LinkedIn campaign",
        "description": "Sent to URLs registered with `type: linkedin` or `linkedin: true`.\n\n- `VISITED`: profile visited.\n- `INVITED`: invitation sent. When the contact was already a connection, `INVITED` and `ACCEPTED` are sent together.\n- `ACCEPTED`: acceptance detected (connections polling, inferred from a reply, or real time LinkedIn notification).\n- `MESSAGE_SENT`: message sent, text in `content`.\n- `INMAIL_SENT`: InMail sent.\n- `REPLIED`: first reply detected, with `reply` and `sentiment`.\n\n`LIKED`, `FOLLOWED` and `CAMPAIGN_ENDING` are never sent for these campaigns. Most events come from the LinkedIn worker (contact without `id` nor `email`). Replies and acceptances detected in real time come from the API server and add `campaignId`, `contact.id` and `contact.email`; that path retries once on network errors, the worker never retries.\n\n**Delivery.** One `POST` per event and per registered URL, header `Content-Type: application/json`, JSON body, no signature or secret header, default User-Agent of the HTTP library. No timeout is set. Your response (status and body) is ignored: an HTTP error is never retried. Network errors are retried only for some events (see each list). No event ID, no ordering guarantee, no replay; a URL registered twice receives everything twice. Absent fields are omitted, never null.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookLinkedinActivityPayload"
              },
              "examples": {
                "VISITED": {
                  "summary": "VISITED",
                  "value": {
                    "event": "VISITED",
                    "campaign": "Acme LinkedIn Q4",
                    "contact": {
                      "companyName": "Acme",
                      "firstName": "John",
                      "lastName": "Doe",
                      "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
                      "company": "Acme",
                      "linkedinId": "ACoAAB1x2y3zAcmeExample"
                    },
                    "date": "2026-10-12T08:30:00.000Z",
                    "step": 0
                  }
                },
                "MESSAGE_SENT": {
                  "summary": "MESSAGE_SENT (with content)",
                  "value": {
                    "event": "MESSAGE_SENT",
                    "campaign": "Acme LinkedIn Q4",
                    "contact": {
                      "companyName": "Acme",
                      "firstName": "John",
                      "lastName": "Doe",
                      "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
                      "company": "Acme",
                      "linkedinId": "ACoAAB1x2y3zAcmeExample"
                    },
                    "date": "2026-10-14T08:30:00.000Z",
                    "step": 2,
                    "content": "Hi John, thanks for connecting. Quick question about Acme..."
                  }
                },
                "REPLIED_worker": {
                  "summary": "REPLIED from the LinkedIn worker",
                  "value": {
                    "event": "REPLIED",
                    "campaign": "Acme LinkedIn Q4",
                    "contact": {
                      "companyName": "Acme",
                      "firstName": "John",
                      "lastName": "Doe",
                      "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
                      "company": "Acme",
                      "linkedinId": "ACoAAB1x2y3zAcmeExample"
                    },
                    "date": "2026-10-15T07:45:12.000Z",
                    "step": 2,
                    "reply": "Hi, happy to chat next week.",
                    "sentiment": {
                      "classification": "INTERESTED",
                      "score": 0.92
                    }
                  }
                },
                "REPLIED_realtime": {
                  "summary": "REPLIED detected in real time (API server)",
                  "value": {
                    "event": "REPLIED",
                    "campaign": "Acme LinkedIn Q4",
                    "campaignId": "6650aa10b2c3d4e5f6071829",
                    "contact": {
                      "companyName": "Acme",
                      "id": "6703d2e5f6a7b8c9d0e1f2a3",
                      "firstName": "John",
                      "lastName": "Doe",
                      "email": "john.doe@acme.com",
                      "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
                      "linkedinId": "ACoAAB1x2y3zAcmeExample",
                      "company": "Acme"
                    },
                    "date": "2026-10-15T07:45:12.000Z",
                    "step": 2,
                    "reply": "Hi, happy to chat next week.",
                    "sentiment": {
                      "classification": "INTERESTED",
                      "score": 0.92
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Any response is accepted. Emelia ignores the status and the body."
          },
          "default": {
            "description": "Ignored too: errors are not retried and the webhook stays active."
          }
        }
      }
    },
    "advancedCampaignActivity": {
      "post": {
        "operationId": "onAdvancedCampaignActivity",
        "tags": [
          "Webhooks"
        ],
        "summary": "Activity on an advanced (multichannel) campaign",
        "description": "Sent to URLs registered with `type: advanced`.\n\n- `EMAIL_SENT`: email step sent (the documented name `MAIL_SENT` never fires).\n- `OPENED`, `CLICKED`, `UNSUBSCRIBED`: same tracking rules as legacy email, delivered 30 to 60 seconds later; contact without `id`, no `sender`.\n- `MAIL_REPLIED`: first email reply, with `reply`, `sentiment`, `sender`, `senderName`, `messageId`, `campaignId`.\n- `BOUNCED`: bounce detected; no `campaign` field, `step` 0.\n- `VISITED`, `INVITED`, `ACCEPTED`, `MESSAGE_SENT` (text in `content`), `INMAIL_SENT`, `LIKED`: LinkedIn actions.\n- `LINKEDIN_REPLIED`: LinkedIn reply with `reply` and `sentiment`; `campaignId`, `linkedinUrlProfile` and `linkedinId` only when detected in real time.\n- `WHATSAPP_MESSAGE_SENT`, `WHATSAPP_REPLIED`: WhatsApp message sent, reply received (`reply`).\n- `CAMPAIGN_ENDING`: see the campaignEnding webhook.\n\nNever sent: `SENT`, `MAIL_SENT`, `FOLLOWED`, `TASK_COMPLETED`, `TASK_FAILED`, `FAILED_ACTION`, `EXCLUDED`, `RE_REPLY_EMAIL`, `RE_REPLY_LINKEDIN`. Only the real time LinkedIn path retries (once, network errors).\n\n**Delivery.** One `POST` per event and per registered URL, header `Content-Type: application/json`, JSON body, no signature or secret header, default User-Agent of the HTTP library. No timeout is set. Your response (status and body) is ignored: an HTTP error is never retried. Network errors are retried only for some events (see each list). No event ID, no ordering guarantee, no replay; a URL registered twice receives everything twice. Absent fields are omitted, never null.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookAdvancedActivityPayload"
              },
              "examples": {
                "EMAIL_SENT": {
                  "summary": "EMAIL_SENT",
                  "value": {
                    "event": "EMAIL_SENT",
                    "campaign": "Acme Q4 multichannel",
                    "contact": {
                      "companyName": "Acme",
                      "id": "6703d2e5f6a7b8c9d0e1f2a3",
                      "firstName": "John",
                      "lastName": "Doe",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-12T08:15:02.123Z",
                    "step": 0
                  }
                },
                "OPENED": {
                  "summary": "OPENED (no contact id, no sender)",
                  "value": {
                    "event": "OPENED",
                    "campaign": "Acme Q4 multichannel",
                    "contact": {
                      "companyName": "Acme",
                      "firstName": "John",
                      "lastName": "Doe",
                      "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-12T09:01:45.000Z",
                    "step": 1
                  }
                },
                "MAIL_REPLIED": {
                  "summary": "MAIL_REPLIED",
                  "value": {
                    "event": "MAIL_REPLIED",
                    "campaign": "Acme Q4 multichannel",
                    "campaignId": "6651f0c2a1b2c3d4e5f60718",
                    "senderName": "Jane Smith",
                    "sender": "jane@acme-mail.com",
                    "contact": {
                      "companyName": "Acme",
                      "id": "6703d2e5f6a7b8c9d0e1f2a3",
                      "firstName": "John",
                      "lastName": "Doe",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-13T10:20:00.000Z",
                    "step": 0,
                    "reply": "Hi Jane, sounds interesting. Can we talk on Tuesday?",
                    "sentiment": {
                      "classification": "INTERESTED",
                      "score": 0.92
                    },
                    "messageId": "<CAF7x2y3z@mail.acme.com>"
                  }
                },
                "INVITED": {
                  "summary": "INVITED",
                  "value": {
                    "event": "INVITED",
                    "campaign": "Acme Q4 multichannel",
                    "contact": {
                      "companyName": "Acme",
                      "id": "6703d2e5f6a7b8c9d0e1f2a3",
                      "firstName": "John",
                      "lastName": "Doe",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-12T08:40:00.000Z",
                    "step": 1
                  }
                },
                "MESSAGE_SENT": {
                  "summary": "MESSAGE_SENT (with content)",
                  "value": {
                    "event": "MESSAGE_SENT",
                    "campaign": "Acme Q4 multichannel",
                    "contact": {
                      "companyName": "Acme",
                      "id": "6703d2e5f6a7b8c9d0e1f2a3",
                      "firstName": "John",
                      "lastName": "Doe",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "content": "Hi John, thanks for connecting. Quick question about Acme...",
                    "date": "2026-10-14T08:30:00.000Z",
                    "step": 3
                  }
                },
                "LINKEDIN_REPLIED_realtime": {
                  "summary": "LINKEDIN_REPLIED detected in real time",
                  "value": {
                    "event": "LINKEDIN_REPLIED",
                    "campaign": "Acme Q4 multichannel",
                    "campaignId": "6651f0c2a1b2c3d4e5f60718",
                    "contact": {
                      "companyName": "Acme",
                      "id": "6703d2e5f6a7b8c9d0e1f2a3",
                      "firstName": "John",
                      "lastName": "Doe",
                      "email": "john.doe@acme.com",
                      "company": "Acme",
                      "linkedinUrlProfile": "https://www.linkedin.com/in/john-doe-acme",
                      "linkedinId": "ACoAAB1x2y3zAcmeExample"
                    },
                    "date": "2026-10-15T07:45:12.000Z",
                    "step": 0,
                    "reply": "Hi, happy to chat next week.",
                    "sentiment": {
                      "classification": "INTERESTED",
                      "score": 0.92
                    }
                  }
                },
                "WHATSAPP_REPLIED": {
                  "summary": "WHATSAPP_REPLIED",
                  "value": {
                    "event": "WHATSAPP_REPLIED",
                    "campaign": "Acme Q4 multichannel",
                    "contact": {
                      "companyName": "Acme",
                      "id": "6703d2e5f6a7b8c9d0e1f2a3",
                      "firstName": "John",
                      "lastName": "Doe",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-16T12:00:00.000Z",
                    "step": 0,
                    "reply": "Sure, call me tomorrow."
                  }
                },
                "BOUNCED": {
                  "summary": "BOUNCED (no campaign name)",
                  "value": {
                    "event": "BOUNCED",
                    "contact": {
                      "companyName": "Acme",
                      "id": "6703d2e5f6a7b8c9d0e1f2a3",
                      "firstName": "John",
                      "lastName": "Doe",
                      "email": "john.doe@acme.com",
                      "company": "Acme"
                    },
                    "date": "2026-10-12T08:16:30.000Z",
                    "step": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Any response is accepted. Emelia ignores the status and the body."
          },
          "default": {
            "description": "Ignored too: errors are not retried and the webhook stays active."
          }
        }
      }
    },
    "campaignEnding": {
      "post": {
        "operationId": "onCampaignEnding",
        "tags": [
          "Webhooks"
        ],
        "summary": "Campaign finished",
        "description": "Sent once to URLs whose events include `CAMPAIGN_ENDING`, when a campaign is closed automatically by the daily job (04:00 server time). In the current code it is only delivered for advanced campaigns that are not `alwaysOn`; the email and LinkedIn variants are accepted at registration but never sent. Marking a campaign as finished by hand (app or API) does not trigger it, nor does archiving. Single attempt, errors ignored.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCampaignEndingPayload"
              },
              "examples": {
                "ending": {
                  "value": {
                    "date": "2026-10-20T04:00:12.000Z",
                    "campaign": "Acme Q4 multichannel",
                    "id": "6651f0c2a1b2c3d4e5f60718"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Any response is accepted. Emelia ignores the status and the body."
          },
          "default": {
            "description": "Ignored too: errors are not retried and the webhook stays active."
          }
        }
      }
    }
  }
}
