---
title: "Create Company Growth signal"
method: POST
path: "/api/v1/signals/company-growth"
tags: ["Signals"]
---

# Create Company Growth signal

`POST /api/v1/signals/company-growth`

## 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/company-growth`
- Auth: HTTP bearer

Find companies whose headcount is growing (or shrinking) by a chosen percentage, then surface the right decision-makers at each — typically executives who control budget or hiring.

**Input:** your target geography and company profile (`locations`, `companySizeFilters`, `industryFilters`), the headcount growth band (`growthMin` and `growthMax` as signed percentages: negative values mean shrinking), which people to find at those companies (`enablePersonaLookup` flag + `targetPersonas` like "Sales Director"), and whether to enrich with emails/phones (`getEmails`, `getPhones`).

**Output:** every new company matching your growth and ICP filters surfaces matched contacts to your `webhookUrl`. Each lead includes contact name, title, LinkedIn URL, company name/website, and matched persona. Email and phone are included only if enrichment is enabled. Persona targeting is mandatory—if `enablePersonaLookup` is false or `targetPersonas` is empty, the signal produces no leads.

**Use cases:**
- Scale-timing — growing companies need more seats and tools; pitch as they scale.
- Restructuring plays — target shrinking companies with cost-optimisation offers.

## Request body

- object: Create a Company Growth signal.
  - `name` string, required: Name for this signal
  - `webhookUrl` string, 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}.
  - `config` CompanyGrowthConfig, required: Config for company_growth — company headcount trajectory. Inherits PersonaTargetingConfig so the enable_persona_lookup -> enable_persona_targeting mapping runs (persona targeting is the whole point). extra="allow" preserves infra fields (daily_lead_limit) read raw by the task.
    - `enablePersonaTargeting` boolean: Enable persona-based targeting
    - `targetPersonas` string[]: Filter by job-role persona (decision-maker targeting). Accepted values are the persona display names listed here; see GET /v1/personas for the full taxonomy with department, seniority and EN/FR job titles. Titles are matched by meaning, not as text: `targetPersonas` and `customPersonaTitles` form ONE list and a person qualifies by matching ANY entry. Each entry is read as a function plus a seniority level, in any language ("Directeur Commercial" satisfies "Sales Director", "Président" or "Directeur général" a CEO target), and a role at that level or above qualifies: a Director or Head target refuses a plain Manager; a founder running the company counts as CEO. A custom title carries the level its own words state; one with no level word accepts every level. Valid values: "Account Executive", "Account Manager", "Accountant", "Agency Director", "Business Analyst", "Business Developer", "Business Unit Director", "Buyer", "CEO", "CIO", "CSR Director", "CSR Manager", "CTO", "Communication Director", "Community Manager", "Content Manager", "Copywriter", "Creative Director", "Customer Success Director", "Customer Success Manager", "Customer Support Agent", "Cybersecurity Director", "Data Analyst", "Data Director", "Designer", "DevOps Engineer", "Digital Director", "Digital Project Manager", "Ecommerce Director", "Ecommerce Manager", "Engineering Manager", "Engineering Office Director", "Ergonomist", "Executive Assistant", "Finance Director", "Finance Manager", "GTM Director", "GTM Manager", "Growth Director", "Growth Manager", "HR Assistant", "HR Development", "HR Director", "Happiness Manager", "IT Project Manager", "Industrial Director", "Innovation Director", "Lab Manager", "Legal Counsel", "Legal Director", "Legal Manager", "Logistics Coordinator", "Logistics Director", "M&A Director", "Marketing Director", "Marketing Manager", "Office Manager", "Omnichannel Director", "Operations Director", "Operations Manager", "Payroll Manager", "Payroll Officer", "Procurement Specialist", "Product Director", "Product Manager", "Project Manager", "Purchasing Director", "Purchasing Manager", "Quality Health and Safety", "Quality Manager", "Quality Technician", "R&D Director", "R&D Engineer", "R&D Manager", "Recruitment Manager", "Research Director", "Research Scientist", "SDR / BDR", "Sales Director", "Sales Manager", "Social Media Director", "Social Media Manager", "Software Engineer", "Supply Chain Director", "Support Director", "Support Manager", "Talent Acquisition", "Technical Director", "Training Director", "Training Officer", "Transformation Director", "UX Designer", "Warehouse Director", "Web Developer"
    - `personaExportMode` string: Which matched contacts to deliver per company. "top_ranked" (default) delivers the best-ranked contacts; "all_personas" delivers contacts matching any of your personas. "maxLeadsPerCompany" caps the number of contacts per company in either mode (1 when omitted).
    - `maxLeadsPerCompany` integer, nullable: Maximum number of leads to create per company; 1 when omitted. On job offer, republished job offer and recruitment campaign signals it is the maximum number of contacts per company per day, and `dailyLeadLimit` counts companies: with a limit of 1 and a maximum of 10, you receive at most 10 contacts from one company per day. A company is delivered at most once a day, however many jobs it posts.
    - `personaId` string, nullable: Legacy single persona ID
    - `personaLocations` string[]: Locations for persona search
    - `personaExcludedLocations` string[]: Locations to exclude from persona search. With only excludes set, every location except these matches; with only includes (personaLocations) set, only those match.
    - `customPersonaTitles` string[]: Custom free-text job titles to target in addition to predefined personas
    - `excludedTitles` string[]: Job titles to exclude. Matched as whole words, case- and accent-insensitive: "intern" excludes "Intern" and "Interns", never "International"; a phrase matches the same words in the same order; end a word with * to match a prefix ("infirmi*" excludes "Infirmière").
    - `requireContact` boolean: Only deliver leads with a contact. Off (default): when the people search finds nobody matching your personas at a company, you still receive the company alone, with no contact, priced as a company-level lead. On: that company is neither delivered nor charged, and it does not count toward `dailyLeadLimit`. Takes precedence over `companyOnlyWhenContactsRefused` where both are set. Only applies when `enablePersonaTargeting` is on.
    - `getEmails` boolean: Enable email enrichment (0.5 credit/lead, charged only on success)
    - `getPhones` boolean: Enable phone enrichment (5 credits/lead, charged only on success)
    - `getSireneData` boolean: Enrich with French SIRENE registry data (1 credit/lead, charged only on success)
    - `detectTechnologies` boolean: Detect technologies used on the company's public website (1 credit/lead, charged only on success)
    - `getHomepageContent` boolean: Fetch the company's homepage content (1 credit/lead, charged only on success)
    - `locations` string[]: Countries / regions to target (e.g. France)
    - `companySizeFilters` string[]: Company sizes to include (51-200, 201-500, ...)
    - `industryFilters` string[]: Industries to target. Use industry names from GET /api/v1/signals/industries — the entries with `kind: "industry"`, e.g. 'Software Development'. Unlike the other signals, this one resolves each name to a LinkedIn industry id, so a `kind: "category"` name does NOT expand to the industries inside it: 9 of the 19 categories resolve to nothing at all and are dropped, and the other 10 narrow to the single same-named industry. A name that does not resolve is skipped silently, so copy values verbatim. Empty = all industries.
    - `growthMin` integer, nullable: Signed headcount-growth floor % (negative = shrinking)
    - `growthMax` integer, nullable: Signed headcount-growth ceiling %
  - `dailyLeadLimit` integer: Max leads per day
  - `duplicateHandling` string, 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.
  - `status` string, nullable: Initial status (default: draft). Accepts draft, active, or paused. If active, mandatory config fields are validated.

