Skip to main content
POST
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. 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 and reconcile before retrying; a blind retry creates a second monitor that reports and charges separately.
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 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.
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 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 to send a failed or uncertain event again.

Next step

Use Get a new hire monitor to follow company resolution and the baseline.

Authorizations

Authorization
string
header
required

Use an Airscale workspace API key. Never expose the key in client-side code.

Body

application/json

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.

name
string
required

Also names the results table: <name> — New hires.

Required string length: 1 - 120
frequency
enum<string>
default:weekly
Available options:
daily,
weekly,
every_30_days,
every_90_days
audience
enum<string>

companies watches the listed companies; market watches every company that matches the market filters. Defaults to companies when companies is sent, otherwise market.

Available options:
companies,
market
companies
object[]

companies audience only, and required for it: 1–10,000 LinkedIn company URLs. Two entries for the same company are rejected.

Required array length: 1 - 10000 elements
job_titles
string[]

Up to 100 titles. Required for the market audience. Empty on a company list means every role.

Maximum array length: 100
Required string length: 1 - 200
excluded_job_titles
string[]

Up to 100 titles to leave out.

Maximum array length: 100
Required string length: 1 - 200
title_match
enum<string>
default:strict

strict matches the titles you list; loose also matches related titles in the same function.

Available options:
strict,
loose
seniorities
enum<string>[]

Leave out or send an empty array for every seniority.

Maximum array length: 4
Available options:
individual_contributor,
manager,
director,
executive
hire_types
enum<string>[]

external: joined from another company. internal: moved into the role inside the same company.

Required array length: 1 - 2 elements
Available options:
external,
internal
industries
string[]

market audience only.

Maximum array length: 300
Required string length: 1 - 200
company_size
enum<string> | null

market audience only.

Available options:
1-10,
11-50,
51-200,
201-500,
501-1000,
1001-5000,
5001-10000,
10001+,
null
company_location
string | null

market audience only. A place name such as France.

Maximum string length: 150
person_location
string | null

market audience only. A place name such as Paris.

Maximum string length: 150
webhook_url
string<uri> | null

A public HTTPS URL without embedded credentials. Internal hostnames and private or reserved addresses are rejected.

Maximum string length: 2048

Response

The monitor was created and its first check scheduled. Checks run asynchronously; creation does not mean a check has completed.

monitor
object
required
companies
object[]
required

The monitor's company list, sorted by URL. Empty for a market monitor.

signing_secret
string | null
required

Returned once when a webhook is configured; null otherwise.

signing_secret_warning
string | null
required