{
  "openapi": "3.1.0",
  "info": {
    "title": "OpenAPI Lonescale",
    "description": "A sample API for Lonescale",
    "license": {
      "name": "MIT"
    },
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://public-api.lonescale.com"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/contacts/{identifier}/enrichment-history": {
      "get": {
        "description": "The last enrichment LoneScale holds for a contact, so you can keep your own long-lived cache and skip an enrichment you have already paid for.\n\n**This is a per-contact record, not a log of your calls.** Three limits follow from that, worth reading before you build on it:\n\n- At most **one entry per contact**: the latest state we know, not one entry per `/trigger/enrich` call. `providers` and `credits` are therefore not returned, since they are not retained per contact.\n- An enrichment that **found nothing leaves no entry**. A contact we searched and missed is indistinguishable from one we never searched: both return an empty `data`.\n- A hit on the 24h enrichment cache does **not** refresh `date`. It is the date of the last enrichment that actually ran, which is the date your cache should key on.\n\nThe history is scoped to your API key: you only ever read contacts your own account enriched.",
        "parameters": [
          {
            "name": "identifier",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The contact, as an email, a phone number, a LinkedIn profile URL or a CRM record id. URL-encode the value (`+` in a phone number especially). A LinkedIn URL can also be passed as its bare vanity slug (`jane-doe`), which avoids encoding slashes altogether.\n\nEmails and phone numbers match secondary values too, not just the main one, and a phone number matches whatever spelling we stored it in: `+33621114568` and `+33 6 21 11 45 68` find the same contact.\n\nOne case to know about: a contact that only ever reached LoneScale through this API, never through a CRM sync or a list import, carries no CRM id and, if you asked for `email` or `phone` alone, no LinkedIn URL either. Look those up by the email or phone we returned."
          },
          {
            "name": "include_data",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Return the enriched contact itself in `data`. Off by default: the entry otherwise carries only what happened, not the data."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100
            },
            "description": "Entries per page."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The `next_cursor` of the previous page. Entries come back newest first."
          }
        ],
        "responses": {
          "200": {
            "description": "The contact's enrichment history. Empty `data` when the contact was never enriched on your account, or was searched without a result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnrichmentHistory"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          }
        }
      }
    },
    "/users": {
      "get": {
        "description": "Get profile information",
        "responses": {
          "200": {
            "description": "Profile information",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/User"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unexpected error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Bad request",
                  "error": "Bad request",
                  "statusCode": 400
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          }
        }
      }
    },
    "/trigger/enrich": {
      "post": {
        "description": "Start waterfall enrichment for contacts.\n\nLoneScale will call your `webhook_url` with this payload once the job is completed.\n\nSee the [Enrichment payload](/api-reference/webhookpayload/enrichment-payload) documentation for the full webhook schema.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnrichmentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Enrichment successfully created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Enrichment"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Bad request",
                  "error": "Bad request",
                  "statusCode": 400
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          }
        }
      }
    },
    "/trigger/enrich/sync": {
      "post": {
        "description": "Enrich contacts synchronously and return results directly in the response — no webhook required.\n\nThe response contains all enriched contacts in a single call. **Maximum 10 contacts per request.** Rate limited to **5 requests per minute, counted per API key** — contact us if your integration needs a higher limit, it can be raised per account.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnrichmentSyncRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Contacts enriched successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SyncEnrichmentResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Bad request",
                  "error": "Bad request",
                  "statusCode": 400
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "403": {
            "description": "Insufficient enrichment credits or admin not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "User has no enrichment credits",
                  "error": "Forbidden",
                  "statusCode": 403
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — max 5 requests per minute",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "statusCode": 429
                }
              }
            }
          }
        }
      }
    },
    "/trigger/contact-sourcing/sync": {
      "post": {
        "description": "Search contacts synchronously and return results directly in the response \u2014 no webhook required.\n\nThe response contains all sourced contacts in a single call. **Maximum 10 contacts per request.** Rate limited to **5 requests per minute**.\n\n**Time budget.** A sync call answers within about 45 seconds. Personas are searched in the order you send them and share the `limit`, so put the most important ones first. When the budget runs out before the limit is reached, the response has `partial: true`: retry with fewer personas, or use the asynchronous endpoint `/trigger/contact-sourcing` with a `webhook_url`, which has no time budget. Only the contacts returned are charged.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactSourcingSyncRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Contacts sourced successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactSourcingSyncResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Bad request",
                  "error": "Bad request",
                  "statusCode": 400
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "403": {
            "description": "Insufficient contact credits or admin not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "User has no contact credits",
                  "error": "Forbidden",
                  "statusCode": 403
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — max 5 requests per minute",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "statusCode": 429
                }
              }
            }
          }
        }
      }
    },
    "/trigger/contact-sourcing": {
      "post": {
        "description": "Search Contacts.\n\nLoneScale will call your `webhook_url` with this payload once the job is completed.\n\nSee the [Sourcing payload](/api-reference/webhookpayload/sourcing-payload) documentation for the full webhook schema.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactSourcingRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Sourcing contacts successfully created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sourcing"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Bad request",
                  "error": "Bad request",
                  "statusCode": 400
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          }
        }
      }
    },
    "/lists": {
      "get": {
        "description": "Retrieve a paginated list of imported lists for the authenticated user, optionally filtered by entity type.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number (10 items per page, starts at 0).",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "entity",
            "in": "query",
            "description": "Filter lists by entity type.",
            "schema": {
              "type": "string",
              "enum": [
                "COMPANY",
                "PEOPLE"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of imported lists",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "list": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/List"
                      }
                    },
                    "hasNextPage": {
                      "type": "boolean"
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          }
        }
      },
      "post": {
        "description": "Create a new imported list.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateListRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Bad request",
                  "error": "Bad request",
                  "statusCode": 400
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          }
        }
      }
    },
    "/lists/{listId}": {
      "get": {
        "description": "Retrieve a specific list by ID.",
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "The ID of the list.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The requested list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "List not found",
                  "error": "Not Found",
                  "statusCode": 404
                }
              }
            }
          }
        }
      },
      "delete": {
        "description": "Delete a list. The list must not be actively used by any workflows.",
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "The ID of the list to delete.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The deleted list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "403": {
            "description": "List is currently used by one or more active workflows",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "List is used by workflows: My Workflow",
                  "error": "Forbidden",
                  "statusCode": 403
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "List not found",
                  "error": "Not Found",
                  "statusCode": 404
                }
              }
            }
          }
        }
      }
    },
    "/lists/{listId}/items": {
      "get": {
        "description": "Retrieve paginated items from a specific list.",
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "The ID of the list.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number (50 items per page, starts at 0).",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list items",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ListItem"
                      }
                    },
                    "hasNextPage": {
                      "type": "boolean"
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "List not found",
                  "error": "Not Found",
                  "statusCode": 404
                }
              }
            }
          }
        }
      }
    },
    "/lists/{listId}/item": {
      "post": {
        "description": "Add an item to a list.\n\n**Validation rules by entity type:**\n\n- **COMPANY**: must include `domain` OR both `name` and `location`.\n- **PEOPLE**: must include at least 2 of: `linkedin_url`, `name`, `location`, `first_name`, `last_name`, `full_name`, `company_name`, `current_position`.",
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "The ID of the list.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddItemRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created list item",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListItem"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Bad request",
                  "error": "Bad request",
                  "statusCode": 400
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "403": {
            "description": "Validation failed — missing required fields for the list entity type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Missing required fields for PEOPLE list",
                  "error": "Forbidden",
                  "statusCode": 403
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "List not found",
                  "error": "Not Found",
                  "statusCode": 404
                }
              }
            }
          }
        }
      }
    },
    "/lists/{listId}/item/{itemId}": {
      "delete": {
        "description": "Remove an item from a list.",
        "parameters": [
          {
            "name": "listId",
            "in": "path",
            "required": true,
            "description": "The ID of the list.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "itemId",
            "in": "path",
            "required": true,
            "description": "The ID of the item to remove.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The parent list after item removal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "404": {
            "description": "List or item not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Item not found",
                  "error": "Not Found",
                  "statusCode": 404
                }
              }
            }
          }
        }
      }
    },
    "/verify-phone": {
      "post": {
        "description": "Verify Phone Numbers \n[Special Access Required]",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyPhoneRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Phone verification successfully created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneVerification"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Bad request",
                  "error": "Bad request",
                  "statusCode": 400
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          }
        }
      }
    },
    "/companies/search": {
      "get": {
        "description": "Resolve a single company by domain, LinkedIn id, LinkedIn slug, or name and return a stable, fixed-shape result.\n\nAt least one of `domain`, `linkedin_id`, `slug`, or `name` is required. The response always returns the same set of fields — `null` (or `[]` for arrays) is used for any field we do not have data for, so client integrations can rely on a stable schema.\n\nWhen `enrich=true` and no cached match is found, the API will trigger an on-demand enrichment via data-sync; when `enrich=true` and a result has both a LinkedIn id and a universal name, a function-by-function headcount breakdown is also attached.\n\nThe optional `custom` map (e.g. `custom[crm_id]=abc-123`) is echoed back unchanged at the top level of the response — useful for correlating results with records on your side.\n\nRate limited to **5 requests per minute**.",
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "required": false,
            "description": "Company domain (e.g. `stripe.com`). Schemes, `www.`, paths, and casing are normalized server-side.",
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "linkedin_id",
            "in": "query",
            "required": false,
            "description": "Numeric LinkedIn company id (the digits in `linkedin.com/company/{id}`).",
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "slug",
            "in": "query",
            "required": false,
            "description": "LinkedIn universal name / slug (the trailing segment in `linkedin.com/company/{slug}`).",
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "name",
            "in": "query",
            "required": false,
            "description": "Company name. Best-effort match — prefer `domain`, `linkedin_id`, or `slug` for deterministic resolution.",
            "schema": {
              "type": "string",
              "minLength": 1
            }
          },
          {
            "name": "enrich",
            "in": "query",
            "required": false,
            "description": "When `true`, fall back to on-demand enrichment if no cached match is found, and attach a `headcount` breakdown when possible. Defaults to `false`.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "custom",
            "in": "query",
            "required": false,
            "description": "Arbitrary key/value passthrough echoed unchanged in the response — useful for correlating results with records on your side. Pass a **URL-encoded JSON object**, e.g. `custom=%7B%22crm_id%22%3A%22abc-123%22%2C%22priority%22%3A1%7D` (decodes to `{\"crm_id\":\"abc-123\",\"priority\":1}`). Only flat string/number values are kept — nested objects, arrays, booleans and null are dropped.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search completed. `results` contains at most one company, or is empty when nothing matched.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompanySearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input — typically no filter provided, or an empty filter value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "at least one of name, domain, linkedin_id, slug is required",
                  "error": "Bad Request",
                  "statusCode": 400
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — max 5 requests per minute",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "ThrottlerException: Too Many Requests",
                  "error": "Too Many Requests",
                  "statusCode": 429
                }
              }
            }
          }
        }
      }
    },
    "/trigger/crm-sync": {
      "post": {
        "description": "Push contacts and/or companies directly into your connected CRM. LoneScale creates or updates the records asynchronously, applying the team-level integration field mappings already configured in LoneScale (create/update fields and unicity criteria). No workflow is involved.\n\nRecord keys must match the LoneScale field keys referenced by your configured mappings (for example `lonescale_first_name`, `lonescale_company_domain`, or any custom key you mapped); see [Available Fields](/api-reference/crm-sync-fields) for the full list. For Salesforce the mapping also decides the object: map only the Lead section and the sync searches, updates or creates Leads instead of Contacts. Set `account_crm_id` on a record to target an existing CRM company by id instead of matching by unicity criteria.\n\nPoll `GET /trigger/crm-sync/{id}` with the returned `id`, or pass a `webhook_url` to be called once with the run summary when every record has been processed.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrmSyncRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "CRM sync run accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CrmSyncRun"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "409": {
            "description": "Ambiguous configuration: several CRMs (or several settings records) match; pass `crm` and/or `settings_type`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Multiple CRMs have mapping settings (hubspot, salesforce); pass \"crm\" to pick one",
                  "error": "Conflict",
                  "statusCode": 409
                }
              }
            }
          },
          "422": {
            "description": "No mapping settings configured for the CRM, or empty payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "No hubspot integration mapping settings found; configure the hubspot integration in LoneScale first",
                  "error": "Unprocessable Entity",
                  "statusCode": 422
                }
              }
            }
          }
        }
      }
    },
    "/trigger/crm-sync/{id}": {
      "get": {
        "description": "Get the status and progress counters of a CRM sync run.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The run id returned by POST /trigger/crm-sync"
          }
        ],
        "responses": {
          "200": {
            "description": "Run status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CrmSyncRunStatus"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "404": {
            "description": "Run not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Run not found",
                  "error": "Not Found",
                  "statusCode": 404
                }
              }
            }
          }
        }
      }
    },
    "/trigger/crm-sync/{id}/records": {
      "get": {
        "description": "Delivery log of a CRM sync run: one entry per record with its final status (`synced` / `failed` / `pending`), the CRM ids created or updated, the error message when it failed and the number of attempts. Records not consumed yet (run still processing) are reported as `pending`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The run id returned by POST /trigger/crm-sync"
          }
        ],
        "responses": {
          "200": {
            "description": "Per-record delivery log",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CrmSyncRunRecords"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "404": {
            "description": "Run not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Run not found",
                  "error": "Not Found",
                  "statusCode": 404
                }
              }
            }
          }
        }
      }
    },
    "/crm/search": {
      "post": {
        "description": "Look up a contact in your connected CRM(s) and surface the record owner. Provide `email`, `linkedin_url`, `first_name` + `last_name` (matched on your CRM unicity criteria only), or a name part with `company_domain` / `company_name` (name + company match, lower confidence). A name-only miss carries a `warnings` entry. Only connected CRMs are queried. Pipedrive and Sellsy search is email-only, so a linkedin-only lookup returns HubSpot/Salesforce matches only. Each match carries `owned_by_you`, and `caller_crm_links` tells which CRM user the caller is linked to, so a client can say \"already yours\" vs \"owned by X\" and warn when a create would go to the default owner.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrmSearchRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "CRM matches with owner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CrmSearchResult"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "422": {
            "description": "No usable identifier: provide email, linkedin_url, first_name + last_name, or a name part with company_domain or company_name",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "email or linkedin_url is required",
                  "error": "Unprocessable Entity",
                  "statusCode": 422
                }
              }
            }
          }
        }
      }
    },
    "/crm/mapping": {
      "get": {
        "description": "Preview which CRM fields a `POST /trigger/crm-sync` will write: the team's resolved contact/company create & update field mappings, the unicity criteria used for matching, and whether creation is enabled. Read-only.",
        "parameters": [
          {
            "name": "crm",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "hubspot",
                "salesforce",
                "pipedrive",
                "sellsy"
              ]
            },
            "description": "Target CRM. Optional when only one CRM has mapping settings."
          }
        ],
        "responses": {
          "200": {
            "description": "Resolved mapping preview",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "crm": {
                      "type": "string"
                    },
                    "settings_type": {
                      "type": "string"
                    },
                    "object": {
                      "type": "string",
                      "enum": [
                        "contact",
                        "lead"
                      ],
                      "description": "Which Salesforce object a sync will write, decided by the mapping itself: `lead` when only the Lead section is mapped."
                    },
                    "contact": {
                      "type": "object",
                      "properties": {
                        "create_enabled": {
                          "type": "boolean"
                        },
                        "create_fields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "from": {
                                "type": "string"
                              },
                              "to": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          }
                        },
                        "update_fields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "from": {
                                "type": "string"
                              },
                              "to": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          }
                        },
                        "unicity": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "lead": {
                      "type": "object",
                      "description": "Only when `object` is `lead`: what a push will search, update and create.",
                      "properties": {
                        "create_enabled": {
                          "type": "boolean"
                        },
                        "create_fields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "from": {
                                "type": "string"
                              },
                              "to": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          }
                        },
                        "update_fields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "from": {
                                "type": "string"
                              },
                              "to": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          }
                        },
                        "unicity": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "company": {
                      "type": "object",
                      "properties": {
                        "create_enabled": {
                          "type": "boolean"
                        },
                        "create_fields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "from": {
                                "type": "string"
                              },
                              "to": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          }
                        },
                        "update_fields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "from": {
                                "type": "string"
                              },
                              "to": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Several CRMs have mapping settings; pass `crm`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No CRM mapping settings configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "description": "List the platform webhooks configured for your team, so you can pass a `webhook_id` destination to `POST /trigger/crm-sync` (push records without a connected CRM). Deduped by URL.",
        "responses": {
          "200": {
            "description": "Configured webhooks",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookList"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          }
        }
      }
    },
    "/todays-accounts": {
      "get": {
        "description": "The accounts of a CRM book worth working today, best first (10 at most). An account gets in with a LoneScale signal in the last 30 days on its domain (job change, new hire, contact sourced, hiring burst; job postings over 14 days) and no CRM activity in the last 7 days. Each account comes with the signal, the person to reach (with the email and phone already known), its owner, the last touch and a score with the lines it adds up from (`score_parts`).\n\nThis returns the last list computed for the book. `status: none` means it was never ranked: call `POST /todays-accounts/refresh`, then poll this endpoint while `status` is `pending`.\n\nRequires Today's accounts to be enabled for the API key's holder. Rate limited to **30 requests per minute**.",
        "parameters": [
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Which CRM book to rank: omit it for the API key holder's own accounts (their linked CRM owner), `all` for every account with a domain, `unassigned` for accounts nobody owns (HubSpot only), or a CRM owner id from `GET /todays-accounts/owners`."
          }
        ],
        "responses": {
          "200": {
            "description": "The book's list, whatever its status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TodaysAccountsList"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "403": {
            "description": "Today's accounts is not enabled for the API key's holder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Today's accounts is not enabled for this user: ask LoneScale to turn it on.",
                  "error": "Forbidden",
                  "statusCode": 403
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded: 30 requests per minute, counted per API key"
          }
        }
      }
    },
    "/todays-accounts/refresh": {
      "post": {
        "description": "Ranks the book again. The ranking runs in the background (a few seconds to a minute): the answer has `status: pending`; poll `GET /todays-accounts` with the same `scope` until it is `ended`. A refresh while one is running returns the running one.\n\nRate limited to **10 requests per minute**.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scope": {
                    "type": "string",
                    "description": "Which CRM book to rank: omit it for the API key holder's own accounts (their linked CRM owner), `all` for every account with a domain, `unassigned` for accounts nobody owns (HubSpot only), or a CRM owner id from `GET /todays-accounts/owners`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The book's list, whatever its status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TodaysAccountsList"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "403": {
            "description": "Today's accounts is not enabled for the API key's holder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Today's accounts is not enabled for this user: ask LoneScale to turn it on.",
                  "error": "Forbidden",
                  "statusCode": 403
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded: 10 requests per minute, counted per API key"
          }
        }
      }
    },
    "/todays-accounts/owners": {
      "get": {
        "description": "Searches the CRM owners (sales reps) of the connected CRM by name or email, 20 at most. Use an `id` as `owner_id` to assign an account, or as `scope` to rank that rep's book. Salesforce needs at least 2 letters.",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "A name or email fragment."
          }
        ],
        "responses": {
          "200": {
            "description": "Matching CRM owners",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "owners": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TodaysAccountsOwner"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "403": {
            "description": "Today's accounts is not enabled for the API key's holder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Today's accounts is not enabled for this user: ask LoneScale to turn it on.",
                  "error": "Forbidden",
                  "statusCode": 403
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded: 30 requests per minute, counted per API key"
          }
        }
      }
    },
    "/todays-accounts/assign": {
      "post": {
        "description": "Makes a sales rep the CRM owner of one of the listed accounts (the HubSpot company or the Salesforce Account), and of its highlighted contact too with `with_contact: true`. Writes to your CRM. Returns the list with the account's new owner.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "domain": {
                    "type": "string",
                    "description": "The account's domain, as listed in `accounts[].domain`.",
                    "example": "pennylane.com"
                  },
                  "owner_id": {
                    "type": "string",
                    "description": "A CRM owner id, from `GET /todays-accounts/owners`."
                  },
                  "with_contact": {
                    "type": "boolean",
                    "default": false,
                    "description": "Also make them the owner of the account's highlighted CRM contact."
                  },
                  "scope": {
                    "type": "string",
                    "description": "Which CRM book to rank: omit it for the API key holder's own accounts (their linked CRM owner), `all` for every account with a domain, `unassigned` for accounts nobody owns (HubSpot only), or a CRM owner id from `GET /todays-accounts/owners`."
                  }
                },
                "required": [
                  "domain",
                  "owner_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The book's list, whatever its status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TodaysAccountsList"
                }
              }
            }
          },
          "400": {
            "description": "The CRM refused the change (e.g. the account was assigned but not the contact)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "The account was assigned, the contact was not: insufficient access rights",
                  "error": "Bad Request",
                  "statusCode": 400
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "403": {
            "description": "Today's accounts is not enabled for the API key's holder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Today's accounts is not enabled for this user: ask LoneScale to turn it on.",
                  "error": "Forbidden",
                  "statusCode": 403
                }
              }
            }
          },
          "404": {
            "description": "The account is not in this book's current list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Account not in this list",
                  "error": "Not Found",
                  "statusCode": 404
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded: 30 requests per minute, counted per API key"
          }
        }
      }
    },
    "/todays-accounts/dismiss": {
      "post": {
        "description": "Skips one of the listed accounts for the whole organization. The CRM is not touched. `reason` says what happens next: `BAD_TIMING` (default) and `OTHER` hide it until a newer signal arrives on it; `IN_PROGRESS` hides it for 90 days whatever comes; `NOT_ICP` hides it and, once two accounts of the same industry were dismissed this way, takes 2 points off that industry's accounts in the ranking; `WRONG_CONTACT` keeps the account, never proposes that person on it again and swaps in the next contact.\n\nRate limited to **30 requests per minute**.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "domain": {
                    "type": "string",
                    "description": "The account's domain, as listed in `accounts[].domain`.",
                    "example": "pennylane.com"
                  },
                  "scope": {
                    "type": "string",
                    "description": "Which CRM book to rank: omit it for the API key holder's own accounts (their linked CRM owner), `all` for every account with a domain, `unassigned` for accounts nobody owns (HubSpot only), or a CRM owner id from `GET /todays-accounts/owners`."
                  },
                  "reason": {
                    "type": "string",
                    "enum": [
                      "BAD_TIMING",
                      "IN_PROGRESS",
                      "NOT_ICP",
                      "WRONG_CONTACT",
                      "OTHER"
                    ],
                    "description": "Why the account is skipped; `BAD_TIMING` when omitted.",
                    "example": "IN_PROGRESS"
                  }
                },
                "required": [
                  "domain"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The book's list, whatever its status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TodaysAccountsList"
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "403": {
            "description": "Today's accounts is not enabled for the API key's holder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Today's accounts is not enabled for this user: ask LoneScale to turn it on.",
                  "error": "Forbidden",
                  "statusCode": 403
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded: 30 requests per minute, counted per API key"
          }
        }
      }
    },
    "/todays-accounts/enrich": {
      "post": {
        "description": "Finds the missing email and phone of one person of a listed account (`person`: the `crm_id` or `linkedin_url` of one of `people[]`; the first when omitted), right away (it can take up to a minute). Runs the same waterfall as the enrichment endpoints and consumes credits **only when something is found**. When the person is a contact of your CRM, the email and phone are written to it; nothing is created otherwise. Returns the list with the person's email and phone.\n\nRate limited to **10 requests per minute**.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "domain": {
                    "type": "string",
                    "description": "The account's domain, as listed in `accounts[].domain`.",
                    "example": "pennylane.com"
                  },
                  "scope": {
                    "type": "string",
                    "description": "Which CRM book to rank: omit it for the API key holder's own accounts (their linked CRM owner), `all` for every account with a domain, `unassigned` for accounts nobody owns (HubSpot only), or a CRM owner id from `GET /todays-accounts/owners`."
                  },
                  "person": {
                    "type": "string",
                    "description": "`crm_id` or `linkedin_url` of one of the account's `people[]`. The first (`person`) when omitted.",
                    "example": "https://www.linkedin.com/in/jen-doe"
                  }
                },
                "required": [
                  "domain"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The book's list, whatever its status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TodaysAccountsList"
                }
              }
            }
          },
          "400": {
            "description": "The person has no LinkedIn profile nor CRM contact to enrich from",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "No LinkedIn profile or CRM contact",
                  "error": "Bad Request",
                  "statusCode": 400
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "402": {
            "description": "No credits left",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Insufficient credits",
                  "error": "Payment Required",
                  "statusCode": 402
                }
              }
            }
          },
          "403": {
            "description": "Today's accounts is not enabled for the API key's holder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Today's accounts is not enabled for this user: ask LoneScale to turn it on.",
                  "error": "Forbidden",
                  "statusCode": 403
                }
              }
            }
          },
          "404": {
            "description": "The account is not in this book's current list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Account not in this list",
                  "error": "Not Found",
                  "statusCode": 404
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded: 10 requests per minute, counted per API key"
          }
        }
      }
    },
    "/todays-accounts/find-people": {
      "post": {
        "description": "Finds up to 3 people matching your organization's persona at one of the listed accounts, right away (it can take up to a minute), and adds them to its `people[]`. Runs the same contact sourcing as `POST /trigger/contact-sourcing/sync` and consumes **one contact credit per person found**. Found people are kept as LoneScale contacts: later lists show them at no cost. Returns the list with the account's `people[]` updated.\n\nRate limited to **5 requests per minute**.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "domain": {
                    "type": "string",
                    "description": "The account's domain, as listed in `accounts[].domain`.",
                    "example": "pennylane.com"
                  },
                  "scope": {
                    "type": "string",
                    "description": "Which CRM book to rank: omit it for the API key holder's own accounts (their linked CRM owner), `all` for every account with a domain, `unassigned` for accounts nobody owns (HubSpot only), or a CRM owner id from `GET /todays-accounts/owners`."
                  }
                },
                "required": [
                  "domain"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The book's list, whatever its status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TodaysAccountsList"
                }
              }
            }
          },
          "400": {
            "description": "No persona on the organization yet",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "No persona on the organization yet: set the job titles you sell to in Settings → Persona.",
                  "error": "Bad Request",
                  "statusCode": 400
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing authentication credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthError"
                },
                "example": {
                  "message": "Invalid API Key",
                  "error": "Unauthorized",
                  "statusCode": 401
                }
              }
            }
          },
          "402": {
            "description": "No contact credits left",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Insufficient contact credits",
                  "error": "Payment Required",
                  "statusCode": 402
                }
              }
            }
          },
          "403": {
            "description": "Today's accounts is not enabled for the API key's holder",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Today's accounts is not enabled for this user: ask LoneScale to turn it on.",
                  "error": "Forbidden",
                  "statusCode": 403
                }
              }
            }
          },
          "404": {
            "description": "The account is not in this book's current list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "message": "Account not in this list",
                  "error": "Not Found",
                  "statusCode": 404
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded: 10 requests per minute, counted per API key"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "EnrichmentHistory": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EnrichmentHistoryEntry"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Pass as `cursor` for the next page. `null` on the last page."
          }
        }
      },
      "EnrichmentHistoryEntry": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date-time",
            "description": "When the contact was last enriched. Not refreshed by a hit on the 24h enrichment cache."
          },
          "first_enriched_at": {
            "type": "string",
            "format": "date-time",
            "description": "The first time this contact was enriched on your account."
          },
          "fields_found": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "email",
                "phone",
                "profile"
              ]
            },
            "description": "Which of the three enrichment types produced a result."
          },
          "identifier": {
            "type": "object",
            "description": "The identifiers we hold for this contact, to reconcile against your own records.",
            "properties": {
              "linkedin_url": {
                "type": "string"
              },
              "email": {
                "type": "string"
              },
              "crm_id": {
                "type": "string"
              }
            }
          },
          "data": {
            "$ref": "#/components/schemas/EnrichmentHistoryContact",
            "description": "Only present when `include_data=true`."
          }
        }
      },
      "EnrichmentHistoryContact": {
        "type": "object",
        "properties": {
          "firstname": {
            "type": "string"
          },
          "lastname": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "email_status": {
            "type": "string"
          },
          "emails": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "email": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                }
              }
            },
            "description": "Secondary addresses, excluding the one in `email`."
          },
          "phone": {
            "type": "string"
          },
          "phones": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "number": {
                  "type": "string"
                },
                "region": {
                  "type": "string"
                }
              }
            },
            "description": "Secondary numbers, excluding the one in `phone`."
          },
          "linkedin_url": {
            "type": "string"
          },
          "job_title": {
            "type": "string"
          },
          "company_name": {
            "type": "string"
          },
          "company_domain": {
            "type": "string"
          }
        }
      },
      "User": {
        "required": [
          "id",
          "firstName",
          "lastName",
          "email"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The user ID",
            "type": "string"
          },
          "firstName": {
            "description": "User first name",
            "type": "string"
          },
          "lastName": {
            "description": "User last name",
            "type": "string"
          },
          "email": {
            "description": "User email address",
            "type": "string"
          },
          "crm_links": { "$ref": "#/components/schemas/CrmLinks" },
          "chrome_credits": {
            "type": "object",
            "description": "Per-user credit quota applied to Chrome extension and Slack bot enrichments, set by the admin in Settings > Members. Distinct from the organization balance in `plan`. `limit` is null when the user has no cap.",
            "properties": {
              "limit": { "type": "integer", "nullable": true, "description": "Maximum credits this user may consume; null = no cap" },
              "used": { "type": "integer", "description": "Credits consumed so far" }
            }
          },
          "plan": {
            "type": "object",
            "properties": {
              "contactCredits": {
                "description": "Remaining contact credits",
                "type": "number"
              },
              "enrichmentCredits": {
                "description": "Remaining enrichment credits",
                "type": "number"
              }
            }
          }
        }
      },
      "EnrichmentRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "enrichment_type",
          "contacts",
          "webhook_url"
        ],
        "properties": {
          "enrichment_type": {
            "type": "array",
            "description": "Types of enrichment to perform.",
            "items": {
              "type": "string",
              "enum": [
                "email",
                "phone",
                "profile"
              ]
            },
            "minItems": 1,
            "uniqueItems": true
          },
          "custom": {
            "$ref": "#/components/schemas/EnrichmentCustomOptions"
          },
          "contacts": {
            "type": "array",
            "description": "Contacts to enrich.",
            "items": {
              "$ref": "#/components/schemas/Contact"
            },
            "minItems": 1
          },
          "webhook_url": {
            "type": "string",
            "format": "uri",
            "description": "Webhook endpoint to receive enrichment results."
          },
          "detect_job_change": {
            "type": "boolean",
            "description": "When `true`, the webhook response will include a `jobChangeDetected` field indicating whether the contact has changed company since the input data was provided.\n\nOnly effective when `enrichment_type` includes `\"profile\"` and `webhook_url` is provided."
          },
          "force_refresh": {
            "type": "boolean",
            "description": "Opt out of the 24h enrichment cache for this call. When a contact was already enriched for your account within the last 24 hours, the cached result is returned and no credits are consumed; pass `true` to force a fresh enrichment instead (credits are consumed and the cache entry is refreshed)."
          },
          "sync_to_crm": {
            "type": "boolean",
            "description": "Push the enriched contact into your connected CRM. Disabled by default: without it, this endpoint only enriches and never writes to your CRM.\n\nWhen `true`, the write-back uses the integration mapping configured for the **Chrome extension** (contact unicity criteria, create fields, company create fields). It can therefore **create** contacts and companies that did not exist, and the created records are owned by the CRM user linked to the API key holder.\n\nTo push records to your CRM with a dedicated mapping and a run status, use `POST /trigger/crm-sync` instead."
          },
          "source": {
            "type": "string",
            "enum": ["slack"],
            "description": "Reserved for LoneScale-hosted callers (the Slack bot). Meters the enrichment on the API key holder's per-user Chrome quota instead of the organization balance, and answers 403 when that quota is reached. Requires `source_signature`; leave both unset for integrations."
          },
          "source_signature": {
            "type": "string",
            "description": "Signature proving `source` comes from LoneScale. Set by the LoneScale Slack agent; integrations never send it."
          }
        },
        "example": {
          "enrichment_type": [
            "email",
            "phone"
          ],
          "contacts": [
            {
              "firstname": "John",
              "lastname": "Doe",
              "email": "john@acme.com",
              "job_title": "ceo",
              "linkedin_url": "https://www.linkedin.com/in/john-doe/",
              "domain": "acme.com",
              "company_name": "Acmeo",
              "custom": {
                "contact_id": "44479301"
              }
            }
          ],
          "webhook_url": "https://api.example.com/webhooks/enrichment",
          "custom": {
            "provider": "lonescale",
            "enrichment_type": "email and phone"
          }
        }
      },
      "EnrichmentSyncRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "enrichment_type",
          "contacts"
        ],
        "properties": {
          "enrichment_type": {
            "type": "array",
            "description": "Types of enrichment to perform.",
            "items": {
              "type": "string",
              "enum": [
                "email",
                "phone",
                "profile"
              ]
            },
            "minItems": 1,
            "uniqueItems": true
          },
          "custom": {
            "$ref": "#/components/schemas/EnrichmentCustomOptions"
          },
          "contacts": {
            "type": "array",
            "description": "Contacts to enrich. Maximum 10 contacts per request.",
            "items": {
              "$ref": "#/components/schemas/Contact"
            },
            "minItems": 1,
            "maxItems": 10
          },
          "detect_job_change": {
            "type": "boolean",
            "description": "When `true`, each contact in the response will include a `jobChangeDetected` field indicating whether the contact has changed company since the input data was provided.\n\nOnly effective when `enrichment_type` includes `\"profile\"`."
          },
          "force_refresh": {
            "type": "boolean",
            "description": "Opt out of the 24h enrichment cache for this call. When a contact was already enriched for your account within the last 24 hours, the cached result is returned and no credits are consumed; pass `true` to force a fresh enrichment instead (credits are consumed and the cache entry is refreshed)."
          },
          "sync_to_crm": {
            "type": "boolean",
            "description": "Push the enriched contact into your connected CRM. Disabled by default: without it, this endpoint only enriches and never writes to your CRM.\n\nWhen `true`, the write-back uses the integration mapping configured for the **Chrome extension** (contact unicity criteria, create fields, company create fields). It can therefore **create** contacts and companies that did not exist, and the created records are owned by the CRM user linked to the API key holder.\n\nTo push records to your CRM with a dedicated mapping and a run status, use `POST /trigger/crm-sync` instead."
          },
          "source": {
            "type": "string",
            "enum": ["slack"],
            "description": "Reserved for LoneScale-hosted callers (the Slack bot). Meters the enrichment on the API key holder's per-user Chrome quota instead of the organization balance, and answers 403 when that quota is reached. Requires `source_signature`; leave both unset for integrations."
          },
          "source_signature": {
            "type": "string",
            "description": "Signature proving `source` comes from LoneScale. Set by the LoneScale Slack agent; integrations never send it."
          }
        },
        "example": {
          "enrichment_type": [
            "email",
            "phone"
          ],
          "contacts": [
            {
              "firstname": "John",
              "lastname": "Doe",
              "email": "john@acme.com",
              "job_title": "ceo",
              "linkedin_url": "https://www.linkedin.com/in/john-doe/",
              "domain": "acme.com",
              "company_name": "Acme",
              "custom": {
                "contact_id": "44479301"
              }
            }
          ],
          "custom": {
            "provider": "lonescale",
            "enrichment_type": "email and phone"
          }
        }
      },
      "SyncEnrichmentResponse": {
        "type": "object",
        "properties": {
          "contacts": {
            "type": "array",
            "description": "List of enriched contacts.",
            "items": {
              "$ref": "#/components/schemas/EnrichPayload"
            }
          }
        },
        "example": {
          "contacts": [
            {
              "firstName": "John",
              "lastName": "Doe",
              "fullName": "John Doe",
              "linkedinUrl": "https://www.linkedin.com/in/john-doe/",
              "jobTitle": "CEO",
              "companyName": "Acme",
              "companyDomain": "acme.com",
              "email": "john@acme.com",
              "emailStatus": "valid",
              "phone": "+14155552671",
              "phoneType": "mobile"
            }
          ]
        }
      },
      "ContactSourcingSyncRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "personas"
        ],
        "properties": {
          "company_domain": {
            "type": "string",
            "description": "Company domain or website to search contacts from."
          },
          "company_name": {
            "type": "string",
            "description": "Company name to search contacts from."
          },
          "company_linkedin_url": {
            "type": "string",
            "description": "Company LinkedIn URL used to search contacts, increasing result coverage and accuracy by 25%."
          },
          "personas": {
            "type": "array",
            "description": "Personas to use to search and filter contacts within the company.",
            "items": {
              "$ref": "#/components/schemas/Persona"
            },
            "minItems": 1
          },
          "included_locations": {
            "type": "array",
            "description": "List of country codes (ISO 3166-1 alpha-2) to include in the search.",
            "items": {
              "$ref": "#/components/schemas/CountryCode"
            }
          },
          "included_cities": {
            "type": "array",
            "description": "List of city names to filter contacts by. Unlike `included_locations`, cities are not used as a search filter: contacts are sourced first, then only those whose location contains one of these cities are returned. City spelling is revalidated against `included_locations`, so both local and English spellings work (`Bruxelles` / `Brussels`, `Zürich` / `Zurich`), and combining `included_cities: [\"Paris\"]` with `included_locations: [\"FR\"]` excludes Paris, Texas. A contact whose city cannot be determined is not returned.",
            "items": {
              "type": "string"
            },
            "example": ["Paris", "Lyon"]
          },
          "limit": {
            "type": "number",
            "description": "Maximum number of contacts to retrieve. Capped at 10.",
            "maximum": 10
          },
          "seniority_levels": {
            "type": "array",
            "description": "List of seniority levels to filter contacts by.",
            "items": {
              "$ref": "#/components/schemas/SeniorityCategory"
            }
          },
          "disable_company_info": {
            "type": "boolean",
            "description": "Disable enrichment of company information for each contact, such as industry, size, etc."
          },
          "custom": {
            "$ref": "#/components/schemas/EnrichmentCustomOptions"
          }
        },
        "example": {
          "company_name": "Acme Corp",
          "company_domain": "acme.com",
          "company_linkedin_url": "https://www.linkedin.com/company/acme",
          "personas": [
            {
              "name": "Tech Leaders",
              "job_titles": [
                "CTO",
                "VP Engineering"
              ]
            }
          ],
          "limit": 5,
          "custom": {
            "id": "12356",
            "provider": "lonescale"
          }
        }
      },
      "ContactSourcingSyncResponse": {
        "type": "object",
        "properties": {
          "contacts": {
            "type": "array",
            "description": "List of sourced contacts.",
            "items": {
              "type": "object",
              "properties": {
                "lonescale_full_name": {
                  "type": "string"
                },
                "lonescale_first_name": {
                  "type": "string"
                },
                "lonescale_last_name": {
                  "type": "string"
                },
                "lonescale_linkedin_url": {
                  "type": "string"
                },
                "lonescale_job_title": {
                  "type": "string"
                },
                "lonescale_job_seniority": {
                  "type": "string"
                },
                "lonescale_department": {
                  "type": "string"
                },
                "lonescale_country_code": {
                  "type": "string"
                },
                "lonescale_geo_country_name": {
                  "type": "string"
                },
                "lonescale_geo_location_name": {
                  "type": "string"
                },
                "lonescale_city": {
                  "type": "string"
                },
                "lonescale_company_name": {
                  "type": "string"
                },
                "lonescale_company_domain": {
                  "type": "string"
                },
                "lonescale_company_linkedin_url": {
                  "type": "string"
                },
                "lonescale_company_linkedin_id": {
                  "type": "string"
                },
                "lonescale_universal_name": {
                  "type": "string"
                },
                "lonescale_company_industry": {
                  "type": "string"
                },
                "lonescale_company_country": {
                  "type": "string"
                },
                "lonescale_company_country_code": {
                  "type": "string"
                },
                "lonescale_company_staff_count": {
                  "type": "integer"
                },
                "lonescale_company_employee_range": {
                  "type": "string"
                },
                "lonescale_company_city": {
                  "type": "string"
                },
                "client_company_name": {
                  "type": "string"
                },
                "client_company_domain": {
                  "type": "string"
                },
                "client_company_linkedin_url": {
                  "type": "string"
                },
                "custom": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Custom fields passed in the request."
                }
              }
            }
          },
          "profiles_found": {
            "type": "integer",
            "description": "Total number of contacts found."
          },
          "partial": {
            "type": "boolean",
            "description": "`true` when the time budget ran out before `limit` contacts were found. The contacts returned are valid; the last personas were not fully searched."
          },
          "elapsed_ms": {
            "type": "integer",
            "description": "Time spent searching, in milliseconds."
          }
        },
        "example": {
          "contacts": [
            {
              "lonescale_full_name": "Jane Doe",
              "lonescale_first_name": "Jane",
              "lonescale_last_name": "Doe",
              "lonescale_linkedin_url": "https://www.linkedin.com/in/janedoe",
              "lonescale_job_title": "CTO",
              "lonescale_job_seniority": "C_SUITE",
              "lonescale_country_code": "US",
              "lonescale_geo_country_name": "United States",
              "lonescale_company_name": "Acme Corp",
              "lonescale_company_domain": "acme.com",
              "lonescale_company_industry": "Software",
              "lonescale_company_staff_count": 250,
              "client_company_name": "Acme Corp",
              "client_company_domain": "acme.com"
            }
          ],
          "profiles_found": 1,
          "partial": false,
          "elapsed_ms": 18342
        }
      },
      "ContactSourcingRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "company_domain",
          "personas",
          "webhook_url"
        ],
        "properties": {
          "company_domain": {
            "type": "string",
            "description": "Company domain or website to search contacts from."
          },
          "company_name": {
            "type": "string",
            "description": "Company name to search contacts from."
          },
          "company_linkedin_url": {
            "type": "string",
            "description": "Company LinkedIn URL used to search contacts, increasing result coverage and accuracy by 25%."
          },
          "personas": {
            "type": "array",
            "description": "Personas to use to search and filter contacts within the company",
            "items": {
              "$ref": "#/components/schemas/Persona"
            },
            "minItems": 1
          },
          "included_locations": {
            "type": "array",
            "description": "List of country codes (ISO 3166-1 alpha-2) to include in the search.",
            "items": {
              "$ref": "#/components/schemas/CountryCode"
            }
          },
          "included_cities": {
            "type": "array",
            "description": "List of city names to filter contacts by. Unlike `included_locations`, cities are not used as a search filter: contacts are sourced first, then only those whose location contains one of these cities are returned. City spelling is revalidated against `included_locations`, so both local and English spellings work (`Bruxelles` / `Brussels`, `Zürich` / `Zurich`), and combining `included_cities: [\"Paris\"]` with `included_locations: [\"FR\"]` excludes Paris, Texas. A contact whose city cannot be determined is not returned.",
            "items": {
              "type": "string"
            },
            "example": [
              "Paris",
              "Lyon"
            ]
          },
          "limit": {
            "type": "number",
            "description": "Defines the maximum number of contacts to retrieve"
          },
          "webhook_url": {
            "type": "string",
            "format": "uri",
            "description": "Webhook endpoint to receive results."
          },
          "disable_company_info": {
            "type": "boolean",
            "description": "Disable enrichment of company information for each contact, such as industry, size, etc. This option is useful if you only want to retrieve contact information without any company details."
          },
          "seniority_levels": {
            "type": "array",
            "description": "List of seniority levels to filter contacts by.",
            "items": {
              "$ref": "#/components/schemas/SeniorityCategory"
            }
          },
          "custom": {
            "$ref": "#/components/schemas/EnrichmentCustomOptions"
          }
        },
        "example": {
          "company_name": "Acme Corp",
          "company_domain": "acme.com",
          "company_linkedin_url": "https://www.linkedin.com/company/acme",
          "personas": [
            {
              "name": "Tech Leaders",
              "job_titles": [
                "CTO",
                "VP Engineering"
              ]
            }
          ],
          "limit": 10,
          "custom": {
            "id": "12356",
            "provider": "lonescale"
          },
          "webhook_url": "https://api.example.com/webhooks/enrichment"
        }
      },
      "EnrichmentCustomOptions": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique ID of the record from the CRM, database, or other source system."
          },
          "provider": {
            "type": "string",
            "description": "Downstream data provider to use (e.g., Lonescale)."
          },
          "enrichment_type": {
            "type": "string",
            "description": "Provider-specific enrichment mode.",
            "example": "phone"
          }
        }
      },
      "Contact": {
        "type": "object",
        "description": "A contact must carry at least one identifier: `linkedin_url`, or `firstname` + `lastname` + (`domain` or `company_name`), or `email` alone. Anything else cannot be resolved and comes back with `status: \"failed\"`.",
        "additionalProperties": false,
        "properties": {
          "firstname": {
            "type": "string"
          },
          "lastname": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Known email of the contact. Also usable as a standalone identifier: sent alone (with `enrichment_type` including `profile`), it resolves the person's identity — name, LinkedIn URL, job title and company.",
            "example": "john@acme.com"
          },
          "job_title": {
            "type": "string"
          },
          "linkedin_url": {
            "type": "string",
            "format": "uri"
          },
          "domain": {
            "type": "string",
            "description": "Company domain (e.g., acme.com)."
          },
          "company_name": {
            "type": "string"
          },
          "custom": {
            "$ref": "#/components/schemas/ContactCustom"
          }
        }
      },
      "SeniorityCategory": {
        "type": "string",
        "description": "Seniority level of a contact.",
        "enum": [
          "owner",
          "founder",
          "c-suite",
          "partner",
          "vp",
          "head",
          "director",
          "manager",
          "senior",
          "entry",
          "intern"
        ]
      },
      "CountryCode": {
        "type": "string",
        "description": "ISO 3166-1 alpha-2 country code",
        "isEnum": true,
        "enum": [
          "AF",
          "AL",
          "DZ",
          "AS",
          "AD",
          "AO",
          "AI",
          "AQ",
          "AG",
          "AR",
          "AM",
          "AW",
          "AU",
          "AT",
          "AZ",
          "BS",
          "BH",
          "BD",
          "BB",
          "BY",
          "BE",
          "BZ",
          "BJ",
          "BM",
          "BT",
          "BO",
          "BQ",
          "BA",
          "BW",
          "BV",
          "BR",
          "IO",
          "BN",
          "BG",
          "BF",
          "BI",
          "CV",
          "KH",
          "CM",
          "CA",
          "KY",
          "CF",
          "TD",
          "CL",
          "CN",
          "CX",
          "CC",
          "CO",
          "KM",
          "CD",
          "CG",
          "CK",
          "CR",
          "HR",
          "CU",
          "CW",
          "CY",
          "CZ",
          "CI",
          "DK",
          "DJ",
          "DM",
          "DO",
          "EC",
          "EG",
          "SV",
          "GQ",
          "ER",
          "EE",
          "SZ",
          "ET",
          "FK",
          "FO",
          "FJ",
          "FI",
          "FR",
          "GF",
          "PF",
          "TF",
          "GA",
          "GM",
          "GE",
          "DE",
          "GH",
          "GI",
          "GR",
          "GL",
          "GD",
          "GP",
          "GU",
          "GT",
          "GG",
          "GN",
          "GW",
          "GY",
          "HT",
          "HM",
          "HN",
          "HK",
          "HU",
          "IS",
          "IN",
          "ID",
          "IR",
          "IQ",
          "IE",
          "IM",
          "IL",
          "IT",
          "JM",
          "JP",
          "JE",
          "JO",
          "KZ",
          "KE",
          "KI",
          "KR",
          "KW",
          "KG",
          "LA",
          "LV",
          "LB",
          "LS",
          "LR",
          "LY",
          "LI",
          "LT",
          "LU",
          "MO",
          "MG",
          "MW",
          "MY",
          "MV",
          "ML",
          "MT",
          "MH",
          "MQ",
          "MR",
          "MU",
          "YT",
          "MX",
          "FM",
          "MD",
          "MC",
          "MN",
          "ME",
          "MS",
          "MA",
          "MZ",
          "MM",
          "NA",
          "NR",
          "NP",
          "NL",
          "NC",
          "NZ",
          "NI",
          "NE",
          "NG",
          "NU",
          "NF",
          "MP",
          "NO",
          "OM",
          "PK",
          "PW",
          "PS",
          "PA",
          "PG",
          "PY",
          "PE",
          "PH",
          "PN",
          "PL",
          "PT",
          "PR",
          "QA",
          "MK",
          "RO",
          "RU",
          "RW",
          "RE",
          "BL",
          "SH",
          "KN",
          "LC",
          "MF",
          "PM",
          "VC",
          "WS",
          "SM",
          "ST",
          "SA",
          "SN",
          "RS",
          "SC",
          "SL",
          "SG",
          "SX",
          "SK",
          "SI",
          "SB",
          "SO",
          "ZA",
          "GS",
          "SS",
          "ES",
          "LK",
          "SD",
          "SR",
          "SJ",
          "SE",
          "CH",
          "SY",
          "TW",
          "TJ",
          "TZ",
          "TH",
          "TL",
          "TG",
          "TK",
          "TO",
          "TT",
          "TN",
          "TR",
          "TM",
          "TC",
          "TV",
          "UG",
          "UA",
          "AE",
          "GB",
          "UM",
          "US",
          "UY",
          "UZ",
          "VU",
          "VE",
          "VN",
          "VG",
          "VI",
          "WF",
          "EH",
          "YE",
          "ZM",
          "ZW",
          "AX"
        ]
      },
      "Persona": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "job_titles",
          "name"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "job_titles": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "exclude_job_titles": {
            "type": "array",
            "description": "Job titles to leave out: a contact whose title contains one of them is not returned.",
            "items": {
              "type": "string"
            }
          },
          "excluded_job_titles": {
            "type": "array",
            "description": "Same as `exclude_job_titles` (both are accepted).",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ContactCustom": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "contact_id": {
            "type": "string",
            "description": "Optional CRM record id specific to this contact."
          }
        }
      },
      "SourcingPayload": {
        "type": "object",
        "properties": {
          "client_company_name": {
            "type": "string",
            "description": "The company name provided in the request."
          },
          "client_company_domain": {
            "type": "string",
            "description": "The company domain provided in the request."
          },
          "client_company_linkedin_url": {
            "type": "string",
            "description": "The company LinkedIn URL provided in the request."
          },
          "lonescale_company_name": {
            "type": "string",
            "description": "The enriched company name."
          },
          "lonescale_company_domain": {
            "type": "string",
            "description": "The enriched company domain."
          },
          "lonescale_company_linkedin_id": {
            "type": "string",
            "description": "The enriched company LinkedIn ID."
          },
          "lonescale_company_linkedin_url": {
            "type": "string",
            "description": "The enriched company LinkedIn URL."
          },
          "lonescale_universal_name": {
            "type": "string",
            "description": "The enriched company universal name."
          },
          "lonescale_company_description": {
            "type": "string",
            "description": "The enriched company description."
          },
          "lonescale_company_industry": {
            "type": "string",
            "description": "The enriched company industry."
          },
          "lonescale_company_country": {
            "type": "string",
            "description": "The enriched company country."
          },
          "lonescale_company_country_code": {
            "type": "string",
            "description": "The enriched company country code."
          },
          "lonescale_company_employee_range": {
            "type": "string",
            "description": "The enriched company employee range."
          },
          "lonescale_company_staff_count": {
            "type": "integer",
            "description": "The enriched company staff count."
          },
          "lonescale_company_city": {
            "type": "string",
            "description": "The enriched company city."
          },
          "lonescale_job_title": {
            "type": "string",
            "description": "The enriched job title."
          },
          "lonescale_country_code": {
            "type": "string",
            "description": "The enriched country code."
          },
          "lonescale_full_name": {
            "type": "string",
            "description": "The enriched full name."
          },
          "lonescale_first_name": {
            "type": "string",
            "description": "The enriched first name."
          },
          "lonescale_last_name": {
            "type": "string",
            "description": "The enriched last name."
          },
          "lonescale_geo_country_name": {
            "type": "string",
            "description": "The enriched geo country name."
          },
          "lonescale_geo_location_name": {
            "type": "string",
            "description": "The enriched geo location name."
          },
          "lonescale_city": {
            "type": "string",
            "description": "The enriched city."
          },
          "lonescale_linkedin_url": {
            "type": "string",
            "description": "The enriched LinkedIn URL."
          },
          "message": {
            "type": "string",
            "description": "Status message of the sourcing job, the job was successful."
          },
          "status": {
            "type": "string",
            "description": "Status of the sourcing job: `end` (contacts were sent), `not_found` (no contact matched) or `no_credits` (the plan has no contact credits left: nothing was searched or charged).",
            "enum": [
              "not_found",
              "end",
              "no_credits"
            ]
          },
          "profiles_found": {
            "type": "integer",
            "description": "Number of profiles found for the sourcing job."
          }
        }
      },
      "Sourcing": {
        "type": "string",
        "description": "Response indicating the sourcing job has been created.",
        "example": "Sourcing started for company Acme Corp"
      },
      "EnrichPayload": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string",
            "description": "The enriched first name."
          },
          "lastName": {
            "type": "string",
            "description": "The enriched last name."
          },
          "fullName": {
            "type": "string",
            "description": "The enriched full name."
          },
          "linkedinUrl": {
            "type": "string",
            "description": "The enriched LinkedIn URL."
          },
          "universalName": {
            "type": "string",
            "description": "The enriched universal name."
          },
          "companyDomain": {
            "type": "string",
            "description": "The enriched company domain."
          },
          "jobTitle": {
            "type": "string",
            "description": "The enriched job title."
          },
          "location": {
            "type": "string",
            "description": "Where the contact is based, as reported on their profile. Omitted when the provider has no location.",
            "example": "Madrid, Community of Madrid, Spain"
          },
          "companyDescription": {
            "type": "string",
            "description": "The enriched company description."
          },
          "companyEmployeeCount": {
            "type": "integer",
            "description": "The enriched company employee count."
          },
          "companyIndustry": {
            "type": "string",
            "description": "The enriched company industry."
          },
          "companyLinkedinId": {
            "type": "string",
            "description": "The enriched company LinkedIn ID."
          },
          "companyLinkedinUrl": {
            "type": "string",
            "description": "The enriched company LinkedIn URL."
          },
          "companyName": {
            "type": "string",
            "description": "The enriched company name."
          },
          "companyEmployeeRange": {
            "type": "string",
            "description": "The enriched company employee range."
          },
          "companyCountryCode": {
            "type": "string",
            "description": "The enriched company country code."
          },
          "companyCountry": {
            "type": "string",
            "description": "The enriched company country."
          },
          "companyCity": {
            "type": "string",
            "description": "The enriched company city."
          },
          "jobTitleSince": {
            "type": "object",
            "properties": {
              "month": {
                "type": "integer"
              },
              "year": {
                "type": "integer"
              }
            }
          },
          "companySince": {
            "type": "object",
            "properties": {
              "month": {
                "type": "integer"
              },
              "year": {
                "type": "integer"
              }
            }
          },
          "email": {
            "type": "string",
            "description": "The enriched email address. When no email is found via the provider waterfall, this falls back to a pattern-based predicted email (free)."
          },
          "emailStatus": {
            "type": "string",
            "description": "The enriched email status: `valid`, `catch_all`, `invalid`, `unknown`, or `predicted`. `predicted` means the email was derived from the company's email pattern as a free fallback and was not verified."
          },
          "predicted_email": {
            "type": "string",
            "description": "Pattern-based predicted email, set when the provider waterfall returned no email. Free — does not consume credits."
          },
          "email_pattern": {
            "type": "string",
            "description": "Detected company email format used to build the predicted email, e.g. `firstname.lastname@domain`."
          },
          "predicted_email_score": {
            "type": "string",
            "description": "Confidence of the detected email pattern (percentage when detected, `0` for the deterministic fallback)."
          },
          "phone": {
            "type": "string",
            "description": "The enriched phone number."
          },
          "phoneType": {
            "type": "string",
            "description": "The enriched phone type."
          },
          "phoneCountryCode": {
            "type": "string",
            "description": "The enriched phone country code."
          },
          "emails": {
            "type": "array",
            "description": "List of enriched email addresses.",
            "items": {
              "type": "object",
              "properties": {
                "email": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "description": "Email status: `valid`, `catch_all`, `invalid`, `unknown`, or `predicted` (free pattern-based fallback)."
                }
              }
            }
          },
          "phones": {
            "type": "array",
            "description": "List of enriched phone numbers.",
            "items": {
              "type": "object",
              "properties": {
                "number": {
                  "type": "string"
                },
                "region": {
                  "type": "string"
                }
              }
            }
          },
          "jobChangeDetected": {
            "type": "boolean",
            "description": "Indicates whether the contact has changed company since the input data was provided.\n\n- `true` — the enriched company (domain or name) differs from the one in the input contact\n- `false` — the company is the same, or there is not enough data to determine a change\n\nOnly present in the webhook payload when `detect_job_change: true` was set in the request. Detection first compares company domains using fuzzy matching, falling back to company name if domains are unavailable."
          },
          "cached": {
            "type": "boolean",
            "description": "Present and `true` when the result was served from the 24h enrichment cache. No credits were consumed for this contact. Pass `force_refresh: true` in the request to bypass the cache."
          },
          "cached_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the cached result was originally enriched. Only present when `cached` is `true`."
          }
        }
      },
      "VerifyPhoneRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "phone",
          "webhook_url",
          "firstname",
          "lastname"
        ],
        "properties": {
          "phone": {
            "type": "string",
            "description": "Phone number to verify, in E.164 format."
          },
          "webhook_url": {
            "type": "string",
            "format": "uri",
            "description": "Webhook endpoint to receive verification status for a phone number"
          },
          "firstname": {
            "type": "string",
            "description": "First name of the person to verify."
          },
          "lastname": {
            "type": "string",
            "description": "Last name of the person to verify."
          },
          "country": {
            "type": "string",
            "description": "Country code of the phone number to verify. Increase result coverage and accuracy by 25%."
          },
          "city": {
            "type": "string",
            "description": "City of the phone number to verify."
          },
          "state": {
            "type": "string",
            "description": "State code of the phone number to verify. Increase result coverage and accuracy by 25%."
          },
          "zipcode": {
            "type": "string",
            "description": "Zipcode of the phone number to verify. Increase result coverage and accuracy by 25%."
          },
          "address": {
            "type": "string",
            "description": "Address of the person to verify."
          }
        }
      },
      "PhoneVerification": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "The verification status of the phone number.",
            "enum": [
              "verified",
              "regular",
              "unknown"
            ]
          },
          "type": {
            "type": "string",
            "description": "The type of the phone number.",
            "enum": [
              "mobile",
              "landline",
              "unknown"
            ]
          }
        }
      },
      "Error": {
        "required": [
          "error",
          "message"
        ],
        "type": "object",
        "properties": {
          "error": {
            "type": "integer",
            "format": "int32"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "List": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier of the list."
          },
          "name": {
            "type": "string",
            "description": "Name of the list."
          },
          "entity": {
            "type": "string",
            "enum": [
              "COMPANY",
              "PEOPLE"
            ],
            "description": "The entity type of the list."
          },
          "description": {
            "type": "string",
            "description": "Description of the list."
          },
          "organizationId": {
            "type": "string",
            "description": "The organization this list belongs to."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Creation timestamp."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Last update timestamp."
          }
        }
      },
      "ListItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier of the item."
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "full_name": {
            "type": "string"
          },
          "linkedin_url": {
            "type": "string",
            "format": "uri"
          },
          "company_name": {
            "type": "string"
          },
          "current_position": {
            "type": "string"
          },
          "domain": {
            "type": "string",
            "description": "Company domain (e.g., acme.com)."
          },
          "location": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "contact_id": {
            "type": "string",
            "description": "Optional CRM record ID for this contact."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateListRequest": {
        "type": "object",
        "required": [
          "name",
          "entity"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Name of the list."
          },
          "entity": {
            "type": "string",
            "enum": [
              "COMPANY",
              "PEOPLE"
            ],
            "description": "Entity type the list will hold."
          }
        },
        "example": {
          "name": "My Target Accounts",
          "entity": "COMPANY"
        }
      },
      "AddItemRequest": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "full_name": {
            "type": "string"
          },
          "linkedin_url": {
            "type": "string",
            "format": "uri"
          },
          "company_name": {
            "type": "string"
          },
          "current_position": {
            "type": "string"
          },
          "domain": {
            "type": "string",
            "description": "Company domain (e.g., acme.com). Required for COMPANY lists (unless `name` + `location` are provided)."
          },
          "location": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "contact_id": {
            "type": "string",
            "description": "Optional CRM record ID for this contact."
          }
        }
      },
      "CompanySearchResponse": {
        "type": "object",
        "required": [
          "results",
          "count",
          "custom"
        ],
        "properties": {
          "results": {
            "type": "array",
            "description": "At most one matching company. Empty when nothing was found.",
            "items": {
              "$ref": "#/components/schemas/CompanySearchResult"
            }
          },
          "count": {
            "type": "integer",
            "description": "Number of items in `results` (0 or 1).",
            "example": 1
          },
          "custom": {
            "type": "object",
            "description": "The `custom` map from the request, echoed unchanged. `{}` when none was sent.",
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ]
            },
            "example": {
              "crm_id": "abc-123",
              "priority": "1"
            }
          }
        }
      },
      "CompanySearchResult": {
        "type": "object",
        "description": "Stable, fixed-shape company hit. Every field is always present — `null` (or `[]`) is used for any datum we do not have.",
        "required": [
          "linkedinId",
          "linkedinUniversalName",
          "linkedinUrl",
          "name",
          "domain",
          "websiteUrl",
          "logoUrl",
          "description",
          "linkedinTagline",
          "industry",
          "foundedYear",
          "staffCount",
          "staffCountRange",
          "countryCode",
          "city",
          "postalCode",
          "linkedinSpecialities",
          "linkedinHashtags",
          "confirmedLocations",
          "fundingData",
          "headcount"
        ],
        "properties": {
          "linkedinId": {
            "type": [
              "string",
              "null"
            ],
            "example": "2135371"
          },
          "linkedinUniversalName": {
            "type": [
              "string",
              "null"
            ],
            "example": "stripe"
          },
          "linkedinUrl": {
            "type": [
              "string",
              "null"
            ],
            "example": "https://www.linkedin.com/company/stripe"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "example": "Stripe"
          },
          "domain": {
            "type": [
              "string",
              "null"
            ],
            "example": "stripe.com"
          },
          "websiteUrl": {
            "type": [
              "string",
              "null"
            ],
            "example": "https://stripe.com"
          },
          "logoUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "linkedinTagline": {
            "type": [
              "string",
              "null"
            ],
            "example": "Help increase the GDP of the internet."
          },
          "industry": {
            "type": [
              "string",
              "null"
            ],
            "example": "Computer Software"
          },
          "foundedYear": {
            "type": [
              "integer",
              "null"
            ],
            "example": 2010
          },
          "staffCount": {
            "type": [
              "integer",
              "null"
            ],
            "example": 13809
          },
          "staffCountRange": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "start": {
                "type": "integer"
              },
              "end": {
                "type": "integer"
              }
            },
            "example": {
              "start": 5001,
              "end": 10000
            }
          },
          "countryCode": {
            "type": [
              "string",
              "null"
            ],
            "example": "US"
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "example": "South San Francisco"
          },
          "postalCode": {
            "type": [
              "string",
              "null"
            ],
            "example": "94080"
          },
          "linkedinSpecialities": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "linkedinHashtags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "confirmedLocations": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "country": {
                  "type": "string"
                },
                "geographicArea": {
                  "type": "string"
                },
                "city": {
                  "type": "string"
                },
                "postalCode": {
                  "type": "string"
                },
                "line1": {
                  "type": "string"
                },
                "headquarter": {
                  "type": "boolean"
                }
              }
            }
          },
          "fundingData": {
            "type": [
              "object",
              "null"
            ],
            "description": "Crunchbase-derived funding info when available."
          },
          "headcount": {
            "type": [
              "array",
              "null"
            ],
            "description": "Function-by-function employee breakdown. Only populated when `enrich=true` AND the result has both `linkedinId` and `linkedinUniversalName`.",
            "items": {
              "type": "object",
              "required": [
                "function",
                "count"
              ],
              "properties": {
                "function": {
                  "type": "string",
                  "example": "Engineering"
                },
                "count": {
                  "type": "integer",
                  "example": 5058
                }
              }
            }
          }
        }
      },
      "AuthError": {
        "required": [
          "message",
          "error",
          "statusCode"
        ],
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "example": "Invalid API Key"
          },
          "error": {
            "type": "string",
            "example": "Unauthorized"
          },
          "statusCode": {
            "type": "integer",
            "example": 401
          }
        }
      },
      "CrmSyncRecord": {
        "type": "object",
        "additionalProperties": true,
        "description": "Free-form record: keys must match the LoneScale field keys referenced by the configured mappings. See [Available Fields](/api-reference/crm-sync-fields) for the full list. The `lonescale_` / `main_` prefix is optional: `first_name`, `job_title`, `company_domain`, `email` and `phone` resolve like their prefixed form, and camelCase keys from enrich payloads (`firstName`, `jobTitle`, ...) are aliased too. A record whose keys match none of the mapped or unicity fields is failed, with the expected field keys in the error.",
        "properties": {
          "account_crm_id": {
            "type": "string",
            "description": "Optional CRM company id: targets that company directly instead of matching by unicity criteria."
          },
          "contact_crm_id": {
            "type": "string",
            "description": "Optional CRM contact id (e.g. the id returned by POST /crm/search): updates that exact contact instead of matching by unicity criteria."
          },
          "owner_id": {
            "type": "string",
            "description": "Salesforce only. Optional User id (005…) that owns the created or updated Lead or Contact. Without it the CRM keeps its default owner on create and the current owner on update; a mapping row on OwnerId takes precedence."
          }
        }
      },
      "CrmSyncRequest": {
        "type": "object",
        "properties": {
          "crm": {
            "type": "string",
            "enum": [
              "hubspot",
              "salesforce",
              "pipedrive",
              "sellsy"
            ],
            "description": "Target CRM. Required only when more than one connected CRM has mapping settings."
          },
          "settings_type": {
            "type": "string",
            "description": "Which team-level settings record to sync with when several exist for the CRM (settings are stored per use-case: api_sync, contact_sourcing, job_changes, ...). When omitted, resolution prefers the dedicated `api_sync` record (configured in the \"API Sync\" tab of the CRM integration), then falls back to `contact_sourcing`, then the only existing record."
          },
          "contacts": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/CrmSyncRecord"
            },
            "description": "Contacts to create/update (company fields may be embedded on each contact)."
          },
          "companies": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/CrmSyncRecord"
            },
            "description": "Companies to create/update, without contacts."
          },
          "webhook_url": {
            "type": "string",
            "description": "Called once with the run summary when every record has been processed."
          },
          "webhook_id": {
            "type": "string",
            "description": "Deliver every record to this platform webhook (id from `GET /webhooks`) instead of a CRM. No CRM connection or mapping is required. Mutually exclusive with `crm` / `settings_type`; distinct from `webhook_url` (the completion callback)."
          },
          "idempotency_key": {
            "type": "string",
            "description": "Reuse the same key to make client retries return the existing run instead of double-writing."
          },
          "trigger_by": {
            "type": "string"
          }
        }
      },
      "CrmSearchRequest": {
        "type": "object",
        "description": "Provide `email`, `linkedin_url`, or a name (`first_name`/`last_name`) together with `company_domain` or `company_name`. A name is never searched unconstrained.",
        "properties": {
          "email": {
            "type": "string",
            "description": "Email to look up in the connected CRM(s)."
          },
          "linkedin_url": {
            "type": "string",
            "description": "LinkedIn profile URL to look up. HubSpot/Salesforce only."
          },
          "first_name": {
            "type": "string",
            "description": "Used with `last_name` + a company when no email/LinkedIn is available. Matches are fuzzier and reported with a lower `confidence`."
          },
          "last_name": {
            "type": "string"
          },
          "company_domain": {
            "type": "string",
            "description": "Constrains a name search to that company. Preferred over `company_name` (more precise)."
          },
          "company_name": {
            "type": "string",
            "description": "Fallback company constraint when no domain is known. Yields `confidence: low`."
          },
          "crm": {
            "type": "string",
            "enum": [
              "hubspot",
              "salesforce",
              "pipedrive",
              "sellsy"
            ],
            "description": "Restrict the search to a single CRM instead of all connected ones."
          }
        }
      },
      "CrmContactMatch": {
        "type": "object",
        "properties": {
          "crm": {
            "type": "string",
            "enum": [
              "hubspot",
              "salesforce",
              "pipedrive",
              "sellsy"
            ]
          },
          "id": {
            "type": "string",
            "description": "CRM record id"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "profileUrl": {
            "type": "string",
            "nullable": true,
            "description": "LinkedIn URL stored on the record"
          },
          "url": {
            "type": "string",
            "nullable": true,
            "description": "Link to the CRM record"
          },
          "company": {
            "type": "string",
            "nullable": true
          },
          "companyDomain": {
            "type": "string",
            "nullable": true
          },
          "ownerId": {
            "type": "string",
            "nullable": true,
            "description": "CRM owner id"
          },
          "ownerName": {
            "type": "string",
            "nullable": true,
            "description": "CRM owner name (null for Sellsy)"
          },
          "matchedEmail": {
            "type": "string",
            "nullable": true,
            "description": "The address that actually matched. HubSpot's contact search also matches secondary addresses, so the record's primary `email` can differ from the one you searched."
          },
          "matchedOn": {
            "type": "string",
            "nullable": true,
            "enum": [
              "primary_email",
              "secondary_email",
              "linkedin",
              "unicity_criteria",
              "name_company_id",
              "name_company_name"
            ],
            "description": "Which identifier produced the match. `unicity_criteria` means the team's own CRM unicity rules matched (the same rule crm-sync applies)."
          },
          "confidence": {
            "type": "string",
            "enum": [
              "high",
              "medium",
              "low"
            ],
            "description": "How much to trust the match. Email / LinkedIn / unicity-criteria hits are `high`; a name constrained by a resolved company is `medium`; a name matched against a free-text company name is `low` (homonym risk) and should be verified before acting."
          },
          "owned_by_you": {
            "type": "boolean",
            "description": "True when the record's owner is the CRM user linked to the API key holder (see `caller_crm_links`). Salesforce ids are compared on their 15-char prefix."
          }
        }
      },
      "CrmLinks": {
        "type": "object",
        "description": "The CRM user linked to the API key holder (Settings > Members), per CRM. Records created by a CRM write-back (`sync_to_crm`, Chrome extension) are assigned to that user; `null` means not linked, so created records go to the connection's default owner. Pipedrive/Sellsy have no per-user link.",
        "properties": {
          "hubspot": {
            "type": "object",
            "nullable": true,
            "properties": {
              "ownerId": {
                "type": "string",
                "description": "HubSpot owner id"
              }
            }
          },
          "salesforce": {
            "type": "object",
            "nullable": true,
            "properties": {
              "ownerId": {
                "type": "string",
                "description": "Salesforce user id"
              }
            }
          }
        }
      },
      "CrmSearchResult": {
        "type": "object",
        "properties": {
          "matches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CrmContactMatch"
            }
          },
          "caller_crm_links": {
            "$ref": "#/components/schemas/CrmLinks"
          },
          "searched_crms": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The connected CRMs actually queried."
          },
          "default_crm": {
            "type": "string",
            "nullable": true,
            "description": "The CRM a `crm`-less sync would resolve to (same rules as POST /trigger/crm-sync). Null when ambiguous: ask the user which CRM to act on."
          },
          "errors": {
            "type": "array",
            "description": "Per-CRM lookup failures. Present only when a provider errored. IMPORTANT: an empty `matches` alongside `errors` is NOT a confirmed \"no match\" — do not treat it as \"contact is new\".",
            "items": {
              "type": "object",
              "properties": {
                "crm": {
                  "type": "string"
                },
                "error": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Pass as `webhook_id` to POST /trigger/crm-sync"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "url": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "WebhookList": {
        "type": "object",
        "properties": {
          "webhooks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            }
          }
        }
      },
      "CrmSyncRun": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Run id, used to poll the status endpoint"
          },
          "status": {
            "type": "string",
            "example": "processing"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "records_received": {
            "type": "integer"
          },
          "crm": {
            "type": "string",
            "example": "hubspot"
          }
        }
      },
      "CrmSyncRunStatus": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "finished",
              "partial",
              "failed"
            ],
            "description": "Terminal values name the outcome: `finished` = all records synced, `partial` = some failed, `failed` = every record failed. Treat all three as terminal when polling — not only `finished`. The completion webhook carries the same `status` plus `errors_sample`."
          },
          "total": {
            "type": "integer"
          },
          "processed": {
            "type": "integer",
            "description": "Records synced successfully"
          },
          "failed": {
            "type": "integer",
            "description": "Records that failed after retries"
          },
          "errors_sample": {
            "type": "array",
            "description": "First few failed records (present only when failed > 0)",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer"
                },
                "kind": {
                  "type": "string"
                },
                "error": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "description": "Per-CRM lookups that were skipped, not failed: for example a `linkedin_url` was given but no LinkedIn property is mapped in that CRM's settings, so only the other identifiers were searched. Present only when something was skipped. An empty `matches` alongside `warnings` is weaker than a plain \"no match\": say so to the user before creating a record.",
            "items": {
              "type": "object",
              "properties": {
                "crm": {
                  "type": "string"
                },
                "warning": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "CrmSyncRecordStatus": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "description": "Position of the record in the submitted payload (contacts first, then companies)"
          },
          "kind": {
            "type": "string",
            "enum": [
              "contact",
              "company"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "synced",
              "failed",
              "pending"
            ]
          },
          "error": {
            "type": "string",
            "description": "Present when status is failed"
          },
          "attempts": {
            "type": "integer"
          },
          "crm_contact_id": {
            "type": "string",
            "description": "CRM id of the contact created/updated"
          },
          "crm_company_id": {
            "type": "string",
            "description": "CRM id of the company created/updated"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CrmSyncRunRecords": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "total": {
            "type": "integer"
          },
          "records": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CrmSyncRecordStatus"
            }
          }
        }
      },
      "TodaysAccountsOwner": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "CRM owner id (HubSpot owner id, Salesforce User id)."
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "email": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "TodaysAccountsList": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "none",
              "pending",
              "ended",
              "error"
            ],
            "description": "`none`: never ranked, call refresh. `pending`: ranking, poll again."
          },
          "scope": {
            "type": "string",
            "nullable": true,
            "description": "The book: `all`, `unassigned`, `owner:<CRM owner id>`, or `unlinked:<user id>` when the caller has no CRM owner."
          },
          "crm": {
            "type": "string",
            "nullable": true,
            "enum": [
              "hubspot",
              "salesforce"
            ]
          },
          "requested_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "generated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "owner_linked": {
            "type": "boolean",
            "nullable": true,
            "description": "False when the caller's own book was asked and no CRM owner matches their email."
          },
          "book_size": {
            "type": "integer",
            "nullable": true,
            "description": "Accounts with a domain in the book."
          },
          "accounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TodaysAccount"
            }
          }
        }
      },
      "TodaysAccount": {
        "type": "object",
        "properties": {
          "rank": {
            "type": "integer",
            "example": 1
          },
          "domain": {
            "type": "string",
            "example": "pennylane.com"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "location": {
            "type": "string",
            "nullable": true,
            "description": "City and country from the CRM address; `null` when the CRM has none.",
            "example": "Paris, France"
          },
          "industry": {
            "type": "string",
            "nullable": true,
            "description": "The CRM industry; `null` when the CRM has none."
          },
          "angle": {
            "type": "string",
            "nullable": true,
            "description": "Why this person, why now: one line written at list time in the account's language. `null` when it could not be written.",
            "example": "Nouveau VP Sales depuis 8 jours : il choisit ses outils maintenant."
          },
          "crm_id": {
            "type": "string",
            "nullable": true,
            "description": "HubSpot company id or Salesforce Account id."
          },
          "owner": {
            "type": "object",
            "nullable": true,
            "description": "`null` when unassigned.",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "signal": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "JOB_CHANGE",
                  "NEW_HIRE",
                  "JOB_POSTING",
                  "CONTACT_SOURCING",
                  "HIRING"
                ]
              },
              "reason": {
                "type": "string",
                "example": "Charlotte C. just moved here as Team Lead BDR"
              },
              "detected_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "person": {
            "type": "object",
            "nullable": true,
            "description": "The one to reach first (`people[0]`); `null` when no contact is known.",
            "properties": {
              "role": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "DECISION_MAKER",
                  "CHAMPION",
                  "USER",
                  null
                ],
                "description": "Who they are to the purchase, read off the job title; `null` when unclear."
              },
              "full_name": {
                "type": "string",
                "nullable": true
              },
              "job_title": {
                "type": "string",
                "nullable": true
              },
              "linkedin_url": {
                "type": "string",
                "nullable": true
              },
              "crm_id": {
                "type": "string",
                "nullable": true,
                "description": "CRM contact id, when the person is in the CRM."
              },
              "email": {
                "type": "string",
                "nullable": true
              },
              "phone": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "people": {
            "type": "array",
            "description": "The buying committee, the signal's person first: up to 3, one per role when LoneScale or your CRM knows them (more after `find-people`). Older lists carry `[person]`.",
            "items": {
              "$ref": "#/components/schemas/TodaysAccountPerson"
            }
          },
          "last_activity_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Last sales activity on the account; `null` when never contacted."
          },
          "open_deal": {
            "type": "boolean"
          },
          "looks_like_your_wins": {
            "type": "boolean",
            "description": "Industry and size typical of your recent closed-won deals."
          },
          "former_customer": {
            "type": "boolean",
            "description": "A deal won more than 18 months ago: a win-back. Current customers (a deal won in the last 18 months, or the CRM's customer stage) are never listed."
          },
          "score": {
            "type": "number",
            "example": 10.4
          },
          "score_parts": {
            "type": "array",
            "description": "How the score adds up.",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string",
                  "example": "Job change, 10 d old"
                },
                "points": {
                  "type": "number",
                  "example": 6.1
                }
              }
            }
          }
        }
      },
      "TodaysAccountPerson": {
        "type": "object",
        "description": "A person to reach at a listed account.",
        "properties": {
          "role": {
            "type": "string",
            "nullable": true,
            "enum": [
              "DECISION_MAKER",
              "CHAMPION",
              "USER",
              null
            ],
            "description": "Who they are to the purchase, read off the job title; `null` when unclear."
          },
          "full_name": {
            "type": "string",
            "nullable": true
          },
          "job_title": {
            "type": "string",
            "nullable": true
          },
          "linkedin_url": {
            "type": "string",
            "nullable": true
          },
          "crm_id": {
            "type": "string",
            "nullable": true,
            "description": "CRM contact id, when the person is in the CRM."
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          }
        }
      }
    },
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Provide your API key in the x-api-key header."
      }
    }
  }
}
