Signals

A signal is waiting on you

Know the moment a signal stops producing until you act — and act from your own system. Some signals need your yes before they spend credits: a New Decision Maker or Account watchlist whose companies must be matched to their LinkedIn company page at a cost above what we match without asking (MATCHING_CONFIRMATION_REQUIRED), and a Company Followers signal in icp mode whose follower extraction must be quoted and accepted (QUOTE_REQUIRED). A Company Followers signal can also stop because a LinkedIn company page it names does not exist (COMPANY_PAGE_UNAVAILABLE): nothing is charged, and apiEndpoint is the PATCH that replaces the page. That state often appears after you created the signal — once a large company list has been read, once a quote is ready — so this event tells you instead of leaving you to poll.

When: once per ask (a new quote is a new ask), after we review it. At the same time the account owners get an email with the quote and a link to confirm in the dashboard, and one reminder email 3 days later if it is still pending; the event itself is not repeated.

Output: event, eventId, signalId, signalName, signalTypeId, code, createdAt, message, quote (the price of saying yes), confirmUrl (dashboard) and apiEndpoint (method, url, body of the call that unblocks it).

Use cases:

  • Approve automatically under your own budget: call apiEndpoint when quote.estimatedCost (or quote.credits) is below your threshold.
  • Route the decision to a person: post message and confirmUrl to the account owner's Slack or ticket queue.
  • Keep your UI honest: show the signal as "waiting on you" until signal.action_resolved arrives. GET /api/v1/signals?actionRequired=true gives the same list on demand.

Your signal's webhookUrl receives leads, and also these events. Every event carries a top-level event key, which no lead has: route on it, and ignore event types you do not handle — new ones may be added. Signed exactly like leads (X-Webhook-Signature, X-Webhook-Timestamp) when the signal has a webhook secret. An event is not a lead: it is free and never counted as a delivered lead.

postWebhooksignal.action_required

Payload

eventstring required

Always signal.action_required.

eventIdstring required

Unique id of this ask. Also sent as the X-Webhook-Id header; use it to ignore a duplicate delivery.

signalIdstring required

The signal waiting on you.

signalNamestring required

Its name.

signalTypeIdstring required

Its type, e.g. new_decision_maker_watchlist.

codestring required

What it waits for: MATCHING_CONFIRMATION_REQUIRED (a New Decision Maker or Account watchlist whose companies must be matched to their LinkedIn company page, at a cost that needs your confirmation), QUOTE_REQUIRED (a Company Followers signal in icp mode whose follower extraction must be quoted and the quote accepted) or COMPANY_PAGE_UNAVAILABLE (a Company Followers signal whose extraction stopped because a LinkedIn company page in linkedinCompanyUrls does not exist: replace the page). The same code as in the signal's warnings.

createdAtstring required

When the event was sent, ISO 8601 UTC.

messagestring required

The same plain-language explanation as the signal's warning.

quoteobject required

The price of saying yes. For MATCHING_CONFIRMATION_REQUIRED: the response of POST /api/v1/watchlists/{signalId}/companies/confirm-matching with confirm: false (companiesToMatch, alreadyMatchedFree, billableCompanies, pricePerCompany, currency, estimatedCost — an upper bound — sufficientBalance, ...). For QUOTE_REQUIRED: state (awaiting_acceptance, not_requested, expired or failed), credits and followers of the quote waiting for acceptance (else null), expiresAt and currency. For COMPANY_PAGE_UNAVAILABLE: state (company_page_unavailable), companyUrls (the pages that do not exist; empty when we cannot tell which of the signal's pages it is), credits (always 0: nothing was charged) and currency.

confirmUrlstring required

The dashboard page that shows the quote with its confirm button.

Example payload

{
  "apiEndpoint": {
    "body": {
      "confirm": true
    },
    "method": "POST",
    "url": "https://api.rodz.io/api/v1/watchlists/d4bab73d-5c1e-4b7a-9f2e-3a6b8c0d1e2f/companies/confirm-matching"
  },
  "code": "MATCHING_CONFIRMATION_REQUIRED",
  "confirmUrl": "https://app.rodz.io/signals/d4bab73d-5c1e-4b7a-9f2e-3a6b8c0d1e2f/configure",
  "createdAt": "2026-10-09T09:16:02+00:00",
  "event": "signal.action_required",
  "eventId": "5f0c2a4e-8b1d-4c3e-9a7f-2d6e1b0c9a84",
  "message": "12,000 companies on this list must be matched to their LinkedIn company page before they can be watched, for at most 600.0 credits (0.05 per company; companies already matched are free). Nothing is matched or charged until you confirm with POST /api/v1/watchlists/d4bab73d-5c1e-4b7a-9f2e-3a6b8c0d1e2f/companies/confirm-matching and {\"confirm\": true}.",
  "quote": {
    "alreadyMatchedFree": 0,
    "billableCompanies": 12000,
    "companiesToMatch": 12000,
    "confirmationRequired": true,
    "currency": "credits",
    "estimatedCost": 600,
    "isUpperBound": true,
    "pricePerCompany": 0.05,
    "status": "awaiting_confirmation",
    "sufficientBalance": true
  },
  "signalId": "d4bab73d-5c1e-4b7a-9f2e-3a6b8c0d1e2f",
  "signalName": "Key accounts - new decision makers",
  "signalTypeId": "new_decision_maker_watchlist"
}

Response

Any 2xx acknowledges the event. Other answers are recorded in your webhook deliveries; they never pause lead delivery.

Changes

Webhooks have no change history: a webhook is keyed by its event name rather than a path and method, so no recorded change can name it.