> ## Documentation Index
> Fetch the complete documentation index at: https://docs.airscale.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a new hire monitor

> Watch a list of up to 10,000 companies, or the whole market, for people who start a role matching your filters, with one results table and an optional webhook. Rate limit: 120 requests per fixed minute per API key, shared by all new hire monitor endpoints.

> [!NOTE]
>
> New hire monitoring requires a **V2 workspace API key**. Legacy V1 API keys are not supported by these endpoints. Monitors and their credits belong to the V2 workspace associated with the key.

Create an active monitor with [Bearer authentication](https://docs.airscale.io/api-reference/authentication.md). A monitor watches either a list of companies (`audience: "companies"`) or the whole market (`audience: "market"`) for people who recently started a role matching your filters. Each new hire is written to the monitor's results table and, when configured, posted to your webhook. Each successful request creates a new monitor, and creation has no idempotency key. If a network failure leaves the result unknown, [list your monitors](https://docs.airscale.io/api-reference/new-hire-monitors/list.md) and reconcile before retrying; a blind retry creates a second monitor that reports and charges separately.

> [!NOTE]
>
> The `frequency` can be `daily`, `weekly`, `every_30_days`, or `every_90_days` (default `weekly`). If you provide `webhook_url`, store the returned `signing_secret` immediately; it is encrypted at rest and returned only once. If you lose it, [rotate it](https://docs.airscale.io/api-reference/new-hire-monitors/update.md) on the existing monitor instead of creating a new one.

## Choose an audience

* **Company list.** Send `companies`: 1 to 10,000 `{ "linkedin_url": ... }` LinkedIn company URLs, with a slug or a numeric company ID. URLs are canonicalized to `https://www.linkedin.com/company/<identifier>`, and two entries for the same company are rejected. `audience` defaults to `companies` when `companies` is sent. An empty `job_titles` list on a company list means every role. The market filters `industries`, `company_size`, `company_location`, and `person_location` are rejected on a company list.
* **Market.** Omit `companies` (or send `audience: "market"`) and send at least one `job_titles` entry. You can narrow it with `industries` (up to 300), `company_size` (`1-10`, `11-50`, `51-200`, `201-500`, `501-1000`, `1001-5000`, `5001-10000`, `10001+`), `company_location` (a place name such as `France`), and `person_location` (such as `Paris`).

Both audiences accept `job_titles` and `excluded_job_titles` (up to 100 each), `title_match` (`strict` matches the titles you list; `loose` also matches related titles in the same function), `seniorities` (`individual_contributor`, `manager`, `director`, `executive`; empty means all), and `hire_types` (`external` for people who joined from another company, `internal` for people who moved into the role inside the company; default both). Unknown fields are rejected with `422 invalid_monitor`.

> [!WARNING]
>
> **Filters are set at creation and cannot be edited.** `PATCH` accepts only the name, frequency, pause or resume, and the webhook. To change titles, seniorities, hire types, or market filters, create a new monitor.

## How monitoring works

Checks run asynchronously. The `201 Created` response contains `monitor`, `companies`, `signing_secret`, and `signing_secret_warning`; save `monitor.id`. The first completed check records a **baseline**: people already in a matching role are not reported. Later checks report people whose matching role started after the baseline. `baseline_completed_at` is `null` until the baseline exists.

When checks cannot progress, `run_block_reason` explains why, for example `unresolved_companies` (a company URL could not be found; the monitor is paused), `audience_needs_review` (the filters match too many people; the monitor is paused, so create a narrower monitor or resume to retry), `unsupported_location`, or a temporary `account_pool_unavailable`.

## Credits

Each reported new hire costs **1 credit**, charged when it is written to the results table. Nothing is charged at creation, for empty checks, for webhook deliveries or redeliveries, or for marking events read. When the workspace has no credits, new hires wait undelivered (`results_status: "blocked"`, `results_error: "insufficient_credits"`) and are delivered and charged after a top-up.

## Results table

Each monitor owns a regular Airscale results table named `<name> — New hires`. The monitor reports `results_table_id`, `results_status` (`ready`, `pending`, or `blocked`) and `results_error` (for example `insufficient_credits`, `results_table_deleted`, `table_row_limit_reached`, `results_columns_missing`, or a temporary `credit_settlement_busy` or `table_mutation_locked`). Deleting the monitor keeps its results table.

## Request size

The complete JSON request body must be at most **8 MiB (8,388,608 bytes)**; a larger body returns `413`. A missing or malformed body returns `400` with `{"error": "Invalid JSON"}`.

## Webhook URL rules

`webhook_url` must be a public HTTPS URL without embedded credentials. Other protocols, internal hostnames, and private or reserved addresses are rejected with `422 invalid_monitor` before the monitor is created.

## Verify webhook signatures

Airscale sends each new hire as a JSON `POST` to `webhook_url`, within about a minute of delivery to the results table. The body has `"type": "new_hire.detected"` plus the same fields as an event from [List new hire events](https://docs.airscale.io/api-reference/new-hire-monitors/events.md) without `read_at` or `webhook`: `id`, `monitor_id`, `detected_at`, `hire_type`, `person`, `title`, `start_date`, `company`, `previous_title`, and `previous_company_name`.

For example, `"start_date": "2026-10"` is the reported start month, `company.linkedin_url` is built from the numeric `linkedin_company_id`, and any unknown field is `null`.

The request includes `X-Airscale-Event: new_hire.detected`, `X-Airscale-Event-Id: <event_id>`, `X-Airscale-Timestamp`, `X-Airscale-Signature: sha256=<hex>`, and `Idempotency-Key: new-hire-event:<event_id>`. Compute HMAC-SHA256 with the `signing_secret` over `<timestamp>.<raw request body>`, compare it in constant time, and reject timestamps more than five minutes from the current time. This is the same scheme as job-change monitor webhooks. Deduplicate on the event ID and return a 2xx response only after durable handling.

Airscale makes up to five attempts with exponential backoff. `408`, `425`, `429`, and `5xx` responses are retried; other responses fail permanently (`webhook_http_<status>`), and timeouts or dropped connections are marked `uncertain` because the request may have been received. Use [Redeliver a webhook](https://docs.airscale.io/api-reference/new-hire-monitors/events/redeliver.md) to send a failed or uncertain event again.

## Next step

Use [Get a new hire monitor](https://docs.airscale.io/api-reference/new-hire-monitors/get.md) to follow company resolution and the baseline.


## OpenAPI

````yaml openapi.json POST /v1/new-hire-monitors
openapi: 3.1.0
info:
  title: Airscale Public API
  version: '2026-09-11'
  description: >-
    Search, enrich, resolve public business data, and monitor job changes with
    Airscale.
  x-airscale-source-repository: ViceScale/airscale-code
  x-airscale-source-sha: 1de19e1b70a052a4b8d9c2075021a7e5e7a94d51
servers:
  - url: https://api.airscale.io
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Search and discovery
    description: Search people, companies, and the web.
  - name: Contact data
    description: Find professional and personal contact data.
  - name: Profiles and reverse lookup
    description: Extract profiles or resolve a person from known contact data.
  - name: Post engagement
    description: Retrieve and enrich people who liked or commented on LinkedIn posts.
  - name: LinkedIn content
    description: >-
      Search LinkedIn posts, read profile and company feeds, and retrieve
      profile comments and comment reactions.
  - name: Job change monitoring
    description: Track LinkedIn profiles and receive job-change events.
  - name: New hire monitoring
    description: Watch companies or the market for people who start a matching role.
  - name: Post engagement monitoring
    description: >-
      Follow LinkedIn company pages and profiles and receive the people who
      engage with their posts.
  - name: Account
    description: Inspect workspace account state.
  - name: Miscellaneous
    description: >-
      Find website technologies and check WhatsApp availability, Meta ads, and
      email deliverability.
paths:
  /v1/new-hire-monitors:
    post:
      tags:
        - New hire monitoring
      summary: Create a new hire monitor
      description: >-
        Creates an active monitor that watches either a list of 1–10,000
        companies or the whole market for people who recently started a role
        matching your filters. Each monitor owns an Airscale results table that
        receives one row per new hire. Checks run asynchronously; the first
        completed check records a baseline, and people already in a matching
        role are not reported. Filters cannot be edited after creation. If
        webhook_url is set, the 201 response includes a signing_secret exactly
        once. Every request creates a new monitor and there is no idempotency
        key: if the response is lost, list monitors before retrying. Requires a
        V2 workspace API key. Legacy V1 API keys are not supported. Monitors and
        their credits belong to the V2 workspace.
      operationId: createNewHireMonitor
      requestBody:
        required: true
        description: >-
          Choose a name, a frequency, an audience with its filters, and an
          optional webhook in a JSON body of at most 8 MiB (8,388,608 bytes).
          Unknown fields are rejected.
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
                - name
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                  description: 'Also names the results table: `<name> — New hires`.'
                frequency:
                  type: string
                  enum:
                    - daily
                    - weekly
                    - every_30_days
                    - every_90_days
                  default: weekly
                audience:
                  type: string
                  enum:
                    - companies
                    - market
                  description: >-
                    companies watches the listed companies; market watches every
                    company that matches the market filters. Defaults to
                    companies when companies is sent, otherwise market.
                companies:
                  type: array
                  minItems: 1
                  maxItems: 10000
                  items:
                    type: object
                    additionalProperties: false
                    required:
                      - linkedin_url
                    properties:
                      linkedin_url:
                        type: string
                        minLength: 1
                        maxLength: 500
                        description: >-
                          A LinkedIn company URL with a slug or numeric company
                          ID, such as
                          https://www.linkedin.com/company/example-company.
                          Airscale canonicalizes it to
                          https://www.linkedin.com/company/{identifier}.
                  description: >-
                    companies audience only, and required for it: 1–10,000
                    LinkedIn company URLs. Two entries for the same company are
                    rejected.
                job_titles:
                  type: array
                  maxItems: 100
                  items:
                    type: string
                    minLength: 1
                    maxLength: 200
                  default: []
                  description: >-
                    Up to 100 titles. Required for the market audience. Empty on
                    a company list means every role.
                excluded_job_titles:
                  type: array
                  maxItems: 100
                  items:
                    type: string
                    minLength: 1
                    maxLength: 200
                  default: []
                  description: Up to 100 titles to leave out.
                title_match:
                  type: string
                  enum:
                    - strict
                    - loose
                  default: strict
                  description: >-
                    strict matches the titles you list; loose also matches
                    related titles in the same function.
                seniorities:
                  type: array
                  maxItems: 4
                  items:
                    type: string
                    enum:
                      - individual_contributor
                      - manager
                      - director
                      - executive
                  description: Leave out or send an empty array for every seniority.
                hire_types:
                  type: array
                  minItems: 1
                  maxItems: 2
                  items:
                    type: string
                    enum:
                      - external
                      - internal
                  default:
                    - external
                    - internal
                  description: >-
                    external: joined from another company. internal: moved into
                    the role inside the same company.
                industries:
                  type: array
                  maxItems: 300
                  items:
                    type: string
                    minLength: 1
                    maxLength: 200
                  default: []
                  description: market audience only.
                company_size:
                  type:
                    - string
                    - 'null'
                  enum:
                    - 1-10
                    - 11-50
                    - 51-200
                    - 201-500
                    - 501-1000
                    - 1001-5000
                    - 5001-10000
                    - 10001+
                    - null
                  description: market audience only.
                company_location:
                  type:
                    - string
                    - 'null'
                  maxLength: 150
                  description: market audience only. A place name such as France.
                person_location:
                  type:
                    - string
                    - 'null'
                  maxLength: 150
                  description: market audience only. A place name such as Paris.
                webhook_url:
                  type:
                    - string
                    - 'null'
                  format: uri
                  maxLength: 2048
                  description: >-
                    A public HTTPS URL without embedded credentials. Internal
                    hostnames and private or reserved addresses are rejected.
            examples:
              companies:
                summary: Watch a list of companies
                value:
                  name: Target accounts
                  frequency: weekly
                  webhook_url: https://hooks.example.com/airscale/new-hires
                  companies:
                    - linkedin_url: https://www.linkedin.com/company/example-company
                    - linkedin_url: https://www.linkedin.com/company/example-company-2
                  job_titles:
                    - VP Sales
                    - Head of Sales
                  hire_types:
                    - external
              market:
                summary: Watch the whole market
                value:
                  name: French sales leaders
                  audience: market
                  frequency: every_30_days
                  job_titles:
                    - VP Sales
                  title_match: loose
                  seniorities:
                    - director
                    - executive
                  industries:
                    - Software Development
                  company_size: 51-200
                  company_location: France
      responses:
        '201':
          description: >-
            The monitor was created and its first check scheduled. Checks run
            asynchronously; creation does not mean a check has completed.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                  - monitor
                  - companies
                  - signing_secret
                  - signing_secret_warning
                properties:
                  monitor:
                    $ref: '#/components/schemas/NewHireMonitor'
                  companies:
                    type: array
                    items:
                      $ref: '#/components/schemas/NewHireCompany'
                    description: >-
                      The monitor's company list, sorted by URL. Empty for a
                      market monitor.
                  signing_secret:
                    type:
                      - string
                      - 'null'
                    description: >-
                      Returned once when a webhook is configured; null
                      otherwise.
                  signing_secret_warning:
                    type:
                      - string
                      - 'null'
              examples:
                success:
                  summary: Successful response
                  value:
                    monitor:
                      id: 61111111-1111-4111-8111-111111111111
                      name: Target accounts
                      frequency: weekly
                      status: active
                      audience: companies
                      legacy: false
                      filters:
                        job_titles:
                          - VP Sales
                          - Head of Sales
                        excluded_job_titles: []
                        title_match: strict
                        seniorities: []
                        hire_types:
                          - external
                        industries: []
                        company_size: null
                        company_location: null
                        person_location: null
                      results_table_id: 64444444-4444-4444-8444-444444444444
                      results_status: ready
                      results_error: null
                      baseline_completed_at: null
                      next_check_at: '2026-11-02T10:00:00.000Z'
                      last_successful_check_at: null
                      run_block_reason: null
                      created_at: '2026-11-02T10:00:00.000Z'
                      updated_at: '2026-11-02T10:00:00.000Z'
                      webhook_url: https://hooks.example.com/airscale/new-hires
                      webhook_configured: true
                      company_count: 2
                      unresolved_company_count: 0
                      new_hire_count: 0
                      unread_event_count: 0
                    companies:
                      - identifier: example-company
                        linkedin_url: https://www.linkedin.com/company/example-company
                        linkedin_company_id: null
                        name: null
                        status: pending
                        last_checked_at: null
                      - identifier: example-company-2
                        linkedin_url: https://www.linkedin.com/company/example-company-2
                        linkedin_company_id: null
                        name: null
                        status: pending
                        last_checked_at: null
                    signing_secret: example-signing-secret
                    signing_secret_warning: Store this secret now. It is not returned again.
        '400':
          description: >-
            The request body is missing or is not valid JSON. The public API
            returns {"error":"Invalid JSON"}.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                error:
                  summary: Error response
                  value:
                    error: Invalid JSON
        '401':
          $ref: '#/components/responses/MonitorUnauthorized'
        '413':
          description: The JSON request body exceeds the 8 MiB (8,388,608 bytes) limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: >-
            invalid_monitor, for example a missing name, an unknown field, an
            invalid or duplicate LinkedIn company URL, job_titles missing on a
            market monitor, a market filter (industries, company_size,
            company_location, person_location) on a company list, or a
            webhook_url that is not a public HTTPS destination.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorApiError'
        '429':
          description: >-
            The API key has exceeded 120 requests in the current minute across
            new hire monitor endpoints. Wait for the Retry-After seconds before
            retrying.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                error:
                  summary: Error response
                  value:
                    error: Too Many Requests
                    message: 'Rate limit exceeded: 120 requests per minute'
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: string
              example: '60'
        '503':
          description: >-
            monitoring_not_enabled while new hire monitoring is closed, or
            service_unavailable.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MonitorApiError'
components:
  schemas:
    NewHireMonitor:
      type: object
      additionalProperties: false
      required:
        - id
        - name
        - frequency
        - status
        - audience
        - legacy
        - filters
        - results_table_id
        - results_status
        - results_error
        - baseline_completed_at
        - next_check_at
        - last_successful_check_at
        - run_block_reason
        - created_at
        - updated_at
        - webhook_url
        - webhook_configured
        - company_count
        - unresolved_company_count
        - new_hire_count
        - unread_event_count
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        frequency:
          type: string
          enum:
            - daily
            - weekly
            - every_30_days
            - every_90_days
        status:
          type: string
          enum:
            - active
            - paused
        audience:
          type: string
          enum:
            - companies
            - market
        legacy:
          type: boolean
          description: >-
            true for monitors created in the app before role filters existed.
            They have filters: null and can be read, paused, resumed and
            renamed, but their companies can only be edited in the app.
        filters:
          description: >-
            The role and market filters set at creation, or null for a legacy
            monitor. Filters cannot be edited.
          oneOf:
            - $ref: '#/components/schemas/NewHireMonitorFilters'
            - type: 'null'
        results_table_id:
          type: string
          description: The Airscale results table that receives one row per new hire.
        results_status:
          type: string
          enum:
            - ready
            - pending
            - blocked
          description: >-
            Whether found new hires are delivered to the results table. pending:
            deliveries are waiting. blocked: see results_error.
        results_error:
          type:
            - string
            - 'null'
          description: >-
            Why found new hires are not yet delivered, for example
            insufficient_credits, results_table_deleted,
            table_row_limit_reached, results_columns_missing, or a temporary
            credit_settlement_busy or table_mutation_locked.
        baseline_completed_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            When the first completed check recorded the baseline. null until it
            exists, and again after a company change resets it.
        next_check_at:
          type:
            - string
            - 'null'
          format: date-time
        last_successful_check_at:
          type:
            - string
            - 'null'
          format: date-time
        run_block_reason:
          type:
            - string
            - 'null'
          description: >-
            Why checks are not progressing, for example unresolved_companies,
            audience_needs_review, unsupported_location, or a temporary
            account_pool_unavailable.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        webhook_url:
          type:
            - string
            - 'null'
          format: uri
        webhook_configured:
          type: boolean
        company_count:
          type: integer
          minimum: 0
          maximum: 10000
          description: 0 for a market monitor.
        unresolved_company_count:
          type: integer
          minimum: 0
          description: Companies that are pending or could not be found.
        new_hire_count:
          type: integer
          minimum: 0
          description: Every delivered new hire, not only the 100-event preview.
        unread_event_count:
          type: integer
          minimum: 0
    NewHireCompany:
      type: object
      additionalProperties: false
      required:
        - identifier
        - linkedin_url
        - linkedin_company_id
        - name
        - status
        - last_checked_at
      properties:
        identifier:
          type: string
          description: >-
            The lower-case company slug or numeric company ID from the LinkedIn
            URL. Use it to remove the company.
        linkedin_url:
          type: string
          format: uri
        linkedin_company_id:
          type:
            - string
            - 'null'
          description: The numeric LinkedIn company ID once the company is resolved.
        name:
          type:
            - string
            - 'null'
        status:
          type: string
          enum:
            - pending
            - resolved
            - unresolved
        last_checked_at:
          type:
            - string
            - 'null'
          format: date-time
    Error:
      type: object
      additionalProperties: true
      properties:
        error:
          type: string
        message:
          type: string
    MonitorApiError:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          type: object
          additionalProperties: false
          required:
            - code
            - message
          properties:
            code:
              type: string
            message:
              type: string
    NewHireMonitorFilters:
      type: object
      additionalProperties: false
      required:
        - job_titles
        - excluded_job_titles
        - title_match
        - seniorities
        - hire_types
        - industries
        - company_size
        - company_location
        - person_location
      properties:
        job_titles:
          type: array
          items:
            type: string
        excluded_job_titles:
          type: array
          items:
            type: string
        title_match:
          type: string
          enum:
            - strict
            - loose
        seniorities:
          type: array
          items:
            type: string
            enum:
              - individual_contributor
              - manager
              - director
              - executive
          description: Empty means every seniority.
        hire_types:
          type: array
          items:
            type: string
            enum:
              - external
              - internal
        industries:
          type: array
          items:
            type: string
        company_size:
          type:
            - string
            - 'null'
          enum:
            - 1-10
            - 11-50
            - 51-200
            - 201-500
            - 501-1000
            - 1001-5000
            - 5001-10000
            - 10001+
            - null
        company_location:
          type:
            - string
            - 'null'
        person_location:
          type:
            - string
            - 'null'
  responses:
    MonitorUnauthorized:
      description: >-
        A missing Bearer token returns
        {"error":"Unauthorized","message":"Missing or invalid bearer token"}. An
        unknown API key returns invalid_api_key.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/MonitorApiError'
              - $ref: '#/components/schemas/Error'
          examples:
            invalidKey:
              summary: Unknown API key
              value:
                error:
                  code: invalid_api_key
                  message: The API key is not valid
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: >-
        Use an Airscale workspace API key. Never expose the key in client-side
        code.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.