Signals

Create Job Changes signal

Track when the decision-makers you care about change jobs — get notified when someone matching your target personas starts a new position at a company in your ICP, and receive their enriched profile at your webhookUrl.

Input: name, webhookUrl, and a config object with: jobTitles (preset personas from GET /api/v1/personas, e.g. "CTO", "Sales Director"), customTitles (your own titles), excludedTitles, companySizeFilters / excludedCompanySizeFilters, locations / excludedLocations, industryFilters / excludedIndustryFilters (values from GET /api/v1/signals/industries), companyTypeFilters, excludedCompanyUrls, maxJobAgeDays (how recent the new position must be; minimum and default 180 days), and optional enrichments (getEmails, getPhones, getSireneData, detectTechnologies, getHomepageContent). Top-level dailyLeadLimit (1–999,999) and status (draft/active/paused, default draft) complete the request.

How the filters are applied — read this before sizing a list:

  • Titles are matched by meaning, not as text. jobTitles and customTitles form ONE list; a person qualifies by matching ANY entry. Each entry is read as a function plus a seniority level, in any language ("Directeur Commercial" satisfies a "Sales Director" target), and a role at that level OR ABOVE qualifies: a Director target accepts VPs and heads of the function and refuses a plain Manager; a Manager target also accepts Directors. A custom title carries the level its own words state; one with no level word accepts every level. excludedTitles is applied first and matches whole words, case- and accent-insensitive ("intern" removes "Intern" and "Interns", never "International"; end a word with * to match a prefix).
  • Company size is judged on the NEW employer: its declared band OR its employee count must fall in companySizeFilters (a count within 10% of a band edge counts for both bands; a company declaring 501+ employees is never placed in a smaller band by a low count). Exclusions are strict on either value. A company whose size cannot be determined is delivered only when your selection includes a band under 51 employees.
  • Location is the PERSON's profile location, not the company's headquarters. One matching entry in locations is enough; a profile with no location is dropped when locations is set; a profile that states only a country does not match a city or region entry. locationFilter is not used — send locations.
  • Industry is an exact-name match on the new employer's industry, except that a CATEGORY name accepts every industry inside it — and ten names ("Manufacturing", "Retail", "Wholesale", …) are both an industry and a category and are applied as the category. GET /api/v1/signals/industries gives each category's real industryCount and ?category= lists its members. A value outside that list matches nothing.
  • Recency: a position is delivered up to maxJobAgeDays days after its start date, never less than 180; a smaller value is applied as 180.

Output: 201 Created returns the signal, its stored config, applied (the settings in force where they differ from config) and warnings (filter values that are probably not what you meant — a category sent with its own industries, an unknown industry). Each person matching your personas and filters who recently started a new job is pushed to your webhookUrl as a JSON lead payload (WebhookPersonLeadPayload): a signal object (id, name, type, human-readable label, detection date, plus the move itself — previous_title, previous_company, previous_company_linkedin_url, previous_job_started_on, previous_job_ended_on, new_title, new_company, new_job_started_on ("MM/YYYY"), new_job_start_month (the same month as "YYYY-MM", sortable) and change_type: company, title or both. Test the freshness of a change against new_job_started_on / new_job_start_month — previous_job_ended_on is a different date whenever there was a gap between the two jobs. These ten signal keys are a stable contract: present on every job-change lead, never renamed or null, "" when a value is unknown — safe to map straight into your CRM), a contact object (first/last name, title, location, LinkedIn and Sales Navigator profile URLs, matched persona, and email/phone when enrichment is enabled), and a company object (name, website, LinkedIn URL — plus detected tech stack and homepage title/description when those enrichments are on). Fields that aren't available are returned as empty strings.

Use cases:

  • New-buyer outreach — reach decision-makers in their first 90 days, when they re-evaluate tools and vendors.
  • Champion tracking — follow past champions to their new company for a warm re-intro.
  • Territory alerts — notify the owning rep when a target persona lands at an account.
post/api/v1/signals/job-changes

Request

  • Base URL: https://api.rodz.io (the document declares no server; this is the origin it was published from)
  • URL: https://api.rodz.io/api/v1/signals/job-changes
  • Auth: HTTP bearer

Request body

namestring required

Name for this signal

webhookUrlstring nullable

Where each lead is POSTed as it is produced. Optional (since 2026-09-23): omit it when you read leads with GET /api/v1/signals/{signalId}/leads or route them to a connected CRM or sequencer with destinations on PATCH /api/v1/signals/{signalId}.

dailyLeadLimitinteger

Max leads per day

duplicateHandlingstring nullable

Duplicate lead handling: 'refuse' (never accept), 'allow' (always accept), or 'Xd' (accept a duplicate after X days, e.g. '7d'). Optional — defaults to 'allow', so a contact who triggers this signal a second time is delivered a second time.

statusstring nullable

Initial status (default: draft). Accepts draft, active, or paused. If active, mandatory config fields are validated.

Example request

{
  "config": {
    "companySizeFilters": [
      "51-200",
      "201-500",
      "501-1000"
    ],
    "customTitles": [
      "Head of Platform"
    ],
    "detectTechnologies": false,
    "excludedCompanySizeFilters": [
      "Self-employed",
      "1-10"
    ],
    "excludedLocations": [],
    "excludedTitles": [],
    "getEmails": true,
    "getHomepageContent": false,
    "getPhones": false,
    "getSireneData": false,
    "jobTitles": [
      "CTO",
      "Product Director"
    ],
    "locations": [
      "France"
    ]
  },
  "dailyLeadLimit": 50,
  "name": "Decision makers changing jobs",
  "status": "active",
  "webhookUrl": "https://your-server.com/webhooks/rodz"
}

Response

Signal created

idstring required
namestring required
statusstring required
signalTypeIdstring required
webhookUrlstring nullable
configobject nullable

Signal-specific configuration

dailyLeadLimitinteger required
leadsTodayinteger required
leadsTotalinteger required
duplicateHandlingstring required

Duplicate lead handling: 'refuse' (never accept), 'allow' (always accept), or 'Xd' (accept after X days)

createdAtstring required
updatedAtstring required
lastLeadAtstring nullable
appliedobject

Settings this signal runs with where they differ from config, keyed like config. {} when every stored setting is applied as stored. A null value means the stored field is not used by this signal. Every entry is explained by a warning naming the same field.

Example response

{
  "applied": {
    "allowUnknownCompanySize": false,
    "maxJobAgeDays": 180
  },
  "config": {
    "companySizeFilters": [
      "201-500",
      "501-1000"
    ],
    "getEmails": true,
    "jobTitles": [
      "CTO"
    ],
    "locations": [
      "France"
    ],
    "maxJobAgeDays": 90
  },
  "createdAt": "2026-01-15T10:30:00Z",
  "dailyLeadLimit": 50,
  "duplicateHandling": "refuse",
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "lastLeadAt": "2026-03-15T14:22:00Z",
  "leadsToday": 3,
  "leadsTotal": 127,
  "name": "CTOs changing jobs in France",
  "signalTypeId": "job_changes",
  "status": "active",
  "updatedAt": "2026-03-16T08:00:00Z",
  "warnings": [
    {
      "code": "VALUE_BELOW_MINIMUM",
      "field": "maxJobAgeDays",
      "message": "`maxJobAgeDays` is 90; this signal applies 180. Job changes are delivered up to 180 days after the start date, and a shorter window is raised to that minimum.",
      "severity": "info",
      "values": [
        "90"
      ]
    }
  ],
  "webhookUrl": "https://your-server.com/webhooks/rodz"
}

Changes

No recorded changes to this operation across all 1 revision of this API.