## Response `201`

Successful Response

- object: A single signal: create, GET /signals/{id} and PATCH. It carries two fields the list does not. A customer reads their stored `config` back and assumes it is the one in force; on `job_changes` three fields were not (`maxJobAgeDays` 90 stored / 180 applied, `publishedDate` stored and never read, `allowUnknownCompanySize` true and applied false), with nothing anywhere saying so.
  - `id` string, required
  - `name` string, required
  - `status` string, required
  - `signalTypeId` string, required
  - `webhookUrl` string, nullable
  - `config` object, nullable: Signal-specific configuration
  - `dailyLeadLimit` integer, required
  - `leadsToday` integer, required
  - `leadsTotal` integer, required
  - `duplicateHandling` string, required: Duplicate lead handling: 'refuse' (never accept), 'allow' (always accept), or 'Xd' (accept after X days)
  - `createdAt` string, required
  - `updatedAt` string, required
  - `lastLeadAt` string, nullable
  - `applied` object: 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.
  - `warnings` SignalConfigWarning[]: What you should know about this configuration: values applied differently from what is stored, and filter values that are probably not what you meant. Computed from the stored configuration, so they appear on GET as well as on create and update - a signal created before these fields existed reports them too. Never an error: the signal is created or updated regardless.
    - `code` string, required: Stable identifier. `VALUE_BELOW_MINIMUM` (a value under the documented minimum is applied as the minimum), `VALUE_ADJUSTED` (a value the search cannot use as written is applied as the nearest one it can, e.g. a `publishedDate` of 14d searches 15 days), `FIELD_NOT_USED` (a stored field this signal ignores), `UNKNOWN_SIZE_NOT_ACCEPTED`, `CATEGORY_WITH_OWN_INDUSTRIES` (a category listed together with industries it already covers), `UNKNOWN_INDUSTRY` (a value matching no published industry), `UNKNOWN_FIELD` (a config key this signal does not have), `UNKNOWN_TOOL` / `UNKNOWN_CATEGORY` (a tracked tool or category name that is not in the catalog of GET /api/v1/signals/technographic-tools; on a GMB Discovery signal, a `category` that is not a Google Maps category, so the signal matches no business), `UNKNOWN_CITY` (GMB Discovery: cities in `cities` that are not in the signal's `country` — `values` lists them; when none is, the signal matches no business until `cities` or `country` is changed; never an error, nothing stored is changed), `NO_FOLLOWER_SOURCE` (a Company Followers signal with no follower extraction set up yet, so it produces nothing), `DAILY_LIMIT_PENDING` (a job signal's `dailyLeadLimit` changed less than a day after its search was last rebuilt: the search keeps its previous volume until the time in `values`, while the leads delivered per day already follow the new limit), `WATCHED_LIST_EMPTY` (a watchlist reading its list from your CRM holds no company or person — the CRM is not connected, the list could not be read, or it is empty — so it produces nothing until the list fills; cleared on its own once it does), `QUOTE_REQUIRED` (an `active` Company Followers signal in `icp` mode whose follower extraction has not been priced and accepted, so it produces nothing yet: `values[0]` says why — `not_requested`, `awaiting_acceptance` (then `values[1]` is the quoted price in credits), `expired`, `declined`, `failed`, or, after an automatic quote, `auto_quote_refused` / `auto_accept_refused` (then `values[1]` is the error code); the message names the call to make — see POST /api/v1/signals/{signal_id}/extraction/quote; cleared once a quote is accepted), `WATCHED_LIST_IMPORTING` (a company watchlist whose HubSpot list is still being read: companies are added as they arrive; `values[0]` is the number of members read so far, `values[1]` the list id; cleared once the whole list is read), `MATCHING_CONFIRMATION_REQUIRED` (a New Decision Maker or Account watchlist whose companies must be matched to their LinkedIn company page before they can be watched, at a cost above the amount we match without asking: nothing is matched or charged until you confirm with POST /api/v1/watchlists/{signalId}/companies/confirm-matching; `values[0]` is the maximum cost, `values[1]` the number of companies to match; cleared once confirmed), `DESTINATION_UNAVAILABLE` (the outreach campaign this signal sends leads to cannot receive them — not found, archived, ended or without any step — so leads are not reaching it: `values[0]` is `campaign_not_found`, `campaign_archived`, `campaign_ended` or `campaign_no_steps`; cleared once a lead reaches the campaign or the signal's destination is changed), `DESTINATION_NEEDS_CONTACT` (the signal delivers companies with no contact and a destination in `destinations` only accepts people, so none of its leads reach that destination), `DESTINATION_SKIPS_COMPANY_ONLY` (some leads are companies with no contact, e.g. no one matching your personas was found, and a destination only accepts people), `DESTINATION_NEEDS_EMAIL` (a destination only accepts leads with an email and `getEmails` is off) — for these three, `values[0]` is the destination key in `destinations`, the next values are the fixes: `add_persona` (turn on `enablePersonaTargeting` with `targetPersonas`), `require_contact` (turn on `requireContact`), `add_company_destination` (add a destination that accepts companies: `hubspot`, `pipedrive`, `googleSheets` or `customWebhook`), `enable_email_finding` (turn on `getEmails`); severity `warning` when those leads reach no destination at all, `info` when another destination still receives them; never blocks the save; `COMPANY_PAGE_UNAVAILABLE` (a Company Followers signal whose extraction stopped because a page in `linkedinCompanyUrls` does not exist on LinkedIn: nothing was extracted or charged; `values` lists the pages; replace them with PATCH /api/v1/signals/{signal_id}, then start the extraction again; cleared once the pages are replaced or a new extraction runs), `COMPANY_PAGE_UNVERIFIED` (create / PATCH responses only: a page in `linkedinCompanyUrls` could not be verified at that moment and was accepted; `values` lists them — if one does not exist, its extraction stops without using credits and the signal carries `COMPANY_PAGE_UNAVAILABLE`).
    - `severity` string, required: `info`: nothing to do. `warning`: probably not what you meant.
    - `field` string, required: The `config` key concerned, camelCase (or `dailyLeadLimit`).
    - `message` string, required
    - `values` string[]: The offending values, when there are some.

## Other responses

- `400`: Missing required config fields when creating as active
- `401`: Missing or invalid API key
- `402`: Account has no credits
- `422`: Validation Error
- `429`: Rate limit exceeded. Retry after the number of seconds in the `Retry-After` response header.

---

[API](https://skmtc.dev/rodz/apis/rodz-api.md) · [All operations](https://skmtc.dev/rodz/apis/rodz-api/llms.txt) · [OpenAPI document](https://skmtc.dev/rodz/apis/rodz-api/revisions/3003505ff896?raw)
