Monitors
A monitor watches companies for new signals. Every morning at 09:00 UTC it checks each of its companies for each signal type it watches, and delivers what is new: by email (a daily digest to the workspace owner) or to your webhook. Everything it delivered stays readable in Signals.
Signal types: job_openings (hiring), financing_events (funding rounds), news_events (news) and connections (partnerships).
Cost#
Each company × signal type checked costs 1 credit per daily run, so a monitor watching 5 companies for hiring and funding costs 10 credits a day, every day, until you pause or delete it. A check that could not be made is refunded. A monitor's estimated_daily_cost is that number. If the workspace runs out of credits, its monitors pause themselves (paused_no_credits) and resume at the next run after you top up. A workspace can have up to 10 active monitors.
Who can do what#
Any token can read monitors and their runs (read). Creating, changing, pausing or deleting one, or changing its companies, needs a read_write token and its holder must be an owner or admin of the workspace, checked on every request: a token never outranks the person who made it.
Create a monitor#
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Your name for the monitor. |
signal_types | enum[] | Yes | What to watch for, one or more of: job_openings (hiring), funding rounds, news, and connections (partnerships). |
delivery_method | enum | No | How matched signals reach you. 'email' (default) sends a digest to the workspace owner once per run — no setup required. 'webhook' POSTs each match to an HTTPS endpoint you operate and requires webhook_id; that is the developer path. One of: email, webhook. |
webhook_id | string | No | Required when delivery_method='webhook'. Must be omitted for 'email': a monitor cannot carry a webhook it will never use. |
domains | string[] | No | The companies to watch, by domain, e.g. acme.com. At least one. |
job_opening_filters | JobOpeningFilters | No | Narrow hiring signals to some roles. Ignored unless job_openings is watched. |
slack_connection_id | string | No | Also post each run's signals to this Slack channel. Channels are connected in the app; a monitor's slack_connection_id shows one. |
job_opening_filters narrows hiring signals to some roles:
| Field | Type | Required | Description |
|---|---|---|---|
title_keyword | string | No | Only openings whose job title contains this text, e.g. sales. |
seniority | string[] | No | Only these seniority levels: founder, c_level, partner, president, vice_president, head, director, manager, mid_senior, junior. |
categories | string[] | No | Only these job categories, e.g. sales, marketing, engineering. |
curl -X POST https://api.cornect.io/api/v1/monitors \
-H "Authorization: Bearer cornect_your_token_here" \
-H "Content-Type: application/json" \
-d '{"name":"Competitors hiring sales","signal_types":["job_openings","financing_events"],"domains":["acme.com","globex.com"],"job_opening_filters":{"title_keyword":"sales"}}'The response is the monitor (below), with status 201. This one costs 4 credits a day: 2 companies × 2 signal types.
List and get monitors#
List returns { items, total }; Get returns one monitor with its companies and its last 7 runs.
{
"id": "3f1c2a9e-8b7d-4c6e-9a1b-2d3e4f5a6b7c",
"name": "Competitors hiring sales",
"signal_types": ["job_openings", "financing_events"],
"delivery_method": "email",
"webhook_id": null,
"job_opening_filters": { "title_keyword": "sales", "seniority": [], "categories": [] },
"status": "active",
"last_run_at": "2026-10-11T09:00:34Z",
"last_run_status": "success",
"last_error": null,
"domains": [
{ "domain": "acme.com", "added_at": "2026-10-10T14:02:11Z" },
{ "domain": "globex.com", "added_at": "2026-10-10T14:02:11Z" }
],
"estimated_daily_cost": 4,
"recent_runs": [],
"created_at": "2026-10-10T14:02:11Z",
"updated_at": "2026-10-10T14:02:11Z"
}| Field | Type | Description |
|---|---|---|
id | string | The monitor's id (monitor_id in paths). |
workspace_id | string | |
created_by | string | The user who created it. |
name | string | |
signal_types | string[] | What it watches for. |
delivery_method | string | email (a daily digest to the workspace owner) or webhook. |
webhook_id | string | The webhook it delivers to; null for email. |
job_opening_filters | JobOpeningFilters | Role filters for hiring signals, if any. |
list_id | string | Set when the monitor watches a list from the app: its companies are the list's, and are changed there. |
list_name | string | That list's name. |
slack_connection_id | string | The Slack channel it also posts to, if any. |
slack_channel | string | That channel's name. |
status | string | active, paused_by_user, or paused_no_credits (the workspace ran out of credits; it resumes by itself once there are credits again). |
last_run_at | string | |
last_run_status | string | How its last run ended, as in a run's status. |
last_error | string | Why it last stopped, if it did. |
domains | SignalCronJobDomainResponse[] | The companies it watches. |
estimated_daily_cost | integer | Credits each daily run costs: companies × signal types. |
recent_runs | SignalCronRunSummary[] | Its last 7 runs, newest first. |
created_at | string | |
updated_at | string |
Update, pause and resume#
Send only the fields to change. Pause with {"status": "paused_by_user"} (no runs, no charges); resume with {"status": "active"}. A monitor paused for lack of credits cannot be resumed by hand: it resumes at the next run after a top-up.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | A new name. |
signal_types | enum[] | No | The full new list of what to watch for (it replaces the old one). |
webhook_id | string | No | Deliver to this webhook instead. |
delivery_method | enum | No | Switch delivery. Moving to webhook needs webhook_id in the same request. One of: email, webhook. |
status | enum | No | paused_by_user pauses the monitor (no runs, no charges); active resumes it. One of: active, paused_by_user. |
job_opening_filters | JobOpeningFilters | No | New role filters for hiring signals. Send {} to clear them. |
slack_connection_id | string | No | Post to this Slack channel too. null stops posting to Slack; leave it out to keep it as is. |
curl -X PATCH https://api.cornect.io/api/v1/monitors/3f1c2a9e-8b7d-4c6e-9a1b-2d3e4f5a6b7c \
-H "Authorization: Bearer cornect_your_token_here" \
-H "Content-Type: application/json" \
-d '{"status": "paused_by_user"}'Add and remove companies#
Add one company per call, with body {"domain": "initech.com"}; its first check is at the next morning run, and the daily cost rises by 1 credit per signal type. Removing answers 204. A monitor that watches a list from the app takes its companies from that list, so both are refused with DOMAINS_FOLLOW_LIST.
curl -X POST https://api.cornect.io/api/v1/monitors/3f1c2a9e-8b7d-4c6e-9a1b-2d3e4f5a6b7c/domains \
-H "Authorization: Bearer cornect_your_token_here" \
-H "Content-Type: application/json" \
-d '{"domain": "initech.com"}'
curl -X DELETE https://api.cornect.io/api/v1/monitors/3f1c2a9e-8b7d-4c6e-9a1b-2d3e4f5a6b7c/domains/initech.com \
-H "Authorization: Bearer cornect_your_token_here"Delete a monitor#
Deletes the monitor and its run history, and answers 204. Signals it already delivered stay in Signals. To stop the charges for a while instead, pause it.
Runs#
A monitor's daily runs, newest first (limit 1–200, offset; the response carries has_more), and one run check by check: each company and signal type, what it found, any error, and whether its credit was refunded.
| Field | Type | Description |
|---|---|---|
id | string | |
run_date | string | The UTC day of the run. |
status | string | running, success, partial (some checks failed or credits ran out) or failed. |
started_at | string | |
completed_at | string | |
domains_total | integer | Checks to make: companies × signal types. |
domains_processed | integer | Checks made. |
domains_skipped_credits | integer | Checks skipped because the workspace ran out of credits. |
domains_failed | integer | Checks that could not be made (each one refunded). |
credits_charged | integer | Credits charged: 1 per check. |
credits_refunded | integer | Credits refunded for checks that could not be made. |
new_items_total | integer | New signals delivered. |
Each check in a run's run_domains:
| Field | Type | Description |
|---|---|---|
domain | string | The company. |
signal_type | string | What was checked for. |
status | string | success, skipped_credits, or an error (error_pl, error_other). |
error_code | string | Why the check could not be made, if it could not. |
items_fetched | integer | Signals found for the company. |
items_new | integer | Of those, how many were new to this monitor. |
items_pushed | integer | New signals delivered. |
credit_charged | boolean | |
credit_refunded | boolean | |
started_at | string | |
completed_at | string |
Errors#
| Code | Status | When |
|---|---|---|
INSUFFICIENT_SCOPE | 403 | A read token tried to change a monitor. |
WORKSPACE_EDIT_FORBIDDEN | 403 | The token's holder is not an owner or admin. A new token will not help. |
NOT_FOUND | 404 | No such monitor (or run) in your workspace. |
WORKSPACE_CRON_JOB_LIMIT | 409 | The workspace already has 10 active monitors. |
DOMAIN_ALREADY_ACTIVE | 409 | The monitor already watches that company. |
DOMAINS_FOLLOW_LIST | 409 | The monitor watches a list; change its companies in the list. |
STATUS_TRANSITION_FORBIDDEN | 409 | Resuming a monitor paused for lack of credits. |
WEBHOOK_REQUIRED | 422 | Webhook delivery without a webhook_id. |
WEBHOOK_NOT_FOUND | 422 | That webhook is not in your workspace. |
| — | 422 | Any other invalid field: the message names it. |
| — | 429 | Rate limit exceeded (120/min to read, 60/min to change). |
A monitor can deliver to a webhook, and also post to a Slack channel, that you set up in the Cornect app; pass its id here. A monitor shows the ids it uses.