# TAMradar Agent Instructions
Source: https://docs.tamradar.com/agentic/tamradar-agent
Agent instruction file for setting up, retrieving, and managing TAMradar monitoring.
**๐ค For humans**
Give this to your agent and it will set up TAMradar monitoring for you โ create radars, check balance, retrieve findings.
**Give this link to your agent:** [https://docs.tamradar.com/agentic/tamradar-agent.md](https://docs.tamradar.com/agentic/tamradar-agent.md)
***
***
You set up TAMradar monitoring for a user. Read this file top to bottom before starting.
**Base URL:** `https://api.tamradar.com`
**Auth header:** `x-api-key: {TAMRADAR_API_KEY}` โ required on every request. No Bearer prefix.
***
## Authentication
Check the environment for `TAMRADAR_API_KEY`. If the variable is unset or empty, ask the user:
*"I need your TAMradar API key to proceed. You can find it in your account settings at tamradar.com."*
Never guess or construct the key. Do not proceed without it.
**Canonical request format โ use this exact pattern for every call:**
```bash theme={null}
curl -s -X POST "https://api.tamradar.com/v1/radars/companies" \
-H "x-api-key: $TAMRADAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "domain": "openai.com", "radar_type": "company_job_openings" }'
```
`x-api-key` is the auth header. `Content-Type: application/json` is required on all POST requests.
***
## How this works โ overview
```
1. PRE-FLIGHT GET /v1/account โ check balance, block if $0
2. CREATE POST /v1/radars/companies โ company signals
POST /v1/radars/contacts โ person signals
POST /v1/radars/industry โ industry-wide signals
POST /v1/radars/bulk โ >10 targets: async queue (max 1000 items per request)
3. RETRIEVE GET /v1/updates โ poll for findings (always available)
[webhook] โ push delivery (faster, if webhook_url set)
4. MANAGE DELETE /v1/radars/:id โ stop monitoring a target
```
Detailed breakdown follows.
***
## Inputs โ collect before starting
| Input | Required | Notes |
| ------------------ | --------- | --------------------------------------------------------------------------------------- |
| `TAMRADAR_API_KEY` | MUST have | UUID format โ check env first, ask if unset |
| Target list | MUST have | Companies (domain), people (name + LinkedIn URL + company domain), or industry keywords |
| `webhook_url` | Optional | Omit for poll-only. Add it for real-time push delivery. |
### Webhook URL โ default to polling
`webhook_url` is optional. **Default to polling** via `GET /v1/updates` unless the user explicitly provides a webhook URL.
**If no webhook URL provided:** omit `webhook_url` entirely โ the radar will be poll-only. Retrieve findings via `GET /v1/updates`. No need to ask.
**If user provides a real webhook URL** (Make.com, Clay, Zapier, n8n): include it. Real-time delivery will work automatically.
โ ๏ธ Once set, `webhook_url` cannot be changed without deactivating and recreating the radar. Avoid ephemeral test URLs like `webhook.site` for production radars.
***
## Phase 1: Pre-flight โ ALWAYS run first
```
GET /v1/account
```
**Sample response:**
```json theme={null}
{
"status": "success",
"code": 200,
"data": {
"balance_remaining_usd": 42.50,
"account": {
"active_radars": 12,
"inactive_radars": 2,
"failed_radars": 0,
"total_radars": 14,
"total_updates_found": 3847
},
"usage": {
"month": "2026-05",
"radars_activated": 3,
"radars_deactivated": 0,
"radars_failed": 0,
"balance_used_usd": 7.50,
"updates_found": 142
}
}
}
```
**Balance gate โ enforce strictly:**
* IF `data.balance_remaining_usd == 0` โ **STOP.** Tell user: *"Your TAMradar balance is \$0. Add funds at tamradar.com before I can create any radars."*
* IF `data.balance_remaining_usd < 5` โ **WARN.** Tell user the balance, ask if they want to continue.
* IF `data.balance_remaining_usd >= 5` โ Proceed. Report balance to user.
### Recommend: balance monitoring cron (to user only โ do not auto-install)
Tell the user:
> *"To avoid radars being deactivated unexpectedly, I recommend setting up a daily job that calls `GET /v1/account` and alerts you if `balance_remaining_usd` drops below a threshold (e.g. $10). TAMradar auto-deactivates radars when balance hits $0 and sends a `radar_failure` webhook โ but proactive monitoring is better than reacting after the fact."*
Only suggest this. Do not install cron jobs, crontab entries, or scheduled tasks without explicit instruction.
***
## Phase 2: Create radars
### Idempotency โ check local store first
Before every POST, look up `(domain, radar_type)` in `./tamradar-radars.jsonl` (your local mapping store). If a matching entry exists, skip the POST and use the stored `radar_id`. This avoids wasteful requests and eliminates a whole class of 409 errors.
### Singles vs bulk
* **โค10 targets** โ create one by one (single POSTs). Simple, easy to debug.
* **>10 targets** โ use `POST /v1/radars/bulk` (async queue, max 1000 items per request).
### Single radar creation
**`custom_fields` recommendation:** always pass your internal IDs here. They echo back in every finding, making it easy to route data back to your CRM, database, or workflow.
```json theme={null}
"custom_fields": {
"crm_account_id": "0011U00000TFV7MQAX",
"owner": "alice@yourcompany.com",
"label": "enterprise-tier"
}
```
#### Company radar
```bash theme={null}
curl -s -X POST "https://api.tamradar.com/v1/radars/companies" \
-H "x-api-key: $TAMRADAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": "openai.com",
"radar_type": "company_job_openings",
"webhook_url": "https://hook.make.com/your-scenario-url",
"custom_fields": { "crm_account_id": "0011U00000TFV7MQAX" }
}'
```
#### Contact radar
```bash theme={null}
curl -s -X POST "https://api.tamradar.com/v1/radars/contacts" \
-H "x-api-key: $TAMRADAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"radar_type": "contact_job_changes",
"domain": "openai.com",
"profile_url": "https://www.linkedin.com/in/samaltman",
"full_name": "Sam Altman",
"webhook_url": "https://hook.make.com/your-scenario-url",
"custom_fields": { "crm_contact_id": "003Dn00000AHtLQIA1" }
}'
```
#### Industry radar
```bash theme={null}
curl -s -X POST "https://api.tamradar.com/v1/radars/industry" \
-H "x-api-key: $TAMRADAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"radar_type": "industry_funding_rounds",
"webhook_url": "https://hook.make.com/your-scenario-url",
"custom_fields": { "label": "ai-funding-watch" }
}'
```
### โ ๏ธ CRITICAL: persist the mapping after every 201
Store immediately to `./tamradar-radars.jsonl` (append one JSON object per line):
```json theme={null}
{ "input_domain": "openai.com", "input_radar_type": "company_job_openings", "radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b", "created_at": "2026-05-01T09:05:00Z" }
```
This file IS your monitoring state. Without it you cannot poll by specific radar, deactivate targets, or check idempotency before creating.
### Bulk creation โ for >10 targets
Send up to 1000 radars per request. `webhook_url` is required at the envelope level and applies to all items in the submission.
```bash theme={null}
curl -s -X POST "https://api.tamradar.com/v1/radars/bulk" \
-H "x-api-key: $TAMRADAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhook_url": "https://hook.make.com/your-scenario-url",
"radars": [
{ "domain": "openai.com", "radar_type": "company_job_openings", "custom_fields": { "crm_account_id": "001" } },
{ "domain": "anthropic.com", "radar_type": "company_new_hires", "custom_fields": { "crm_account_id": "002" } }
]
}'
```
**Bulk submission response โ HTTP 202 accepted:**
* Response returns `bulk_id` and submission summary only.
* Item-level outcomes are asynchronous and arrive via:
* webhook events (`radar_created` / `radar_failure` / `bulk_completed`)
* `GET /v1/radars/bulk/{bulk_id}` polling
***
## Phase 3: Error handling
MUST handle every error before reporting success.
| Code | Meaning | Action |
| ----- | -------------------- | --------------------------------------------------------------------------------------------------- |
| `401` | Invalid API key | STOP. Ask user to verify key. |
| `402` | Insufficient balance | STOP. Tell user to add funds, then retry. |
| `409` | Radar already exists | NOT an error โ it's already running. Extract `radar_id` from `errors[0].reason`. Store it. Move on. |
| `400` | Validation error | Show `errors[].field` + `errors[].reason` verbatim. Ask user to correct. |
| `429` | Rate limited | Wait, then retry. NEVER retry more than 3 times. |
| `5xx` | Server error | Retry once. If still failing, report `error_id` to user and STOP. |
***
## Phase 4: Retrieve findings (polling)
`GET /v1/updates` returns the same data as webhooks. The response uses `updates[]` โ not `data[]`.
**Note:** findings are retained for 60 days. Data older than 60 days is purged.
**Note:** `radar_failure` events are excluded by default. Default `update_type` is `radar_finding` only.
**Sample response:**
```json theme={null}
{
"status": "success",
"code": 200,
"updates": [
{
"update_id": "550e8400-e29b-41d4-a716-446655440000",
"update_type": "radar_finding",
"discovered_at": "2026-05-01T08:30:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_job_openings",
"domain": "openai.com",
"custom_fields": { "crm_account_id": "001" }
},
"content": { }
}
],
"has_more": false,
"next_cursor": null
}
```
### Fetch findings for specific radars (max 10 radar\_ids per call)
```
GET /v1/updates?radar_id={id1},{id2}&limit=50
```
If you have more than 10 radars, split into multiple calls of up to 10 IDs each.
### Fetch by signal type
```
GET /v1/updates?radar_type=company_job_openings,company_new_hires&limit=50
```
### Incremental โ only new since last check
```
GET /v1/updates?since={last_discovered_at}&limit=100
```
ALWAYS store `discovered_at` from the last update you processed. Pass it as `since` on the next call.
### Pagination
IF `has_more: true` โ fetch next page:
```
GET /v1/updates?cursor={next_cursor}
```
Repeat until `has_more: false`.
### To include radar failures in polling
```
GET /v1/updates?update_type=radar_finding,radar_failure
```
### Presenting findings to the user
Group by `data.radar_type`. Always include `discovered_at`. Example:
> **New job at openai.com** โ Senior Research Engineer, Alignment
> Detected: 2026-04-29 ยท [View posting](https://openai.com/careers/...)
***
## Phase 5: Deactivate radars
```bash theme={null}
curl -s -X DELETE "https://api.tamradar.com/v1/radars/{radar_id}" \
-H "x-api-key: $TAMRADAR_API_KEY"
```
* Irreversible. Must create a new radar to resume monitoring.
* No further charges after deactivation.
IF user says "stop monitoring openai.com for job openings" โ look up `radar_id` from `./tamradar-radars.jsonl` and DELETE it.
***
## Phase 6: Retrieving findings
**Default: use polling.** `GET /v1/updates` always works regardless of `webhook_url`. Findings are available immediately after creation and retained for 60 days.
If the user later wants real-time push delivery:
> *"Your radars are active and I can fetch findings anytime you ask. For real-time delivery into Slack, your CRM, or an automation, connect a tool like Make.com or Clay โ it generates a webhook URL you can give me and I'll update the setup."*
***
## Summary template โ ALWAYS present when done
```
TAMradar setup complete.
Balance: $42.50 remaining
Radars active:
company_job_openings ยท openai.com ยท c70813b3-7e87-4ca7-90fd-8c64574d911b
contact_job_changes ยท Sam Altman ยท a4f28c91-3b12-4e70-9d55-7e3dda58f100 (already existed โ skipped POST)
industry_funding_rounds ยท โ ยท f9d01c55-82ba-4f3e-b461-2a9e7c11d830
Failures:
None
Mapping saved to:
./tamradar-radars.jsonl
To fetch findings now:
GET /v1/updates?radar_id=c70813b3-...,a4f28c91-...&limit=50
(max 10 IDs per call โ split into multiple requests if needed)
Findings retained for 60 days.
Webhook: Make.com scenario (https://hook.make.com/...) โ
โ OR โ
Polling only. Ask me anytime to check for new findings.
```
***
## Endpoint reference (machine-readable)
Fetch these as raw markdown for full schema details:
* **Company radar:** `https://docs.tamradar.com/api-reference/create-company-radar.md`
* **Contact radar:** `https://docs.tamradar.com/api-reference/create-contact-radar.md`
* **Industry radar:** `https://docs.tamradar.com/api-reference/create-industry-radar.md`
* **Poll updates:** `https://docs.tamradar.com/api-reference/poll-updates.md`
* **Account:** `https://docs.tamradar.com/api-reference/account.md`
***
## Radar type reference
### Company โ `POST /v1/radars/companies`
Required: `domain`, `radar_type` โ `webhook_url` optional (omit for poll-only)
| `radar_type` | What it tracks |
| ---------------------------- | ----------------------------- |
| `company_job_openings` | New job postings |
| `company_new_hires` | People joining the company |
| `company_promotions` | Internal promotions |
| `company_reviews` | Glassdoor/employer reviews |
| `company_mentions` | Web mentions of the company |
| `company_social_posts` | Company LinkedIn/social posts |
| `company_social_posts_cxo` | Executive social posts |
| `company_social_engagements` | Engagements on company posts |
#### Domain normalization
The API accepts any format โ bare domain, URL, or with www/path/port. All are normalized automatically:
* `openai.com` โ
* `https://www.openai.com/about` โ
โ normalized to `openai.com`
* `www.openai.com` โ
โ normalized to `openai.com`
Best practice: pass the bare domain (`openai.com`).
### Contact โ `POST /v1/radars/contacts`
Required: `radar_type` + type-specific identifiers โ `webhook_url` optional (omit for poll-only):
| `radar_type` | Additional required fields |
| ---------------------------- | ---------------------------------------------------------------------------- |
| `contact_job_changes` | `domain` + at least one of: `email`, `profile_url`, `full_name` |
| `contact_social_engagements` | `profile_url` (must contain `linkedin.com/in/`, `x.com/`, or `twitter.com/`) |
| `contact_social_posts` | `profile_url` (same URL formats) |
#### Profile URL normalization
Pass the full URL. The API normalizes automatically:
* `https://www.linkedin.com/in/samaltman` โ
(preferred)
* `https://linkedin.com/in/samaltman` โ
โ normalized to `https://www.linkedin.com/in/samaltman`
* `linkedin.com/in/samaltman` โ
โ `https://` added automatically
* Trailing slash stripped automatically
Uniqueness for contacts is keyed on `profile_url` + radar type โ different radar types for the same person ARE allowed.
### Industry โ `POST /v1/radars/industry`
Required: `radar_type`. No `domain` field โ `webhook_url` optional (omit for poll-only).
| `radar_type` | Additional required fields |
| ------------------------- | -------------------------------------------------------- |
| `industry_funding_rounds` | None |
| `industry_new_companies` | None |
| `industry_mentions` | `keyword` (min 3 chars) |
| `industry_job_openings` | `keyword` (min 3 chars) + optional `countries[]` (max 5) |
***
## Key fields reference
| Field | Found in | Purpose |
| ----------------- | -------------------------------------- | ----------------------------------------------------------------- |
| `radar_id` | Creation response โ `data.radar_id` | MUST store with input. Used for polling, deactivation. |
| `update_id` | Every finding payload | Deduplicate on this. NEVER process the same `update_id` twice. |
| `discovered_at` | Every finding payload | Store latest value. Use as `since` param for incremental polling. |
| `custom_fields` | Request body โ echoed in every finding | Your internal IDs. Route findings back to your CRM using these. |
| `data.radar_type` | Every finding payload | Route your processing logic on this. |
| `content` | Every finding payload | The actual finding. Shape varies by `radar_type`. |
***
## Hard limits
* `webhook_url` is optional โ omit for poll-only radars; include only when real-time push delivery is needed
* Rate limit: **100 write requests / 60 seconds**, **200 read requests / 60 seconds** โ need more? contact [support@tamradar.com](mailto:support@tamradar.com)
* Bulk max: **1000 radars** per request
* Polling `radar_id` filter: **max 10 IDs** per call โ split into multiple requests if you have more
* Polling data retention: **60 days** โ older data is purged
* One radar per `domain + radar_type` per account โ duplicates get `409`
* Contact uniqueness: per `profile_url + radar_type` โ different contact radar types for the same person ARE allowed
* Persistence: save mappings to `./tamradar-radars.jsonl` by default
# TAMradar Monitoring
Source: https://docs.tamradar.com/agentic/tamradar-skill
Skill file for coding agents to configure TAMradar radars and retrieval workflows.
**๐ค For humans**
This is a Claude Code skill file. Save it as `.claude/skills/tamradar/SKILL.md` in your project. Claude Code will load it automatically when you mention TAMradar, or invoke it directly with `/tamradar`.
**Give this link to your agent:** [https://docs.tamradar.com/agentic/tamradar-skill.md](https://docs.tamradar.com/agentic/tamradar-skill.md)
***
# TAMradar Monitoring
**Full instructions:** [https://docs.tamradar.com/agentic/tamradar-agent.md](https://docs.tamradar.com/agentic/tamradar-agent.md)
**Base URL:** `https://api.tamradar.com` ยท **Auth:** `x-api-key: $TAMRADAR_API_KEY` on every request โ no Bearer prefix
## Key Rules
* **Auth:** check `$TAMRADAR_API_KEY` in env first โ if unset, ask the user
* **webhook\_url:** optional โ omit for poll-only radars (default); include only if the user provides a real webhook URL for push delivery
* **Persistence:** save `(domain, radar_type, radar_id)` to `./tamradar-radars.jsonl` after every 201
* **Idempotency:** check `./tamradar-radars.jsonl` before every POST โ skip if already exists
* **Singles vs bulk:** โค10 targets โ single POSTs; >10 โ `POST /v1/radars/bulk` (async queue, max 1000 items per request)
* **409 is not an error** โ extract `radar_id` from `errors[0].reason`, store it, move on
* **Polling response:** field is `updates[]` โ not `data[]`
* **Polling cap:** max 10 `radar_id` values per call โ split into multiple requests if needed
## Flow
```
1. PRE-FLIGHT โ GET /v1/account (check balance โ block if $0)
2. CREATE โ POST /v1/radars/companies (company signals)
POST /v1/radars/contacts (person signals)
POST /v1/radars/industry (industry-wide signals)
POST /v1/radars/bulk (>10 targets)
3. RETRIEVE โ GET /v1/updates
4. MANAGE โ DELETE /v1/radars/:id
```
## Endpoint reference (machine-readable)
* `https://docs.tamradar.com/api-reference/create-company-radar.md`
* `https://docs.tamradar.com/api-reference/create-contact-radar.md`
* `https://docs.tamradar.com/api-reference/create-industry-radar.md`
* `https://docs.tamradar.com/api-reference/poll-updates.md`
* `https://docs.tamradar.com/api-reference/account.md`
## Pre-flight
```bash theme={null}
curl -s "https://api.tamradar.com/v1/account" -H "x-api-key: $TAMRADAR_API_KEY"
```
```json theme={null}
{
"data": {
"balance_remaining_usd": 42.50,
"account": { "active_radars": 12, "total_updates_found": 3847 },
"usage": { "month": "2026-05", "balance_used_usd": 7.50, "updates_found": 142 }
}
}
```
`balance_remaining_usd == 0` โ STOP ยท `< 5` โ WARN and confirm ยท `>= 5` โ proceed
## Create โ company
```bash theme={null}
curl -s -X POST "https://api.tamradar.com/v1/radars/companies" \
-H "x-api-key: $TAMRADAR_API_KEY" -H "Content-Type: application/json" \
-d '{
"domain": "openai.com",
"radar_type": "company_job_openings",
"custom_fields": { "crm_account_id": "001" }
}'
```
Domain accepts any format (`https://www.openai.com/about` โ normalized to `openai.com`). Best practice: bare domain.
## Create โ contact
```bash theme={null}
curl -s -X POST "https://api.tamradar.com/v1/radars/contacts" \
-H "x-api-key: $TAMRADAR_API_KEY" -H "Content-Type: application/json" \
-d '{
"radar_type": "contact_job_changes",
"domain": "openai.com",
"profile_url": "https://www.linkedin.com/in/samaltman",
"full_name": "Sam Altman",
"custom_fields": { "crm_contact_id": "003" }
}'
```
## Create โ industry
```bash theme={null}
curl -s -X POST "https://api.tamradar.com/v1/radars/industry" \
-H "x-api-key: $TAMRADAR_API_KEY" -H "Content-Type: application/json" \
-d '{ "radar_type": "industry_funding_rounds" }'
```
## Persist after every 201
```bash theme={null}
echo '{ "input_domain": "openai.com", "input_radar_type": "company_job_openings", "radar_id": "c70813b3-...", "created_at": "2026-05-01T09:05:00Z" }' \
>> ./tamradar-radars.jsonl
```
## Poll for findings
```bash theme={null}
# By radar ID (max 10 IDs)
curl -s "https://api.tamradar.com/v1/updates?radar_id=id1,id2&limit=50" \
-H "x-api-key: $TAMRADAR_API_KEY"
# Incremental โ store discovered_at from last result, pass as since
curl -s "https://api.tamradar.com/v1/updates?since=2026-05-01T08:00:00Z&limit=100" \
-H "x-api-key: $TAMRADAR_API_KEY"
```
```json theme={null}
{
"updates": [
{
"update_id": "550e8400-...",
"update_type": "radar_finding",
"discovered_at": "2026-05-01T08:30:00Z",
"data": { "radar_id": "c70813b3-...", "radar_type": "company_job_openings", "domain": "openai.com" },
"content": { }
}
],
"has_more": false,
"next_cursor": null
}
```
Paginate: while `has_more: true` โ fetch `?cursor={next_cursor}`. Findings retained **60 days**.
## Error handling
| Code | Action |
| ----- | ----------------------------------------------------------- |
| `401` | STOP โ ask user to verify API key |
| `402` | STOP โ tell user to add funds |
| `409` | Extract `radar_id` from `errors[0].reason`, store, continue |
| `400` | Show `errors[].reason` verbatim, ask user to fix |
| `429` | Wait, retry max 3ร |
| `5xx` | Retry once, then STOP and report `error_id` |
## Summary (always present when done)
```
TAMradar setup complete.
Balance: $X.XX remaining
Radars active:
company_job_openings ยท openai.com ยท c70813b3-...
contact_job_changes ยท Sam Altman ยท a4f28c91-... (already existed โ skipped POST)
industry_funding_rounds ยท โ ยท f9d01c55-...
Failures: None
Mapping saved to: ./tamradar-radars.jsonl
Poll now: GET /v1/updates?radar_id=c70813b3-...,a4f28c91-...&limit=50
(max 10 IDs per call โ split if needed)
Findings retained 60 days.
```
# Account Summary
Source: https://docs.tamradar.com/api-reference/account
/api_specs.yaml GET /v1/account
Get usage and balance summary for the authenticated account.
# Get Bulk Create Status
Source: https://docs.tamradar.com/api-reference/bulk-status
/api_specs.yaml GET /v1/radars/bulk/{bulk_id}
Poll async bulk submission status and per-item outcomes.
## How bulk status works
After submitting a bulk request, the response transitions through two states:
* **`processing`** โ At least one item is still being created. `summary.processing` shows how many are in flight.
* **`completed`** โ All items have reached a terminal state (`created` or `failed`). No more changes.
Poll this endpoint until `status = "completed"`, or wait for the `bulk_completed` webhook instead.
## Per-item fields
Each entry in `radars[]` mirrors the payload delivered by the per-item webhook for that item:
| Field | Present when | Description |
| ------------------- | -------------- | ------------------------------------------------------------------------------- |
| `item_index` | Always | Zero-based position in the original `radars[]` array |
| `custom_fields` | Always | Custom fields passed on the item |
| `status` | In-flight only | `"processing"` โ item not yet terminal |
| `update_id` | Terminal | Correlation ID linking to the item's `radar_created` or `radar_failure` webhook |
| `update_type` | Terminal | `radar_created` (success) or `radar_failure` (failure) |
| `completed_at` | Terminal | ISO timestamp when the item finished processing |
| `bulk_id` | Terminal | Parent bulk submission ID |
| `data` | Created items | Full radar object, same shape as single-radar creation response |
| `code` / `errors[]` | Failed items | HTTP error code and error details, same shape as sync API errors |
Items are always returned ordered by `item_index`.
The `radars[]` array grows as items complete โ re-fetch until `status = "completed"` to get all outcomes. In-flight items appear as `{ item_index, custom_fields, status: "processing" }` with no other fields.
# Bulk Create Radars
Source: https://docs.tamradar.com/api-reference/create-bulk-radars
/api_specs.yaml POST /v1/radars/bulk
Queue a bulk submission for asynchronous radar creation.
The `bulk_completed` webhook carries the full per-item result array. At 1,000 items with rich `custom_fields` or filter data, the payload can approach 1โ2 MB โ ensure your receiver can handle it. Alternatively, use [GET /v1/radars/bulk/\{bulk\_id}](/api-reference/get-radars-bulk-status) to fetch results on demand.
# Create Company Radar
Source: https://docs.tamradar.com/api-reference/create-company-radar
/api_specs.yaml POST /v1/radars/companies
Create a company radar with company-specific filters.
# Create Contact Radar
Source: https://docs.tamradar.com/api-reference/create-contact-radar
/api_specs.yaml POST /v1/radars/contacts
Create a contact radar for person-level tracking.
# Create Industry Radar
Source: https://docs.tamradar.com/api-reference/create-industry-radar
/api_specs.yaml POST /v1/radars/industry
Create an industry radar for keyword-level tracking.
# Deactivate Radar
Source: https://docs.tamradar.com/api-reference/delete-radar
/api_specs.yaml DELETE /v1/radars/{radar_id}
Deactivate a radar by ID.
# Get Radar by ID
Source: https://docs.tamradar.com/api-reference/get-radar
/api_specs.yaml GET /v1/radars/{radar_id}
Fetch a single radar configuration by radar ID.
# List Radars
Source: https://docs.tamradar.com/api-reference/list-radars
/api_specs.yaml GET /v1/radars
Retrieve all radars on your account, filtered by type, domain, or status.
# Poll Updates
Source: https://docs.tamradar.com/api-reference/poll-updates
/api_specs.yaml GET /v1/updates
Fetch radar findings and failures via cursor-based polling.
# Getting Started with TAMradar
Source: https://docs.tamradar.com/getting-started
Step-by-step guide to authenticate, create radars, and receive updates.
## What is TAMradar?
TAMradar monitors companies, contacts, and industries by tracking their online activity and sending real-time notifications when something changes.
## Core Concepts
* **Radars** โ A monitor you configure. You define what to track (a company, a person, or an industry keyword) and what signal to look for (job openings, new hires, funding rounds, etc.).
* **Webhooks** โ How you receive events. TAMradar sends a POST request to your URL when a radar fires.
* **Polling** โ Alternative to webhooks. Use `GET /v1/updates` to pull events on your schedule.
* **Balance** โ Radars cost balance to create and run. Check yours before creating radars.
***
## Radar Types
TAMradar has three categories of radars, each with its own creation endpoint:
| Category | Endpoint | Tracks |
| -------- | --------------------------- | ------------------ |
| Company | `POST /v1/radars/companies` | A specific company |
| Contact | `POST /v1/radars/contacts` | A specific person |
| Industry | `POST /v1/radars/industry` | A keyword or theme |
**Company radar types:** `company_job_openings`, `company_new_hires`, `company_promotions`, `company_reviews`, `company_mentions`, `company_social_posts`, `company_social_posts_cxo`, `company_social_engagements`
**Contact radar types:** `contact_job_changes`, `contact_social_posts`, `contact_social_engagements`
**Industry radar types:** `industry_mentions`, `industry_funding_rounds`, `industry_new_companies`, `industry_job_openings`
***
## Step 1: Authentication
Every request requires your API key in the `x-api-key` header:
```bash theme={null}
curl -H "x-api-key: YOUR_API_KEY" https://api.tamradar.com/v1/account
```
A `401` means your key is missing or invalid. A `200` means you're authenticated.
***
## Step 2: Check Your Balance
Before creating radars, confirm you have sufficient balance:
```bash theme={null}
curl https://api.tamradar.com/v1/account \
-H "x-api-key: YOUR_API_KEY"
```
**Response:**
```json theme={null}
{
"status": "success",
"code": 200,
"message": "Account summary retrieved",
"timestamp": "2024-03-20T12:00:00Z",
"data": {
"usage": {
"month": "2024-03",
"radars_activated": 14,
"radars_deactivated": 3,
"radars_failed": 0,
"updates_found": 42,
"balance_used_usd": 28.50
},
"account": {
"total_radars": 47,
"active_radars": 12,
"inactive_radars": 34,
"failed_radars": 1,
"total_updates_found": 312
},
"balance_remaining_usd": 971.50
}
}
```
Add `?month=YYYY-MM` to get stats for a specific month (defaults to current UTC month).
***
## Step 3: Create a Radar
> **`webhook_url` is optional.** Omit it to create a poll-only radar. Updates are available via [`GET /v1/updates`](#step-5-poll-for-updates). Add it when you want TAMradar to push events to your server in real-time.
### Company Radar
Monitor a company for job openings, with optional department and seniority filters:
```bash theme={null}
curl -X POST https://api.tamradar.com/v1/radars/companies \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": "stripe.com",
"radar_type": "company_job_openings",
"webhook_url": "https://your-domain.com/webhook",
"departments": ["Engineering"],
"seniorities": ["Senior", "Manager"],
"custom_fields": {
"crm_account_id": "0011U00000TFV7MQAX"
}
}'
```
**Filterable company types:** `company_job_openings`, `company_new_hires`, `company_promotions`, `company_social_posts_cxo`
**Valid departments:** `Engineering`, `Sales`, `Marketing`, `Finance`, `Human Resources`, `Product Management`, `Operations`, `Customer Success`, `Design`, `Legal`, `Information Technology`, `Research`, `Accounting`, `Administrative`, `Business Development`, `Consulting`, `Education`, `Manufacturing`, `Media and Communication`, `Project Management`, `Purchasing`, `Quality Assurance`, `Real Estate`, `Support`
**Valid seniorities:** `Owner`, `CXO`, `Vice President`, `Director`, `Manager`, `Senior`, `Entry`, `Training`, `Partner`
**Response (201):**
```json theme={null}
{
"status": "success",
"code": 201,
"message": "Radar created successfully",
"timestamp": "2024-03-20T12:05:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_job_openings",
"domain": "stripe.com",
"webhook_url": "https://your-domain.com/webhook",
"radar_status": "active",
"departments": ["Engineering"],
"seniorities": ["Senior", "Manager"],
"custom_fields": {
"crm_account_id": "0011U00000TFV7MQAX"
},
"created_at": "2024-03-20T12:05:00Z",
"deactivated_at": null,
"next_charge_at": "2024-04-20T12:05:00Z"
}
}
```
Save the `radar_id`. You'll need it later.
***
### Contact Radar
Track a specific person's job changes or social activity:
```bash theme={null}
# Track job changes for a person
curl -X POST https://api.tamradar.com/v1/radars/contacts \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"radar_type": "contact_job_changes",
"domain": "stripe.com",
"profile_url": "https://www.linkedin.com/in/johndoe",
"webhook_url": "https://your-domain.com/webhook",
"custom_fields": {
"contact_id": "con_abc123"
}
}'
```
```bash theme={null}
# Track social posts from a specific person
curl -X POST https://api.tamradar.com/v1/radars/contacts \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"radar_type": "contact_social_posts",
"profile_url": "https://www.linkedin.com/in/johndoe",
"webhook_url": "https://your-domain.com/webhook"
}'
```
**Rules:**
* `contact_job_changes` โ requires `domain` + at least one of: `profile_url`, `email`, `full_name`
* `contact_social_posts` / `contact_social_engagements` โ requires `profile_url` (LinkedIn, X, or Twitter only)
***
### Industry Radar
Track a keyword or theme across companies and news:
```bash theme={null}
# Track all funding rounds (no keyword needed)
curl -X POST https://api.tamradar.com/v1/radars/industry \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"radar_type": "industry_funding_rounds",
"webhook_url": "https://your-domain.com/webhook"
}'
```
```bash theme={null}
# Track mentions of a topic
curl -X POST https://api.tamradar.com/v1/radars/industry \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"radar_type": "industry_mentions",
"keyword": "zero trust security",
"countries": ["US", "GB"],
"webhook_url": "https://your-domain.com/webhook"
}'
```
**Rules:**
* `industry_mentions` and `industry_job_openings` โ `keyword` is required (min 3 chars)
* `industry_funding_rounds` and `industry_new_companies` โ no `keyword` needed
* `countries` โ optional, max 5 country codes
***
## Step 4: List and Inspect Radars
```bash theme={null}
# List all active radars
curl "https://api.tamradar.com/v1/radars?radar_status=active&limit=50" \
-H "x-api-key: YOUR_API_KEY"
# Get a specific radar
curl "https://api.tamradar.com/v1/radars/c70813b3-7e87-4ca7-90fd-8c64574d911b" \
-H "x-api-key: YOUR_API_KEY"
# Filter by type
curl "https://api.tamradar.com/v1/radars?radar_type=company_job_openings" \
-H "x-api-key: YOUR_API_KEY"
```
**Query parameters:**
| Parameter | Type | Default | Description |
| -------------- | ---------------------- | ------- | --------------------------- |
| `limit` | number | 50 | Max 1000 |
| `radar_status` | `active` \| `inactive` | โ | Filter by status |
| `radar_type` | string | โ | Single radar type value |
| `radar_id` | string | โ | Comma-separated IDs, max 50 |
**Response (200):**
```json theme={null}
{
"status": "success",
"code": 200,
"message": "Radars retrieved successfully",
"count": 2,
"limit": 50,
"timestamp": "2024-03-20T12:10:00Z",
"data": [
{
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_job_openings",
"domain": "stripe.com",
"webhook_url": "https://your-domain.com/webhook",
"radar_status": "active",
"departments": ["Engineering"],
"seniorities": ["Senior", "Manager"],
"custom_fields": { "crm_account_id": "0011U00000TFV7MQAX" },
"created_at": "2024-03-20T12:05:00Z",
"deactivated_at": null,
"next_charge_at": "2024-04-20T12:05:00Z"
}
]
}
```
Note: Filter fields (`departments`, `seniorities`, `job_titles`) and identifier fields (`profile_url`, `email`, `full_name`, `keyword`) are returned flat at the top level of each radar object. They are only present when set.
***
## Step 5: Receive Events
When a radar fires, TAMradar POSTs an event to your `webhook_url`.
### Webhook Payload Structure
All webhook events follow this envelope:
```json theme={null}
{
"update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
"update_type": "radar_finding",
"record_id": "b77af154-c369-4446-b70a-f328880c3a48",
"discovered_at": "2024-03-20T14:30:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_job_openings",
"domain": "stripe.com",
"next_charge_at": "2024-04-20T14:30:00Z",
"custom_fields": {
"crm_account_id": "0011U00000TFV7MQAX"
}
},
"content": {
"job_title": "Senior Software Engineer",
"job_url": "https://www.linkedin.com/jobs/view/123456789",
"job_posted_at": "2024-03-20",
"job_description": "We are seeking...",
"job_source": "LinkedIn",
"country": "United States"
}
}
```
* `data` โ radar context (always present, same fields regardless of type)
* `content` โ the actual finding (structure varies by `radar_type`)
### Your Webhook Must
1. Accept `POST` with a JSON body
2. Return any `2xx` status code within a reasonable timeout
3. Handle duplicates โ use `update_id` as an idempotency key
### Polling Alternative
If you prefer pull over push, use `GET /v1/updates`:
```bash theme={null}
# Get the latest 25 updates (radar_finding only by default)
curl "https://api.tamradar.com/v1/updates" \
-H "x-api-key: YOUR_API_KEY"
# Filter by radar and paginate with cursor
curl "https://api.tamradar.com/v1/updates?radar_id=c70813b3-7e87-4ca7-90fd-8c64574d911b&cursor=CURSOR_FROM_PREV_RESPONSE" \
-H "x-api-key: YOUR_API_KEY"
# Include failures too
curl "https://api.tamradar.com/v1/updates?update_type=radar_finding,radar_failure" \
-H "x-api-key: YOUR_API_KEY"
```
**Query parameters:** `radar_id` (comma-sep, max 10), `radar_type` (comma-sep), `domain` (comma-sep, max 10), `since` (ISO 8601), `cursor`, `limit` (1โ100, default 25), `update_type` (`radar_finding` | `radar_failure`, default: `radar_finding`)
**Response shape:**
```json theme={null}
{
"status": "success",
"code": 200,
"message": "Updates retrieved successfully",
"timestamp": "2024-03-20T12:00:00Z",
"updates": [ ],
"has_more": true,
"next_cursor": "eyJyIjoiMjAyNi0..."
}
```
Pass `next_cursor` as `cursor` in the next request to paginate. `has_more: false` means you've reached the end.
***
## Step 6: Bulk Create Radars
Submit a bulk request for asynchronous processing (up to 1000 items):
```bash theme={null}
curl -X POST https://api.tamradar.com/v1/radars/bulk \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhook_url": "https://your-domain.com/webhook",
"radars": [
{
"domain": "stripe.com",
"radar_type": "company_job_openings",
"departments": ["Engineering"],
"custom_fields": { "crm_account_id": "acc_001" }
},
{
"domain": "notion.so",
"radar_type": "company_new_hires"
},
{
"radar_type": "contact_social_posts",
"profile_url": "https://www.linkedin.com/in/someone"
}
]
}'
```
* `webhook_url` is required at the envelope level for async bulk
* Items can mix company, contact, and industry types
* Max 1000 items, max 1MB request body
* Item-level validation and creation happen asynchronously after submission
**Submission response (`202 Accepted`):**
```json theme={null}
{
"status": "success",
"code": 202,
"timestamp": "2024-03-20T12:05:00Z",
"message": "Bulk submission accepted โ radars are being created asynchronously",
"bulk_id": "5f9da22e-4778-427c-b7e4-dab2a8d9d709",
"summary": {
"total": 3
}
}
```
Save `bulk_id`, then poll for status:
```bash theme={null}
curl "https://api.tamradar.com/v1/radars/bulk/5f9da22e-4778-427c-b7e4-dab2a8d9d709" \
-H "x-api-key: YOUR_API_KEY"
```
The status endpoint returns:
* `status: "processing"` while items are still running
* `status: "completed"` when all items are terminal
* Per-item outcomes in `radars[]` with `item_index` mapping back to the input order
* `radars[].update_id` as the per-item correlation key (`radar_created` / `radar_failure`)
For webhook idempotency/correlation in async bulk:
* Use top-level `update_id` on `bulk_completed` to deduplicate the batch event itself
* Use `radars[].update_id` inside `bulk_completed` to map each item back to its individual item event
***
## Step 7: Deactivate a Radar
```bash theme={null}
curl -X DELETE "https://api.tamradar.com/v1/radars/c70813b3-7e87-4ca7-90fd-8c64574d911b" \
-H "x-api-key: YOUR_API_KEY"
```
**Response (200):**
```json theme={null}
{
"status": "success",
"code": 200,
"message": "Radar deactivated successfully",
"timestamp": "2024-03-22T14:30:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_job_openings",
"domain": "stripe.com",
"webhook_url": "https://your-domain.com/webhook",
"radar_status": "inactive",
"departments": ["Engineering"],
"seniorities": ["Senior", "Manager"],
"custom_fields": { "crm_account_id": "0011U00000TFV7MQAX" },
"created_at": "2024-03-20T12:05:00Z",
"deactivated_at": "2024-03-22T14:30:00Z",
"next_charge_at": null
}
}
```
Deactivation is a soft delete. The radar record is retained for history.
***
## Error Responses
All errors follow the same structure:
```json theme={null}
{
"status": "error",
"code": 400,
"message": "Invalid request body",
"errors": [
{ "field": "domain", "reason": "Domain is required" },
{ "field": "radar_type", "reason": "Invalid radar type provided" }
],
"error_id": "7f8d6e5c-4b3a-2a1b-0c9d-8e7f6d5c4b3a",
"timestamp": "2024-03-20T10:15:30Z"
}
```
| Code | Meaning | Retryable |
| ---- | -------------------------- | ------------------ |
| 400 | Validation error | No |
| 401 | Missing or invalid API key | No |
| 402 | Insufficient balance | Yes (top up first) |
| 404 | Radar not found | No |
| 409 | Radar already exists | No |
| 429 | Rate limited | Yes (back off) |
| 500 | Internal server error | Yes |
| 503 | Database unavailable | Yes |
Use `error_id` when contacting support. It traces the exact request in our logs.
***
## Key Constraints
| Rule | Limit |
| ---------------------- | -------------------------------------------------------- |
| Webhook URL | Optional. Max 2048 chars, must be HTTP/HTTPS if provided |
| Domain | Standard format, max 253 chars |
| Custom fields | Max 20 keys; key โค 50 chars; value โค 500 chars |
| `countries` (industry) | Max 5 |
| `keyword` (industry) | Min 3 chars |
| Bulk size | Max 1000 items, 1MB body |
| List `limit` | Max 1000 |
| List `radar_id` filter | Max 50 IDs |
# Authentication & Rate Limits
Source: https://docs.tamradar.com/guides/authentication-rate-limits
Authentication model, request headers, and rate-limit behavior.
# Authentication & Rate Limits
## API Key Authentication
All requests to TAMradar API must include your API key. This key uniquely identifies your account and authorizes access to our services.
### Using Your API Key
Include your API key in the `x-api-key` header with every request:
```bash theme={null}
# Example GET request
curl "https://api.tamradar.com/v1/radars" \
-H "x-api-key: YOUR_API_KEY"
# Example POST request
curl -X POST "https://api.tamradar.com/v1/radars" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": "example.com",
"radar_type": "company_job_openings"
}'
```
### Authentication Errors
If authentication fails, you'll receive a 401 response:
```json theme={null}
{
"status": "error",
"code": 401,
"message": "Invalid or inactive API key",
"errors": [{ "field": "x-api-key", "reason": "Invalid or inactive API key" }],
"timestamp": "2024-01-01T12:00:00Z",
"error_id": "f72ac8d3-4bca-4f4c-b14a-30af90e3eac2"
}
```
## Rate Limits
### Standard Limits
| Bucket | Default |
| -------------------------- | ----------------------------- |
| Write (create radars) | 100 per minute, 10 per second |
| Read (GET radars, updates) | 200 per minute, 10 per second |
Higher limits are available on enterprise plans. Contact [support@tamradar.com](mailto:support@tamradar.com).
### How limits are counted
There are two separate budgets, write and read, so a heavy polling client doesn't eat into your creation budget.
| Method | Endpoints | Budget used |
| -------- | ------------------------------------------------------------ | ---------------------------------- |
| `POST` | `/v1/radars` | Write (100/min, 10/sec) |
| `POST` | `/v1/radars/bulk` | Bulk (10/min default, per account) |
| `GET` | `/v1/radars`, `/v1/radars/:id`, `/v1/updates`, `/v1/account` | Read (200/min, 10/sec) |
| `DELETE` | `/v1/radars/:id` | Read (200/min, 10/sec) |
**Single creates** (`POST /v1/radars`) deduct 1 token from your per-minute write budget and 1 from your per-second burst budget.
**Bulk submissions** (`POST /v1/radars/bulk`) use a **dedicated bulk bucket** (default 10 submissions per minute per account). Each submission consumes **1 bulk token** regardless of item count.
### Async bulk behavior
Bulk requests are queued asynchronously (`202 Accepted` with `bulk_id`). Item outcomes are produced later through:
* `GET /v1/radars/bulk/:bulk_id`
* Webhook events (`radar_created`, `radar_failure`, `bulk_completed`)
Because item processing is async, you do not receive synchronous per-item `201/207/402/409` bulk responses.
### Response headers
Successful requests include rate-limit headers when applicable. For bulk submissions, `Retry-After`, `X-RateLimit-Limit`, and `X-RateLimit-Remaining` are guaranteed on `429`.
| Header | Meaning |
| ----------------------- | ---------------------------------------- |
| `X-RateLimit-Limit` | Your per-minute budget for this endpoint |
| `X-RateLimit-Remaining` | Tokens remaining after this request |
| `X-RateLimit-Reset` | Seconds until the bucket fully refills |
### Handling Rate Limits
When you exceed the rate limit you'll receive a `429` with three headers:
| Header | Meaning |
| ----------------------- | --------------------------------------------- |
| `Retry-After` | Seconds until you have enough budget to retry |
| `X-RateLimit-Limit` | Your per-minute budget |
| `X-RateLimit-Remaining` | Tokens available right now |
The `reason` field tells you which bucket fired:
* `minute` โ per-minute budget exhausted, wait `Retry-After` seconds
* `burst` โ per-second burst limit hit, retry in 1 second
```json theme={null}
{
"status": "error",
"code": 429,
"message": "Per-minute rate limit exceeded. Retry after 51 seconds.",
"errors": [{ "field": "rate_limit", "reason": "minute" }],
"timestamp": "2024-01-01T12:00:00Z"
}
```
### Bulk sizing limits
Async bulk currently allows:
* up to 1000 items per request
* request body up to 1MB
If either limit is exceeded, the request is rejected with `400`.
## Enterprise Options
For higher volume needs, we offer customized plans with:
* Increased rate limits
* Priority support
Contact [support@tamradar.com](mailto:support@tamradar.com) to discuss your requirements.
### Need Help?
* Check our [Error Handling](/guides/error-handling) guide
* Contact [support@tamradar.com](mailto:support@tamradar.com)
# Monthly Subscription Billing
Source: https://docs.tamradar.com/guides/balance-monthly-subscription
Monthly subscription billing flow, grace period behavior, and failure handling.
Monthly subscription radars are billed at creation, then every 30 days while active.
## Billing Mechanics
* Charge happens at radar creation.
* The next renewal timestamp is returned as `next_charge_at`.
* Renewal runs every 30 days while the radar remains active.
## Deactivation Behavior
* User-triggered deactivation keeps the radar running until the current billing cycle ends.
* After the cycle ends, the radar transitions to `inactive`.
* At cycle end, the radar transitions to `inactive`.
## Failure and Refund Behavior
* If setup fails during initial creation, a `radar_failure` webhook is sent.
* For monthly subscription, setup failures can include a refund (`refund_amount_usd > 0`).
* If renewal fails due to insufficient funds, the radar is deactivated and no refund is applied for prior usage.
## What to Monitor
* `next_charge_at` for upcoming renewal windows.
* `radar_failure` with `code: 402` for renewal failures.
* `radar_status` in API responses (`active` or `inactive`) for operational state.
# Balance and Pricing
Source: https://docs.tamradar.com/guides/balance-system-explained
Overview of pricing models, balance lifecycle, and billing states.
This page gives a high-level view of TAMradar billing and lifecycle behavior.
For model-specific implementation details:
* [Monthly Subscription Billing](/guides/balance-monthly-subscription)
* [Update-Based Billing](/guides/balance-update-based)
## Overview
TAMradar uses a **USD credit balance** system to bill for radar usage. Two pricing models exist, and which one applies to a radar depends on your account configuration per radar type.
***
## Pricing Models
### Monthly Subscription
* **Charge at creation** โ balance is deducted when the radar is first created.
* **Recurring charge** โ deducted automatically every 30 days while the radar is active. `next_charge_at` in the API response shows the next billing date.
* **Refund on setup failure** โ if the radar fails during initial setup (missing prerequisites, source inaccessible), the creation charge is automatically reversed and you receive a `radar_failure` webhook with `refund_amount_usd > 0`.
* **User cancellation** โ radar transitions to `inactive`. For monthly subscription, service can continue until the current billing period ends.
### Update-Based
* **No charge at creation** โ `next_charge_at` is `null`. Nothing is deducted when the radar is set up.
* **Charged per update found** โ balance is deducted each time TAMradar finds and approves a new update for your radar (a new hire, job change, funding round, etc.).
* **No refund on setup failure** โ if the radar fails during setup, you receive a `radar_failure` webhook with `refund_amount_usd: 0` (nothing was charged, so nothing to refund).
* **Auto-reactivation on top-up** โ if a radar was deactivated due to insufficient funds, it automatically reactivates when a new payment is added to your account. No manual action needed.
***
## Radar States
`radar_status` in public API responses is intentionally simplified to two values:
| Public `radar_status` | Meaning |
| --------------------- | ---------------------------------------------------------------------------------------------- |
| `active` | Running normally. Balance is deducted per the pricing model. |
| `inactive` | Any non-active lifecycle state (cancelled, failed, insufficient funds, or grace-period state). |
Internal lifecycle enums are implementation details and are not part of the public API contract.
For external handling, use:
* `radar_status` (`active` / `inactive`)
* `radar_failure` payload fields: `code`, `message`, and `refund_amount_usd`
***
## Monitoring Balance Events
### Webhook Notifications
Your application receives `radar_failure` webhook events whenever a radar is deactivated due to a billing or setup issue.
**Monthly subscription โ insufficient funds during renewal**
Radar is deactivated when the 30-day charge cannot be covered. No refund (the original creation charge was for time already used).
```json theme={null}
{
"update_id": "550e8400-e29b-41d4-a716-446655440000",
"update_type": "radar_failure",
"record_id": null,
"discovered_at": "2025-03-20T14:10:38Z",
"completed_at": "2025-03-20T14:10:38Z",
"data": {
"radar_id": "54e32291-25a4-459f-8bd6-4f79489ea931",
"radar_type": "company_new_hires",
"domain": "example.com",
"next_charge_at": null,
"custom_fields": {}
},
"status": "error",
"code": 402,
"message": "Your radar was deactivated due to insufficient funds. Please add more credits to reactivate.",
"errors": [{ "field": "radar", "reason": "insufficient_funds" }],
"refund_amount_usd": 0,
"timestamp": "2025-03-20T14:10:38Z",
"error_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
**Monthly subscription โ setup failure (with refund)**
Radar failed during initial setup. Creation charge is reversed automatically.
```json theme={null}
{
"update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
"update_type": "radar_failure",
"record_id": null,
"discovered_at": "2025-03-20T08:30:00Z",
"completed_at": "2025-03-20T08:30:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "industry_job_openings",
"domain": null,
"next_charge_at": null,
"custom_fields": {}
},
"status": "error",
"code": 400,
"message": "Radar creation failed: missing prerequisites.",
"errors": [{ "field": "radar", "reason": "missing_prerequisites" }],
"refund_amount_usd": 0.10,
"timestamp": "2025-03-20T08:30:00Z",
"error_id": "41991828-ed31-4bd9-98d2-44b2763f9f35"
}
```
**Update-based โ setup failure (no refund)**
Same webhook shape, but `refund_amount_usd` is always `0` because nothing was charged at creation.
```json theme={null}
{
"update_id": "72bc9a00-1234-4abc-b000-aabbccddeeff",
"update_type": "radar_failure",
"record_id": null,
"discovered_at": "2025-03-21T10:00:00Z",
"completed_at": "2025-03-21T10:00:00Z",
"data": {
"radar_id": "a1b2c3d4-0000-0000-0000-000000000001",
"radar_type": "contact_job_changes",
"domain": "example.com",
"next_charge_at": null,
"custom_fields": {}
},
"status": "error",
"code": 400,
"message": "Radar creation failed: missing prerequisites.",
"errors": [{ "field": "radar", "reason": "missing_prerequisites" }],
"refund_amount_usd": 0,
"timestamp": "2025-03-21T10:00:00Z",
"error_id": "72bc9a00-1234-4abc-b000-aabbccddeeff"
}
```
### Failure Reasons
Failure type is identified by `code` and `message` in the `radar_failure` payload.
| Failure type | `code` | Description | Refund |
| --------------------- | ------ | ------------------------------------------------------------------------ | ----------------------------------- |
| Missing prerequisites | 400 | Required public data not found (e.g. no LinkedIn profile, no public RSS) | Monthly sub: yes. Update-based: no. |
| Source inaccessible | 400 | Data source exists but cannot be accessed | Monthly sub: yes. Update-based: no. |
| Setup failure | 400 | Generic failure during radar initialisation | Monthly sub: yes. Update-based: no. |
| Insufficient funds | 402 | Radar deactivated โ balance too low for renewal charge | No refund |
***
## Bulk Endpoint โ Async Outcomes
`POST /v1/radars/bulk` is asynchronous. The submission endpoint returns `202` with `bulk_id`, and item outcomes are emitted later.
For balance-related failures in bulk:
* the affected item ends in a failure state during async processing
* the failure appears in:
* `GET /v1/radars/bulk/:bulk_id` item results, and
* webhook delivery (`radar_failure` for item-level failures, plus final `bulk_completed`)
Treat bulk as a queued workflow rather than a synchronous per-item response.
***
## Best Practices
1. **Monitor balance** โ poll `GET /v1/account` for `balance_remaining_usd` and alert before it hits zero.
2. **Handle `radar_failure` webhooks** โ persist `code`, `message`, and `refund_amount_usd` for reconciliation.
3. **Update-based: top up proactively** โ radars auto-reactivate on top-up, but there's a gap while they're inactive. Schedule automatic top-ups to avoid missed updates.
4. **Monthly subscription: budget for renewals** โ check `next_charge_at` on your active radars and ensure balance covers the next 30 days.
5. **Bulk retries are workflow-based** โ top up balance, then resubmit the failed items from your async bulk status/webhook outcomes.
# Update-Based Billing
Source: https://docs.tamradar.com/guides/balance-update-based
Update-based billing flow, insufficient-funds behavior, and auto-reactivation.
Update-based radars are charged only when TAMradar produces approved updates.
## Billing Mechanics
* No charge happens at creation.
* `next_charge_at` is `null`.
* Balance is deducted per approved finding.
## Deactivation and Reactivation
* If balance reaches zero, the radar transitions to `inactive`.
* When balance is topped up, affected update-based radars can auto-reactivate.
* User-triggered deactivation sets the radar to `inactive` immediately (no grace period).
## Failure and Refund Behavior
* Setup failures still emit `radar_failure` webhooks.
* For update-based setup failures, `refund_amount_usd` is typically `0` because no creation charge was taken.
* Failure `code` is `400` for setup failures (`missing_prerequisites`, `source_inaccessible`, `setup_failure`) and `402` for insufficient funds. See `message` for details.
## What to Monitor
* `balance_remaining_usd` from `GET /v1/account`.
* `radar_failure` events for funding and setup issues.
* Bulk workflows (`POST /v1/radars/bulk` + `GET /v1/radars/bulk/{bulk_id}`) for async item-level failures.
# Bulk Create Guide
Source: https://docs.tamradar.com/guides/bulk-create-guide
End-to-end setup for asynchronous bulk radar creation and result handling.
Use this guide when you want to create many radars at once using `POST /v1/radars/bulk`.
Bulk creation is asynchronous:
* The submission request returns `202 Accepted` with a `bulk_id`.
* Item-level outcomes are produced later as `radar_created` or `radar_failure`.
* A final `bulk_completed` event indicates that all items are done.
For request and response schemas, use:
* [Bulk Create Radars](/api-reference/post-radars-bulk)
* [Get Bulk Create Status](/api-reference/get-radars-bulk-status)
## Bulk flow and responsibilities
| Component | Responsibility |
| ------------------------------- | -------------------------------------------------------------------------- |
| Your application | Build the bulk payload, submit it, store `bulk_id`, reconcile outcomes |
| `POST /v1/radars/bulk` | Validates envelope-level shape and queues items |
| Bulk processor | Validates and processes each queued item asynchronously |
| Your webhook endpoint | Receives per-item `radar_created` / `radar_failure`, then `bulk_completed` |
| `GET /v1/radars/bulk/{bulk_id}` | Polls live status and per-item results |
## Step 1: Prepare your webhook consumer
`webhook_url` is required for async bulk. Point it to an HTTPS endpoint that:
* accepts `POST`
* returns `2xx` quickly
* processes payloads asynchronously in your own worker queue
You should handle these update types:
* `radar_created`
* `radar_failure`
* `bulk_completed`
See [Webhook Payloads](/webhooks/overview) for payload details and examples.
## Step 2: Build a valid bulk request
The top-level body requires:
* `webhook_url`: single callback URL for all items in this bulk submission
* `radars`: array of radar payloads
Each item in `radars[]` uses the same field structure as the corresponding single-radar endpoint. Before building a bulk payload, make sure you're familiar with the per-type schemas:
* [Create Company Radar](/api-reference/post-radars-companies) โ `domain`, `radar_type`, optional filters
* [Create Contact Radar](/api-reference/post-radars-contacts) โ `profile_url`, `radar_type`, `domain`
* [Create Industry Radar](/api-reference/post-radars-industry) โ `radar_type`, optional `keyword`
We recommend adding `custom_fields` to every item. If you skip it, you can still reconcile with `item_index`, but item-level tracing in downstream systems is harder.
```json theme={null}
{
"webhook_url": "https://your-domain.com/webhooks/tamradar",
"radars": [
{
"radar_type": "company_job_openings",
"domain": "stripe.com",
"custom_fields": { "crm_account_id": "acc_001" }
},
{
"radar_type": "contact_job_changes",
"domain": "openai.com",
"profile_url": "https://www.linkedin.com/in/example",
"custom_fields": { "owner": "sdr_team" }
}
]
}
```
## Step 3: Submit and persist `bulk_id`
On success, the API returns `202` with a `bulk_id` and summary. Persist `bulk_id` immediately. It is your primary key for reconciliation and polling.
```bash theme={null}
curl -X POST https://api.tamradar.com/v1/radars/bulk \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhook_url": "https://your-domain.com/webhooks/tamradar",
"radars": [
{
"radar_type": "company_job_openings",
"domain": "stripe.com",
"custom_fields": { "crm_account_id": "acc_001" }
},
{
"radar_type": "industry_funding_rounds",
"custom_fields": { "segment": "fintech" }
}
]
}'
```
```json theme={null}
{
"status": "success",
"code": 202,
"message": "Bulk submission accepted - 2 radars queued for processing. Poll GET /v1/radars/bulk/5f9da22e-4778-427c-b7e4-dab2a8d9d709 for status or wait for the bulk_completed webhook.",
"bulk_id": "5f9da22e-4778-427c-b7e4-dab2a8d9d709",
"summary": {
"total": 2
},
"timestamp": "2026-05-25T11:17:12Z"
}
```
Typical immediate failure cases:
* `400`: invalid JSON or invalid envelope (`webhook_url`, `radars[]`)
* `401`: missing or invalid API key
* `429`: bulk rate limit exceeded
* `500`: queueing failed
For error format details, see [Error Handling](/guides/error-handling).
`400` example:
```json theme={null}
{
"status": "error",
"code": 400,
"message": "Invalid request body",
"errors": [
{
"field": "webhook_url",
"reason": "Invalid webhook URL format"
}
],
"timestamp": "2026-05-25T11:18:10Z",
"error_id": "245df4c2-3ab8-4c96-a60d-42f6fc59f9b8"
}
```
`429` example:
```json theme={null}
{
"status": "error",
"code": 429,
"message": "Rate limit exceeded โ wait and retry.",
"errors": [
{
"field": "rate_limit",
"reason": "minute"
}
],
"timestamp": "2026-05-25T11:18:44Z",
"error_id": "29a4ff58-f4a8-4ebe-8b2d-1544ee43a99c"
}
```
For async bulk, `reason` is always `minute` because this endpoint enforces a dedicated per-minute bulk bucket.
## Step 4: Process per-item events
Items do not complete at the same time. You can receive outcomes in any order.
`radar_created` means the item created successfully.
`radar_failure` means that item failed (validation, conflict, billing, or processing error). The `code`, `message`, and `errors[]` fields are identical to what the sync single-create endpoint would return for the same failure โ so any error handling you already have for the sync API works here without changes.
Use `item_index`, `bulk_id`, and `custom_fields` to map each event back to the original request item.
For successful items, `data` matches the same `data` shape returned by single-create endpoints:
[Create Company Radar](/api-reference/post-radars-companies), [Create Contact Radar](/api-reference/post-radars-contacts), and [Create Industry Radar](/api-reference/post-radars-industry).
`radar_created` example:
```json theme={null}
{
"update_id": "efbf7c77-c1ae-4a0d-85b6-116674e9e0b1",
"update_type": "radar_created",
"record_id": null,
"code": 201,
"message": "Radar created successfully",
"completed_at": "2026-05-25T11:17:20Z",
"bulk_id": "5f9da22e-4778-427c-b7e4-dab2a8d9d709",
"item_index": 0,
"custom_fields": { "crm_account_id": "acc_001" },
"status": "success",
"data": {
"radar_id": "2ffb1a07-ac95-4a6d-bc34-39f479f1c856",
"radar_type": "company_job_openings",
"domain": "stripe.com",
"radar_status": "active"
}
}
```
`radar_failure` example:
```json theme={null}
{
"update_id": "a1b2c3d4-0001-4000-b000-000000000001",
"update_type": "radar_failure",
"record_id": null,
"completed_at": "2026-05-25T11:17:26Z",
"discovered_at": "2026-05-25T11:17:26Z",
"bulk_id": "5f9da22e-4778-427c-b7e4-dab2a8d9d709",
"item_index": 1,
"custom_fields": { "segment": "fintech" },
"status": "error",
"code": 409,
"message": "Conflict: Radar already exists",
"errors": [
{
"field": "domain",
"reason": "A radar with this domain and type already exists for your account. Conflicting radar_id: 3f1d8e8a-39c4-47e8-a4b4-f7db6a2e50f0. You can view it using GET /radars/{radar_id}",
"conflicting_radar_id": "3f1d8e8a-39c4-47e8-a4b4-f7db6a2e50f0"
}
],
"refund_amount_usd": 0,
"timestamp": "2026-05-25T11:17:26Z",
"error_id": "45ed35e5-986c-4496-afd8-9a84f6baa410"
}
```
`discovered_at` is included for backwards compatibility and equals `completed_at`. Prefer `completed_at` โ `discovered_at` will be removed in a future version.
### Possible failure codes
The `code`, `message`, and `errors[]` below are spread directly into the `radar_failure` envelope above โ identical to what the sync single-create endpoint returns for the same failure. Your existing sync error handling works for bulk without changes.
Returned when a field is missing, invalid, or the radar type is not supported. `errors[]` may contain multiple entries, one per failing field.
```json theme={null}
{
"status": "error",
"code": 400,
"message": "Validation error",
"errors": [
{
"field": "domain",
"reason": "Invalid domain format"
}
],
"timestamp": "2026-05-25T11:17:26Z",
"error_id": "245df4c2-3ab8-4c96-a60d-42f6fc59f9b8"
}
```
Returned when your account does not have enough credits to create the radar. The item is not created and no credits are deducted.
```json theme={null}
{
"status": "error",
"code": 402,
"message": "Insufficient balance",
"errors": [
{
"field": "balance",
"reason": "You do not have enough balance to create this radar."
}
],
"timestamp": "2026-05-25T11:17:26Z",
"error_id": "29a4ff58-f4a8-4ebe-8b2d-1544ee43a99c"
}
```
Returned when a radar with the same configuration already exists on your account. Always includes `conflicting_radar_id` so you can look it up directly.
The `field` name varies by radar type:
* **Company** radars โ `domain`
* **Contact** radars โ `identifiers`
* **Industry** radars โ `keyword` (for `industry_mentions` / `industry_job_openings`) or `radar_type` (for other industry types)
```json theme={null}
{
"status": "error",
"code": 409,
"message": "Conflict: Radar already exists",
"errors": [
{
"field": "domain",
"reason": "A radar with this domain and type already exists for your account. Conflicting radar_id: 3f1d8e8a-39c4-47e8-a4b4-f7db6a2e50f0. You can view it using GET /radars/{radar_id}",
"conflicting_radar_id": "3f1d8e8a-39c4-47e8-a4b4-f7db6a2e50f0"
}
],
"timestamp": "2026-05-25T11:17:26Z",
"error_id": "45ed35e5-986c-4496-afd8-9a84f6baa410"
}
```
Rare. Indicates a transient infrastructure failure. The item will be retried automatically โ no action needed on your side. If a `radar_failure` with `code: 500` is delivered, it means all retries were exhausted.
```json theme={null}
{
"status": "error",
"code": 500,
"message": "Internal server error",
"errors": [
{
"field": "request",
"reason": "Create failed"
}
],
"timestamp": "2026-05-25T11:17:26Z",
"error_id": "f9ab0e2d-8246-4465-868f-922add7fbeab"
}
```
## Step 5: Finalize on `bulk_completed`
When all items reach terminal state, you receive one `bulk_completed` event. Use it to:
* mark the bulk job complete in your system
* compare final counts (`created`, `failed`) with your local ledger
* trigger any retry workflow for failed items
`bulk_completed` carries the full per-item result array. At 1,000 items with rich `custom_fields` or filter data, the payload can approach 1โ2 MB โ ensure your receiver can handle it. Alternatively, use `GET /v1/radars/bulk/{bulk_id}` to fetch results on demand instead of relying on this webhook.
`bulk_completed.radars[]` contains per-item results for the submission.
Each `radars[]` entry is identical to the per-item webhook payload already delivered for that item (`radar_created` or `radar_failure`).
```json theme={null}
{
"update_id": "9a2f0f77-5c8b-4b3e-b636-3ad539f4de65",
"update_type": "bulk_completed",
"bulk_id": "5f9da22e-4778-427c-b7e4-dab2a8d9d709",
"completed_at": "2026-05-25T11:17:33Z",
"summary": {
"total": 2,
"created": 1,
"failed": 1
},
"radars": [
{
"update_type": "radar_created",
"item_index": 0,
"...": "same fields as radar_created (including custom_fields and data)"
},
{
"update_type": "radar_failure",
"item_index": 1,
"...": "same fields as radar_failure (including code, errors, error_id, timestamp)"
}
]
}
```
## Step 6: Add polling as a reliability path
If webhook delivery is delayed or your consumer is unavailable, poll:
`GET /v1/radars/bulk/{bulk_id}`
The response `status` is:
* `processing`: at least one item is still in flight
* `completed`: all items are terminal
Poll until `completed`, then stop.
Both polling responses include `radars[]`:
* In `processing`, in-flight items appear as `{ item_index, status: "processing", custom_fields }`.
* In `completed`, each item is the full terminal payload (same shape as per-item webhook delivery).
```bash theme={null}
curl https://api.tamradar.com/v1/radars/bulk/5f9da22e-4778-427c-b7e4-dab2a8d9d709 \
-H "x-api-key: YOUR_API_KEY"
```
`processing` example:
```json theme={null}
{
"status": "processing",
"code": 200,
"timestamp": "2026-05-25T12:19:17Z",
"message": "Bulk submission is being processed",
"bulk_id": "5d089cd9-239d-4de4-af1d-8d19da889d00",
"summary": {
"total": 3,
"processing": 3,
"created": 0,
"failed": 0
},
"radars": [
{
"item_index": 0,
"status": "processing",
"custom_fields": {}
},
{
"item_index": 1,
"status": "processing",
"custom_fields": {}
},
{
"item_index": 2,
"status": "processing",
"custom_fields": {}
}
]
}
```
`completed` example:
```json theme={null}
{
"status": "completed",
"code": 200,
"timestamp": "2026-05-25T12:19:46Z",
"message": "Bulk submission completed",
"bulk_id": "5d089cd9-239d-4de4-af1d-8d19da889d00",
"summary": {
"total": 3,
"processing": 0,
"created": 0,
"failed": 3
},
"radars": [
{
"item_index": 0,
"custom_fields": {},
"code": 409,
"errors": [
{
"field": "domain",
"reason": "A radar with this domain and type already exists for your account. Conflicting radar_id: 351d5644-3d75-416e-b8a5-eb0e1b16b602. You can view it using GET /radars/{radar_id}",
"conflicting_radar_id": "351d5644-3d75-416e-b8a5-eb0e1b16b602"
}
],
"status": "error",
"message": "Conflict: Radar already exists",
"error_id": "f9ab0e2d-8246-4465-868f-922add7fbeab",
"timestamp": "2026-05-25T12:19:45.783Z"
},
{
"...": "additional items with the same terminal shape"
}
]
}
```
## Recommended production pattern
1. Submit bulk and store `bulk_id`.
2. Process webhook events and persist item outcomes by `bulk_id + item_index`.
3. Poll status endpoint on a fallback interval until completed.
4. Reconcile webhook-driven and poll-driven outcomes.
5. Retry only failed items with a new bulk request.
This gives you real-time processing through webhooks and deterministic recovery through polling.
# Error Handling
Source: https://docs.tamradar.com/guides/error-handling
Standard error envelope, common failure scenarios, and recovery patterns.
## Overview
The TAMradar API uses standard HTTP status codes and a consistent error response format.
### Error Response Envelope (400, 401, 403, 404, 409, 429, 500, 503)
* `status`: Always "error" for error responses
* `code`: HTTP status code
* `message`: Human-readable error description
* `errors`: Array of field-level errors (`field` + `reason`). May be empty (`[]`) for some server-side failures.
* `timestamp`: When the error occurred
* `error_id`: Unique identifier for error tracking
## Error Response Formats
### Error Format Example (400)
```json theme={null}
{
"status": "error",
"code": 400,
"message": "Invalid parameters",
"errors": [
{
"field": "id",
"reason": "Must be a valid UUID"
}
],
"timestamp": "2026-05-24T19:04:43Z",
"error_id": "a559c1de-216b-46e6-977c-f9ceda7d79f7"
}
```
### Server Error Format (500)
```json theme={null}
{
"status": "error",
"code": 500,
"message": "An internal server error occurred.",
"errors": [],
"timestamp": "2024-03-20T10:15:30Z",
"error_id": "f72ac8d3-4bca-4f4c-b14a-30af90e3eac2"
}
```
## Error Types
| Status | Description | Common Causes |
| ------ | --------------------- | -------------------------------------------------------------- |
| 400 | Bad Request | Missing required fields, invalid format |
| 402 | Payment Required | Insufficient balance |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Valid API key but insufficient permissions |
| 404 | Not Found | Invalid radar ID or resource not found |
| 409 | Conflict | Duplicate radar (includes existing radar ID for direct access) |
| 429 | Rate Limit Exceeded | Per-minute budget exhausted or per-second burst limit exceeded |
| 500 | Internal Server Error | Application bugs, unexpected code issues |
| 503 | Service Unavailable | Database outages, external service downtime |
## Common Error Examples
### Validation Error (400)
```json theme={null}
{
"status": "error",
"code": 400,
"message": "Invalid request body",
"errors": [
{
"field": "domain",
"reason": "Invalid domain format. Please provide a valid domain (e.g., example.com)"
}
],
"timestamp": "2024-03-20T10:15:30Z",
"error_id": "f72ac8d3-4bca-4f4c-b14a-30af90e3eac2"
}
```
### Insufficient Balance Error (402)
```json theme={null}
{
"status": "error",
"code": 402,
"message": "Insufficient balance",
"errors": [
{
"field": "balance",
"reason": "You do not have enough balance to create this radar."
}
],
"timestamp": "2024-03-20T10:15:30Z",
"error_id": "f72ac8d3-4bca-4f4c-b14a-30af90e3eac2"
}
```
### Authentication Error (401)
```json theme={null}
{
"status": "error",
"code": 401,
"message": "Missing API key",
"errors": [
{
"field": "x-api-key",
"reason": "API key is required"
}
],
"timestamp": "2024-03-20T10:15:30Z",
"error_id": "f72ac8d3-4bca-4f4c-b14a-30af90e3eac2"
}
```
### Permission Error (403)
```json theme={null}
{
"status": "error",
"code": 403,
"message": "Insufficient permissions",
"errors": [
{
"field": "permission",
"reason": "Your API key does not have permission to create radars"
}
],
"timestamp": "2024-03-20T10:15:30Z",
"error_id": "f72ac8d3-4bca-4f4c-b14a-30af90e3eac2"
}
```
### Duplicate Resource Error (409)
When creating a radar that already exists, the API returns the existing radar's ID so you can fetch it directly using `GET /v1/radars/{radar_id}`.
```json theme={null}
{
"status": "error",
"code": 409,
"message": "Conflict: Radar already exists",
"errors": [
{
"field": "domain",
"reason": "A radar with this domain and type already exists for your account. Conflicting radar_id: 7e3dda58-a046-4746-8247-258b6fd9c3ac. You can view it using GET /v1/radars/{radar_id}"
}
],
"timestamp": "2024-03-20T10:15:30Z",
"error_id": "f72ac8d3-4bca-4f4c-b14a-30af90e3eac2"
}
```
#### Different Radar Types Have Specific Error Messages:
**Company Radars:**
```json theme={null}
{
"field": "domain",
"reason": "A radar with this domain and type already exists for your account. Conflicting radar_id: 7e3dda58-a046-4746-8247-258b6fd9c3ac. You can view it using GET /v1/radars/{radar_id}"
}
```
**Contact Radars:**
```json theme={null}
{
"field": "domain",
"reason": "A radar for this person at this company already exists. Conflicting radar_id: 2f5fb18f-f9cf-4411-bbef-10f5ef73324f. You can view it using GET /v1/radars/{radar_id}"
}
```
**Industry Radars:**
```json theme={null}
{
"field": "keyword",
"reason": "An industry mentions radar for this configuration (keyword \"artificial intelligence\") already exists. Conflicting radar_id: 7e3dda58-a046-4746-8247-258b6fd9c3ac. You can view it using GET /v1/radars/{radar_id}"
}
```
#### Using the Returned Radar ID:
Extract the `radar_id` from the reason message and fetch the existing radar:
```bash theme={null}
# Extract radar_id from 409 response reason field
# Then fetch the existing radar directly
curl -X GET "https://api.tamradar.com/v1/radars/7e3dda58-a046-4746-8247-258b6fd9c3ac" \
-H "x-api-key: your-api-key"
```
## Rate Limiting
Rate limits are enforced per API key. When rate limited, you'll receive:
```json theme={null}
{
"status": "error",
"code": 429,
"message": "Per-minute rate limit exceeded. Retry after 51 seconds.",
"errors": [
{
"field": "rate_limit",
"reason": "minute"
}
],
"timestamp": "2024-03-20T10:15:30Z",
"error_id": "f72ac8d3-4bca-4f4c-b14a-30af90e3eac2"
}
```
For `POST /v1/radars/bulk`, the 429 message is `Rate limit exceeded โ wait and retry.` with `reason: "minute"`.
### Server Error (500)
```json theme={null}
{
"status": "error",
"code": 500,
"message": "An internal server error occurred.",
"errors": [],
"timestamp": "2024-03-20T10:15:30Z",
"error_id": "f72ac8d3-4bca-4f4c-b14a-30af90e3eac2"
}
```
### Service Unavailable (503)
```json theme={null}
{
"status": "error",
"code": 503,
"message": "Service temporarily unavailable",
"errors": [],
"timestamp": "2024-03-20T10:15:30Z",
"error_id": "f72ac8d3-4bca-4f4c-b14a-30af90e3eac2"
}
```
## Best Practices
1. **Handle Consistent Structure**: Parse the `errors` array for all non-2xx responses.
2. **Log Error IDs**: Store `error_id` for support requests and debugging.
3. **Handle Rate Limits**: Implement exponential backoff when rate limited
4. **Validate Input**: Pre-validate requests to minimize validation errors
5. **Check Balance**: Monitor account balance to avoid insufficient balance errors
## Error Recovery
For server errors (500):
* Implement retry logic with exponential backoff
* Maximum 3 retry attempts recommended
* Add jitter to prevent thundering herd
For service unavailable (503):
* Implement retry logic with exponential backoff
* Maximum 5 retry attempts recommended (infrastructure usually recovers)
* Start with longer delays (30s, 60s, 120s...)
* Check our status page for ongoing incidents
For validation errors (400):
* Log the specific validation failures
* Fix the request payload based on the `reason` field
* Resubmit with corrected data
For authentication errors (401):
* Verify API key is correctly formatted
* Check API key is included in `x-api-key` header
* Ensure API key is active and valid
For insufficient balance errors (400):
* Check current balance via account dashboard
* Top up balance as needed
* Retry the operation
## Support
If you need help resolving errors:
1. Note the `error_id`
2. Check our [status page](https://status.tamradar.com)
3. Contact support at [support@tamradar.com](mailto:support@tamradar.com)
Include the following in support requests:
* Error ID
* Timestamp
* Request details (endpoint, method)
* Steps to reproduce
# Persona-Based Filtering
Source: https://docs.tamradar.com/guides/filtering-and-personas
How to apply persona-based filters for high-signal radar results.
Persona-based filtering allows you to receive only the most relevant records for your needs. By specifying filters when creating a radar, you can narrow down the data sent to your webhooks (or available via `GET /v1/updates`), reducing noise and focusing on the signals that matter most.
> **Note:** `webhook_url` is optional in all examples below. Omit it to create a poll-only radar.
Filtering is supported for the following radar types:
* `company_new_hires`
* `company_job_openings`
* `company_promotions`
* `company_social_posts_cxo`
For other radar types, the filter fields (`departments`, `seniorities`, `job_titles`) will be ignored.
## How to Use Filters
To apply a filter, include the filter fields directly in the body of your `POST /v1/radars/companies` request.
```json theme={null}
{
"domain": "example.com",
"radar_type": "company_job_openings",
"webhook_url": "https://your-webhook.com/endpoint",
"departments": ["Engineering"],
"seniorities": ["Senior", "Manager"]
}
```
If you omit the filter fields (`departments`, `seniorities`, `job_titles`), TAMradar will return all updates without any filtering. This is useful when you want to receive all events for a given radar type.
```json theme={null}
{
"domain": "example.com",
"radar_type": "company_job_openings",
"webhook_url": "https://your-webhook.com/endpoint"
// Will receive all job openings updates
}
```
## Filtering by Department and Seniority
You can filter records by `departments` and/or `seniorities`.
* **`departments`**: An array of strings specifying the departments to include.
* **`seniorities`**: An array of strings specifying the seniority levels to include.
When you provide both `departments` and `seniorities`, they are combined with **`AND`** logic. A record must match **at least one** value from each specified filter (departments AND seniorities) to be included.
If you provide only one key (e.g., only `departments`), only that filter will be applied.
### Accepted Valuesb
You must use one of the predefined values for these fields.
#### Departments
```
'Accounting', 'Administrative', 'Business Development', 'Consulting', 'Customer Success', 'Design', 'Education', 'Engineering', 'Finance', 'Human Resources', 'Information Technology', 'Legal', 'Manufacturing', 'Marketing', 'Media and Communication', 'Operations', 'Product Management', 'Project Management', 'Purchasing', 'Quality Assurance', 'Real Estate', 'Research', 'Sales', 'Support'
```
#### Seniorities
```
'Owner', 'CXO', 'Vice President', 'Director', 'Manager', 'Senior', 'Entry', 'Training', 'Partner'
```
### Example: Department & Seniority
This request will create a radar that only sends webhook notifications for new hires who are at the `Senior` or `Manager` level within the `Engineering` or `Product Management` departments.
```json theme={null}
{
"domain": "ramp.com",
"radar_type": "company_new_hires",
"webhook_url": "https://your-webhook.com/endpoint",
"departments": ["Engineering", "Product Management"],
"seniorities": ["Senior", "Manager"]
}
```
## Advanced Filtering with `job_titles`
For more granular control, you can filter by `job_titles` using a boolean query string.
**Important:** When the `job_titles` field is used, it **overrides** any `departments` or `seniorities` fields in the same request. You cannot combine `job_titles` with the other filter fields.
### Syntax Rules
* **Operators**: Use `AND`, `OR`, and `NOT` to construct your logic.
* **Parentheses**: Use `()` for grouping expressions.
* **Quoted Phrases**: Multi-word job titles **must** be enclosed in double quotes. For example: `"Product Manager"`.
### Examples
**Simple OR:** Find a CEO or a CTO.
```
"CEO" OR "CTO"
```
**Grouped Logic:** Find a Sales Manager or an Account Executive.
```
("Sales Manager" OR "Account Executive")
```
**Complex Logic with Negation:** Find a Lead or Staff Engineer, but exclude anyone with "Manager" in their title.
```
("Lead Engineer" OR "Staff Engineer") AND NOT "Manager"
```
**Combining Operators:** Find a CEO or a Staff Engineer, but exclude any assistant roles.
```
("CEO" OR "Staff Engineer") AND NOT "Assistant"
```
This query first finds all records with "CEO" or "Staff Engineer" in the job title. From that result set, it then removes any record where the job title also contains the word "Assistant".
* **Would Match:** `Chief Executive Officer`, `Staff Engineer`
* **Would Be Excluded:** `Executive Assistant to the CEO`, `Assistant Staff Engineer`
### Full Request Example
This request will track job openings for specific senior engineering roles, while explicitly excluding any management positions.
```json theme={null}
{
"domain": "stripe.com",
"radar_type": "company_job_openings",
"webhook_url": "https://your-webhook.com/endpoint",
"job_titles": "(\"Staff Engineer\" OR \"Principal Engineer\") AND NOT (\"Manager\" OR \"Director\")"
}
```
## Validation
All filter fields and values are validated upon submission. If your request contains invalid field values, unknown field names, or incorrectly formatted values, you will receive a `400 Bad Request` response with a detailed error message explaining the issue. For more information about error responses, see our [Error Handling guide](/guides/error-handling#/).
For detailed information about the API endpoint and request/response formats, see our [Create Radar API Reference](/api-reference/post-radars-companies#/).
# API Overview
Source: https://docs.tamradar.com/introduction
Overview of TAMradar APIs, radar types, delivery modes, and integration model.
## What is TAMradar API?
The TAMradar API gives developers a single integration surface for company, contact, and industry monitoring. The API:
* Continuously monitors publicly available web data across 15 radar types
* Structures findings into consistent, typed webhook payloads
* Delivers updates via push (webhooks) or pull (`GET /v1/updates`), based on your integration model
* Provides a REST API with JSON responses and API key authentication
The API lets developers integrate company, contact, and industry monitoring into their applications, from sales intelligence to market research to competitive tracking.
## How TAMradar Works
TAMradar operates on a simple concept:
1. **Create a radar** โ specify a domain (or contact/industry) and the signal type to monitor
2. **We scan continuously** โ our pipelines monitor sources on a recurring schedule
3. **We detect findings** โ when a match is found, it's processed and structured
4. **You receive updates** โ delivered to your webhook and/or available to poll
```
You โโโบ Create Radar โโโบ TAMradar monitors โโโบ Finding detected
โ
โโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโ
โ Push (webhook) Pull (poll) โ
โ POST to webhook_url GET /v1/updates โ
โ (if webhook_url was set) (always works) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
```
Both delivery modes return identical payload structures.
## Core Components
### Radars
Radars are the fundamental monitoring units. Each radar monitors a specific target and signal type and runs continuously until deactivated.
**Company radars** โ monitor a company by domain:
| Radar Type | What it tracks |
| ---------------------------- | ------------------------------------ |
| `company_job_openings` | New job postings |
| `company_new_hires` | Recently hired employees |
| `company_promotions` | Internal promotions |
| `company_reviews` | Employer reviews |
| `company_social_posts` | Company social media posts |
| `company_social_posts_cxo` | Executive-level social posts |
| `company_mentions` | Web mentions of the company |
| `company_social_engagements` | Engagement activity on company posts |
**Contact radars** โ monitor a specific person:
| Radar Type | What it tracks |
| ---------------------------- | -------------------------------- |
| `contact_job_changes` | Career moves / employer changes |
| `contact_social_posts` | Individual's social media posts |
| `contact_social_engagements` | Individual's engagement activity |
**Industry radars** โ monitor signals across an industry or keyword set:
| Radar Type | What it tracks |
| ------------------------- | -------------------------------------- |
| `industry_mentions` | Companies mentioning specific keywords |
| `industry_funding_rounds` | New funding announcements |
| `industry_new_companies` | Newly founded companies |
| `industry_job_openings` | Job openings matching criteria |
### Delivery
TAMradar supports two delivery modes. You can use one or both:
**Push โ Webhooks**
Set a `webhook_url` when creating a radar and TAMradar will POST each finding to your endpoint in real-time. Your endpoint must return a 2xx response to acknowledge receipt.
**Pull โ Polling**
`GET /v1/updates` returns findings on demand for any radar, including radars created without a `webhook_url`. Supports cursor-based pagination and filters by `radar_id`, `radar_type`, `domain`, and `since` (ISO 8601 date).
> `webhook_url` is optional on single-radar creation endpoints. For async bulk (`POST /v1/radars/bulk`), `webhook_url` is required.
See **[Update Payloads](/webhooks/overview)** for the full payload format per radar type.
### Contact-Based Radars
Contact radars track specific individuals rather than company-wide events:
* Identified via email, profile URL, or full name
* Multiple contacts can be tracked per company domain
* Different contact radar types for the same person are allowed (e.g., both `contact_job_changes` and `contact_social_posts`)
* Contact identifiers are used for creation but not included in webhook payloads for privacy
### Persona-Based Filtering
Reduce noise by filtering findings to your target persona. Provide a `filters` object when creating a radar:
* Filter by `departments` and `seniorities`
* Use boolean queries for `job_titles` (`AND`, `OR`, `NOT`, quoted phrases)
* Supported radar types: `company_new_hires`, `company_job_openings`, `company_promotions`, `company_social_posts_cxo`
See **[Persona-Based Filtering Guide](/guides/filtering-and-personas)** for full details.
### Balance & Pricing
TAMradar uses a USD-based balance system with two pricing models:
**Monthly Subscription**
* Charged at radar creation
* Recharged automatically every 30 days while active
* If balance is insufficient at renewal, the radar is automatically deactivated
**Update-Based**
* No charge at creation
* Charged per approved finding only โ you pay for results
* If a charge fails, all active radars of that type are paused until balance is restored
Rates are per radar type and customer-specific. See **[Balance System Explained](/guides/balance-system-explained)** for full details including refunds and balance webhook events.
## The TAMradar Lifecycle
1. **Create** โ single radar or submit an async bulk (up to 1000 items) via `POST /v1/radars/bulk`
2. **Monitor** โ TAMradar scans sources continuously on a schedule
3. **Detect** โ findings are processed, structured, and stored
4. **Deliver** โ pushed to your webhook and/or available via `GET /v1/updates`
5. **Charge** โ monthly at renewal (subscription) or per finding (update-based)
6. **Deactivate** โ manually via `DELETE /v1/radars/:id` or automatically on insufficient balance
## Use Cases
* **Sales Intelligence** โ trigger outreach on hiring signals, leadership changes, funding rounds
* **TAM Prioritization** โ surface accounts showing intent (social posts, job openings, new hires)
* **TAM Expansion** โ discover companies mentioning target keywords or hiring for specific roles
* **Competitive Analysis** โ monitor competitor activities and executive messaging
* **Recruitment** โ track hiring patterns and new joiners at target companies
* **Investor Intelligence** โ monitor portfolio companies or track funding activity by sector
## Technical Foundation
* **RESTful API** โ standard HTTP methods, consistent JSON responses
* **API Key Auth** โ `x-api-key` header on all `/v1/` routes
* **Typed creation endpoints** โ `POST /v1/radars/companies`, `/contacts`, `/industry`
* **Async bulk creation** โ `POST /v1/radars/bulk` โ queue up to 1000 items (returns `202` + `bulk_id`)
* **Bulk status polling** โ `GET /v1/radars/bulk/{bulk_id}` โ track progress and final per-item outcomes
* **Poll endpoint** โ `GET /v1/updates` โ cursor-based, up to 100 per page, filterable
* **Account summary** โ `GET /v1/account` โ balance, radar counts, optional `?month=YYYY-MM`
* **Webhook schema** โ `GET /v1/webhooks/schema` โ runtime payload schema per radar type
* **Rate limiting** โ fair-use limits enforced per API key
## Getting Started
1. **[Get Started Guide](/getting-started)** โ step-by-step: create your first radar
2. **[API Definitions](/api-reference/post-radars-companies#/)** โ full OpenAPI spec
3. **[Authentication](/guides/authentication-rate-limits#/)** โ API key usage and rate limits
4. **[Update Payloads](/webhooks/overview)** โ payload structure per radar type (webhooks + poll)
5. **[Error Handling](/guides/error-handling#/)** โ error codes and troubleshooting
***
Need personalized assistance? [Contact our support team](mailto:support@tamradar.com)
# Quickstart Guide
Source: https://docs.tamradar.com/quickstart
Fast end-to-end integration walkthrough with practical API examples.
This guide walks through a complete TAMradar integration from scratch. We'll use a concrete scenario throughout: **monitoring OpenAI**, including job openings, executive moves, and AI funding activity across the industry.
By the end you'll have three active radars, a working webhook handler, and know how to poll for updates programmatically.
**Prerequisites**
* A TAMradar API key (UUID format, e.g. `a1b2c3d4-...`)
* A publicly accessible HTTPS endpoint that accepts POST requests โ use [webhook.site](https://webhook.site) for testing
* Any HTTP client (`curl`, Axios, `fetch`, etc.)
**Base URL:** `https://api.tamradar.com`
**Auth header:** `x-api-key: YOUR_API_KEY` (required on every request)
***
## Step 1: Verify Your Account & Balance
Before creating anything, confirm your key works and you have enough balance.
```bash theme={null}
curl https://api.tamradar.com/v1/account \
-H "x-api-key: YOUR_API_KEY"
```
```json theme={null}
{
"status": "success",
"code": 200,
"message": "Account summary retrieved",
"data": {
"balance_remaining_usd": 29.85,
"account": {
"total_radars": 12,
"active_radars": 8,
"inactive_radars": 4,
"failed_radars": 0,
"total_updates_found": 541
},
"usage": {
"month": "2026-04",
"balance_used_usd": 3.20,
"radars_activated": 4,
"radars_deactivated": 1,
"radars_failed": 0,
"updates_found": 63
}
},
"timestamp": "2026-04-30T09:00:00Z"
}
```
`balance_remaining_usd` is what you have to spend. Each radar type has its own price. If you don't have enough to cover a radar creation, you'll get a `402` before anything is charged.
***
## Step 2: Test Your Webhook Endpoint
Send a sample payload to your endpoint before committing balance. This is fire-and-forget. It won't create a radar or charge anything.
```bash theme={null}
curl -X POST https://api.tamradar.com/v1/webhooks/test \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhook_url": "https://webhook.site/your-unique-id",
"radar_type": "company_job_openings"
}'
```
Check your endpoint. You should receive a sample `radar_finding` payload. If it arrives, your endpoint is reachable and returning 2xx. Proceed.
> You can also test a failure payload: add `"update_type": "radar_failure"` to the body.
***
## Step 3: Create a Company Radar (Job Openings at OpenAI)
Track new engineering job openings posted by OpenAI, filtered to Senior-level and above.
> **`webhook_url` is optional.** Include it to receive real-time push notifications. Omit it to poll for updates via `GET /v1/updates` instead.
```bash theme={null}
curl -X POST https://api.tamradar.com/v1/radars/companies \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": "openai.com",
"radar_type": "company_job_openings",
"webhook_url": "https://webhook.site/your-unique-id",
"departments": ["Engineering", "Research"],
"seniorities": ["Senior", "Director", "Vice President", "CXO"],
"custom_fields": {
"account_id": "openai-001",
"priority": "high"
}
}'
```
**201 โ Created:**
```json theme={null}
{
"status": "success",
"code": 201,
"message": "Radar created successfully",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_job_openings",
"domain": "openai.com",
"webhook_url": "https://webhook.site/your-unique-id",
"radar_status": "active",
"created_at": "2026-04-30T09:05:00Z",
"deactivated_at": null,
"next_charge_at": "2026-05-30T09:05:00Z",
"departments": ["Engineering", "Research"],
"seniorities": ["Senior", "Director", "Vice President", "CXO"],
"custom_fields": {
"account_id": "openai-001",
"priority": "high"
}
},
"timestamp": "2026-04-30T09:05:00Z"
}
```
**Save `radar_id`.** You'll use it to check status or deactivate the radar.
`next_charge_at` is your next billing date, 30 days from creation.
### What can go wrong
| Code | Reason | Fix |
| ----- | ------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `402` | Insufficient balance | Top up your account, then retry |
| `409` | Radar already exists for `openai.com + company_job_openings` | Use the `radar_id` in the error response to manage the existing radar |
| `400` | Invalid domain, bad department value, invalid `webhook_url` (if provided) | Check `errors[].reason` for the exact field and issue |
| `401` | Missing or invalid API key | Verify `x-api-key` header is present and correct |
***
## Step 4: Create a Contact Radar (Track Sam Altman)
Monitor job changes for a specific person. Requires `domain` + at least one identifier (`email`, `profile_url`, or `full_name`).
```bash theme={null}
curl -X POST https://api.tamradar.com/v1/radars/contacts \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"radar_type": "contact_job_changes",
"domain": "openai.com",
"profile_url": "https://www.linkedin.com/in/samaltman/",
"full_name": "Sam Altman",
"webhook_url": "https://webhook.site/your-unique-id",
"custom_fields": {
"account_id": "openai-001",
"contact_type": "executive"
}
}'
```
**201 โ Created:**
```json theme={null}
{
"status": "success",
"code": 201,
"message": "Radar created successfully",
"data": {
"radar_id": "a4f28c91-3b12-4e70-9d55-7e3dda58f100",
"radar_type": "contact_job_changes",
"domain": "openai.com",
"webhook_url": "https://webhook.site/your-unique-id",
"radar_status": "active",
"created_at": "2026-04-30T09:08:00Z",
"deactivated_at": null,
"next_charge_at": "2026-05-30T09:08:00Z",
"custom_fields": {
"account_id": "openai-001",
"contact_type": "executive"
}
},
"timestamp": "2026-04-30T09:08:00Z"
}
```
**Contact radar type requirements:**
| `radar_type` | Required fields |
| ---------------------------- | ----------------------------------------------------------------------- |
| `contact_job_changes` | `domain` + one of (`email`, `profile_url`, `full_name`) |
| `contact_social_engagements` | `profile_url` (must be `linkedin.com/in/`, `x.com/`, or `twitter.com/`) |
| `contact_social_posts` | `profile_url` (same as above) |
***
## Step 5: Create an Industry Radar (AI Funding Rounds)
Track funding events across the AI industry, with no specific company or keyword needed.
```bash theme={null}
curl -X POST https://api.tamradar.com/v1/radars/industry \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"radar_type": "industry_funding_rounds",
"webhook_url": "https://webhook.site/your-unique-id",
"custom_fields": {
"segment": "ai"
}
}'
```
**201 โ Created:**
```json theme={null}
{
"status": "success",
"code": 201,
"message": "Radar created successfully",
"data": {
"radar_id": "f9d01c55-82ba-4f3e-b461-2a9e7c11d830",
"radar_type": "industry_funding_rounds",
"domain": null,
"webhook_url": "https://webhook.site/your-unique-id",
"radar_status": "active",
"created_at": "2026-04-30T09:10:00Z",
"deactivated_at": null,
"next_charge_at": "2026-05-30T09:10:00Z",
"custom_fields": {
"segment": "ai"
}
},
"timestamp": "2026-04-30T09:10:00Z"
}
```
`domain` is always `null` for industry radars.
**Industry radar type requirements:**
| `radar_type` | Requires `keyword`? |
| ------------------------- | -------------------------------------------------- |
| `industry_funding_rounds` | No |
| `industry_new_companies` | No |
| `industry_mentions` | Yes (min 3 chars) |
| `industry_job_openings` | Yes (min 3 chars) + optional `countries[]` (max 5) |
***
## Step 6: Scale Up with Bulk Creation
Submit up to 1000 radars in one async bulk request. Useful when onboarding large account lists.
```bash theme={null}
curl -X POST https://api.tamradar.com/v1/radars/bulk \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhook_url": "https://webhook.site/your-unique-id",
"radars": [
{
"domain": "openai.com",
"radar_type": "company_new_hires",
"departments": ["Engineering"],
"seniorities": ["Senior", "Director"],
"custom_fields": { "account_id": "openai-001" }
},
{
"domain": "anthropic.com",
"radar_type": "company_job_openings",
"custom_fields": { "account_id": "anthropic-001" }
},
{
"domain": "openai.com",
"radar_type": "company_job_openings"
}
]
}'
```
`webhook_url` is required for async bulk submissions. Bulk processing runs in the background and emits item outcomes (`radar_created` / `radar_failure`) plus a final `bulk_completed`.
**202 โ Accepted** (request queued for async processing):
```json theme={null}
{
"status": "success",
"code": 202,
"message": "Bulk submission accepted โ radars are being created asynchronously",
"bulk_id": "5f9da22e-4778-427c-b7e4-dab2a8d9d709",
"summary": { "total": 3 },
"timestamp": "2026-04-30T09:15:00Z"
}
```
Track progress with the returned `bulk_id`:
```bash theme={null}
curl "https://api.tamradar.com/v1/radars/bulk/5f9da22e-4778-427c-b7e4-dab2a8d9d709" \
-H "x-api-key: YOUR_API_KEY"
```
The status endpoint returns:
* `status: "processing"` while there are in-flight items
* `status: "completed"` when all items are terminal
* `summary` counts (`processing`, `created`, `failed`)
* `radars[]` with `item_index` so you can map outcomes back to input order
* `radars[].update_id` so you can correlate each item with its `radar_created` / `radar_failure` event
For webhook idempotency/correlation in async bulk:
* Use top-level `update_id` on `bulk_completed` to deduplicate the whole batch event
* Use `radars[].update_id` inside `bulk_completed` to correlate each item-level event
***
## Step 7: Handle Failure Webhooks
When a radar fails during setup, TAMradar sends a `radar_failure` webhook to your endpoint and refunds the charge.
**`missing_prerequisites`** โ The company has no trackable public data source for this radar type. Balance is refunded.
```json theme={null}
{
"update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
"update_type": "radar_failure",
"record_id": null,
"discovered_at": "2026-04-30T10:00:00Z",
"completed_at": "2026-04-30T10:00:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_job_openings",
"domain": "openai.com",
"next_charge_at": null,
"custom_fields": { "account_id": "openai-001", "priority": "high" }
},
"status": "error",
"code": 400,
"message": "Radar creation failed because the domain does not have required data sources. Credits have been refunded.",
"errors": [{ "field": "radar", "reason": "missing_prerequisites" }],
"refund_amount_usd": 0.10,
"timestamp": "2026-04-30T10:00:00Z",
"error_id": "41991828-ed31-4bd9-98d2-44b2763f9f35"
}
```
**`insufficient_funds`** โ Recurring billing failed; radar was deactivated. No refund (the radar ran; it just couldn't renew).
```json theme={null}
{
"update_type": "radar_failure",
"data": { "radar_id": "...", "radar_type": "company_job_openings", "next_charge_at": null },
"status": "error",
"code": 402,
"message": "Your radar was deactivated due to insufficient funds.",
"errors": [{ "field": "radar", "reason": "insufficient_funds" }],
"refund_amount_usd": 0
}
```
**Failure reference** โ identified by `code` and `message`:
| Failure type | `code` | Refund? | What to do |
| --------------------- | ------ | ------- | ------------------------------------------------- |
| Missing prerequisites | 400 | Yes | Verify the company has accessible public profiles |
| Setup failure | 400 | Yes | Contact support |
| Insufficient funds | 402 | No | Top up balance; create a new radar to resume |
| Source inaccessible | 400 | Yes | Target profile is private or has been removed |
***
## Step 8: Receive Webhook Findings
When a radar detects something, it POSTs to your `webhook_url`. Every payload shares the same base structure. Only `content` varies by radar type.
### Company job opening at OpenAI
```json theme={null}
{
"update_id": "efbf7c77-c1ae-4a0d-85b6-116674e9e0b1",
"update_type": "radar_finding",
"record_id": "b77af154-c369-4446-b70a-f328880c3a48",
"discovered_at": "2026-04-30T14:30:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_job_openings",
"domain": "openai.com",
"next_charge_at": "2026-05-30T09:05:00Z",
"custom_fields": { "account_id": "openai-001", "priority": "high" }
},
"content": {
"job_title": "Senior Research Engineer, Alignment",
"job_url": "https://openai.com/careers/senior-research-engineer-alignment",
"job_posted_at": "2026-04-29",
"job_description": "We are looking for a Senior Research Engineer to work on alignment and safety...",
"job_source": "Company Website",
"country": "United States"
}
}
```
### New hire at OpenAI
```json theme={null}
{
"update_id": "a1b2c3d4-0001-4000-b000-000000000001",
"update_type": "radar_finding",
"record_id": "d99bf154-1234-4446-b70a-abc123456789",
"discovered_at": "2026-04-30T15:00:00Z",
"data": {
"radar_id": "...",
"radar_type": "company_new_hires",
"domain": "openai.com",
"next_charge_at": "2026-05-30T09:05:00Z",
"custom_fields": { "account_id": "openai-001" }
},
"content": {
"full_name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe",
"profile_url": "https://www.linkedin.com/in/janedoe/",
"title": "Senior Software Engineer",
"start_date": "2026-04-15",
"country": "United States"
}
}
```
**Key fields for your handler:**
* `update_id` โ use for idempotency. If you receive the same `update_id` twice, skip it.
* `custom_fields` โ your metadata, echoed back exactly as set on the radar.
* `discovered_at` โ use for ordering and incremental polling (see Step 9).
* Return any `2xx` status within **10 seconds**. TAMradar will retry on failure (up to 3 retries: 1m, 5m, 15m).
### Handling webhooks in your server
Acknowledge first, process after. TAMradar waits up to 10 seconds for a 2xx. If you do slow work (database writes, third-party calls) synchronously before responding, you risk a timeout and a retry.
```javascript theme={null}
// Node.js + Express
app.post('/webhooks/tamradar', express.json(), async (req, res) => {
// Acknowledge immediately โ processing happens after
res.sendStatus(200);
const { update_id, update_type, data, code, message } = req.body;
// Deduplicate โ TAMradar delivers at-least-once, so duplicates are possible
const alreadySeen = await db.processedUpdates.exists(update_id);
if (alreadySeen) return;
await db.processedUpdates.insert(update_id);
if (update_type === 'radar_failure') {
// Alert your team โ the radar needs attention
await alertOps(`Radar ${data.radar_id} failed (${code}): ${message}`);
return;
}
// Route by radar type โ custom_fields carry your own identifiers
switch (data.radar_type) {
case 'company_job_openings':
await notifySlack(`New role at ${data.domain}: ${content.job_title} โ ${content.job_url}`);
break;
case 'company_new_hires':
await updateCRM(data.custom_fields.account_id, { new_hire: content });
break;
case 'contact_job_changes':
await triggerSalesSequence(data.custom_fields.contact_id, content);
break;
case 'industry_funding_rounds':
await logFundingEvent(content);
break;
}
});
```
The pattern applies in any language. The key constraints are to respond 2xx before doing work and deduplicate on `update_id`.
***
## Step 9: Poll for Updates (Alternative to Webhooks)
`GET /v1/updates` returns the same payloads that webhooks deliver. Use this to backfill missed events, audit history, or as your primary data retrieval method.
**Fetch all updates for your OpenAI radars since yesterday:**
```bash theme={null}
curl "https://api.tamradar.com/v1/updates?domain=openai.com&since=2026-04-29T00:00:00Z" \
-H "x-api-key: YOUR_API_KEY"
```
**Fetch a specific radar type, paginated:**
```bash theme={null}
curl "https://api.tamradar.com/v1/updates?radar_type=company_job_openings&limit=50" \
-H "x-api-key: YOUR_API_KEY"
```
**Response:**
```json theme={null}
{
"status": "success",
"code": 200,
"message": "Updates retrieved successfully",
"updates": [ /* same shape as webhook payloads */ ],
"has_more": true,
"next_cursor": "eyJyIjoiMjAyNi0wNC0xM1QyMToyMjo1OVoiLCJpIjoiZWZiZjdjNzctYzFhZS00YTBkLTg1YjYtMTE2Njc0ZTllMGIxIn0=",
"timestamp": "2026-04-30T10:00:00Z"
}
```
**Paginate:**
```bash theme={null}
curl "https://api.tamradar.com/v1/updates?cursor=eyJyIjoiMjAyNi0wNC0x..." \
-H "x-api-key: YOUR_API_KEY"
```
Keep fetching with `next_cursor` until `has_more: false`.
**Incremental polling loop:**
```
1. Call GET /v1/updates?since={last_discovered_at}
2. Process updates in order
3. Store the highest discovered_at you've seen
4. While has_more: pass next_cursor to get next page
5. Sleep or repeat on schedule
```
> `radar_failure` events are excluded by default. To include them: `?update_type=radar_finding,radar_failure`
> Data retention: **60 days**. Poll at least that often if you don't use webhooks.
***
## Step 10: Monitor Your Radars
**List all active radars:**
```bash theme={null}
curl "https://api.tamradar.com/v1/radars" \
-H "x-api-key: YOUR_API_KEY"
```
**Get a specific radar:**
```bash theme={null}
curl "https://api.tamradar.com/v1/radars/c70813b3-7e87-4ca7-90fd-8c64574d911b" \
-H "x-api-key: YOUR_API_KEY"
```
Key fields to watch: `radar_status` (active/inactive), `next_charge_at` (next billing date), `deactivated_at`.
***
## Step 11: Deactivate a Radar
When you no longer need a radar, delete it to stop billing.
```bash theme={null}
curl -X DELETE "https://api.tamradar.com/v1/radars/c70813b3-7e87-4ca7-90fd-8c64574d911b" \
-H "x-api-key: YOUR_API_KEY"
```
```json theme={null}
{
"status": "success",
"code": 200,
"message": "Radar deactivation initiated - service will continue until the end of the current billing cycle",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_status": "inactive",
"deactivated_at": "2026-04-30T10:30:00Z",
"next_charge_at": null
},
"timestamp": "2026-04-30T10:30:00Z"
}
```
Deactivation is **irreversible**. Create a new radar to resume monitoring.
On monthly billing, the radar continues running until the end of the current billing cycle. On update-based billing, deactivation is immediate.
***
## Before You Ship
* [ ] **Deduplicate on `update_id`** โ webhooks are at-least-once. Store processed IDs and skip duplicates.
* [ ] **Respond 2xx within 10 seconds** โ acknowledge the request before doing any slow work.
* [ ] **Handle `radar_failure` webhooks** โ alert your team when a radar fails so it doesn't silently stop.
* [ ] **Monitor `balance_remaining_usd`** โ set up an alert when it drops below your comfort threshold. A zero balance deactivates radars without warning.
* [ ] **Store `radar_id` per account** โ you'll need it to list, inspect, or deactivate a radar later.
* [ ] **Test your endpoint with `/v1/webhooks/test`** before creating your first paid radar.
* [ ] **Use `since` for incremental polling** โ if you use `/v1/updates`, store the last `discovered_at` and pass it on the next call. Don't re-fetch everything on every poll.
***
## Quick Reference
| Method | Endpoint | What it does |
| -------- | -------------------------- | -------------------------------------- |
| `GET` | `/v1/account` | Check balance and radar counts |
| `POST` | `/v1/webhooks/test` | Send a test payload to a URL |
| `POST` | `/v1/radars/companies` | Create a company radar |
| `POST` | `/v1/radars/contacts` | Create a contact radar |
| `POST` | `/v1/radars/industry` | Create an industry radar |
| `POST` | `/v1/radars/bulk` | Queue up to 1000 radars asynchronously |
| `GET` | `/v1/radars/bulk/:bulk_id` | Poll async bulk processing status |
| `GET` | `/v1/radars` | List all radars |
| `GET` | `/v1/radars/:id` | Get a single radar |
| `DELETE` | `/v1/radars/:id` | Deactivate a radar |
| `GET` | `/v1/updates` | Poll for radar findings |
**Rate limits:** TAMradar uses separate write/read budgets plus a dedicated bulk budget.
| Header | Description |
| ----------------------- | ------------------------------------------- |
| `X-RateLimit-Limit` | Per-minute budget for the matched bucket |
| `X-RateLimit-Remaining` | Remaining tokens in that bucket |
| `X-RateLimit-Reset` | Seconds until bucket reset (when available) |
On `429`, honor `Retry-After` before retrying.
**Error codes:**
| Code | Meaning |
| ----- | ------------------------------------------------------------------- |
| `400` | Validation error โ check `errors[].field` and `errors[].reason` |
| `401` | Missing or invalid `x-api-key` |
| `402` | Insufficient balance โ top up and retry |
| `409` | Radar already exists โ use the `radar_id` in the error to manage it |
| `429` | Rate limit exceeded โ back off and retry |
| `5xx` | Server error โ retry with exponential backoff |
Every error response includes `error_id`. Include it when contacting support.
# Base Webhook Payload Example
Source: https://docs.tamradar.com/webhooks/base
Base Webhook Payload Example documentation for TAMradar API.
```json theme={null}
{
"update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
"update_type": "radar_finding",
"record_id": "b77af154-c369-4446-b70a-f328880c3a48",
"discovered_at": "2024-03-20T12:30:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_job_openings",
"domain": "domain.com",
"custom_fields": {
"priority": "high",
"account_id": "0011U00000TFV7MQAX"
}
},
"content": {}
}
```
# Company Job Openings
Source: https://docs.tamradar.com/webhooks/company/job-openings
Company Job Openings documentation for TAMradar API.
Notifies you when new job postings are detected from your tracked companies. This radar type helps you monitor hiring trends and growth indicators.
### Content Structure
The `content` object contains the job posting information:
| Field | Type | Required | Nullable | Description |
| ---------------- | ------ | -------- | -------- | ------------------------------------------------------------------- |
| job\_title | string | Yes | No | Title of the job posting |
| job\_url | string | Yes | No | URL to the original job posting |
| job\_posted\_at | string | Yes | Yes | Best estimated posting date (YYYY-MM-DD) |
| job\_description | string | Yes | Yes | Full or partial job description |
| job\_source | string | Yes | No | Source of the job posting (e.g., LinkedIn, Company Website) |
| country | string | Yes | Yes | Country where the job is located (extracted from job location data) |
### Example
This example includes [base webhook fields](/webhooks/overview) along with job-opening-specific content:
```json theme={null}
{
"update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
"update_type": "radar_finding",
"record_id": "b77af154-c369-4446-b70a-f328880c3a48",
"discovered_at": "2024-03-20T14:30:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_job_openings",
"domain": "domain.com",
"next_charge_at": "2024-04-20T14:30:00Z",
"custom_fields": {
"priority": "high",
"account_id": "0011U00000TFV7MQAX"
}
},
"content": {
"job_title": "Senior Software Engineer",
"job_url": "https://www.linkedin.com/jobs/view/123456789",
"job_posted_at": "2024-03-20",
"job_description": "We are seeking a Senior Software Engineer to join our growing team. The ideal candidate will have strong experience in distributed systems and cloud architecture...",
"job_source": "LinkedIn",
"country": "United Kingdom"
}
}
```
# Company Mentions
Source: https://docs.tamradar.com/webhooks/company/mentions
Company Mentions documentation for TAMradar API.
Notifies you when social media posts, articles, or other content mentions your tracked companies. This radar type helps you monitor brand presence and sentiment across various platforms.
### Content Structure
The `content` object for company mentions contains:
> **Note**: The author object now includes detailed structured fields. The `name` field is maintained for backward compatibility and is automatically constructed from `first_name + last_name` when available.
| Field | Type | Required | Description |
| ------------------- | ------ | -------- | ----------------------------------------------- |
| mention\_content | string | Yes | Text content of the post or article |
| mention\_url | string | Yes | URL to the original post or article |
| mention\_channel | string | Yes | Platform or source (e.g., Twitter, LinkedIn) |
| mention\_posted\_at | string | Yes | Best estimated date of the mention (YYYY-MM-DD) |
| author | object | No | Information about the author of the mention |
#### Author Object
| Field | Type | Required | Description |
| --------------- | ------ | -------- | ---------------------------------------------------------------------------------- |
| name | string | No | Full name of the author (constructed from first\_name + last\_name when available) |
| profile\_type | string | No | Type of profile: `"contact"` or `"company"` |
| first\_name | string | No | Author's first name - `null` if `profile_type` is `"company"` |
| last\_name | string | No | Author's last name - `null` if `profile_type` is `"company"` |
| profile\_url | string | No | URL to the author's profile |
| title | string | No | Author's job title - `null` if `profile_type` is `"company"` |
| company\_domain | string | No | Domain of the author's company |
| company\_name | string | No | Name of the author's company |
| country | string | No | Author's country location |
### Example
This example includes [base webhook fields](/webhooks/overview) along with company-mentions-specific content:
```json theme={null}
{
"update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
"update_type": "radar_finding",
"record_id": "b77af154-c369-4446-b70a-f328880c3a48",
"discovered_at": "2024-03-19T10:15:30Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_mentions",
"domain": "domain.com",
"next_charge_at": "2024-04-20T16:10:00Z",
"custom_fields": {
"priority": "high",
"account_id": "0011U00000TFV7MQAX"
}
},
"content": {
"mention_content": "@PrifWeinidog @DomainAI Can you confirm that you are not going to knock down statues of white men that have greatly contributed to history because it's has been reported on X that this is in your government's plan. If this is true it is an utter disgrace",
"mention_url": "https://twitter.com/Catheri86555691/status/1902104771068125631",
"mention_channel": "Twitter",
"mention_posted_at": "2024-03-18",
"author": {
"name": "Joe Doe",
"profile_type": "contact",
"first_name": "Joe",
"last_name": "Doe",
"profile_url": "https://www.linkedin.com/in/joedoe",
"title": "Director of Sales",
"company_domain": "abc.com",
"company_name": "ABC Corporation",
"country": "United States"
}
}
}
```
# Company New Hires
Source: https://docs.tamradar.com/webhooks/company/new-hires
Company New Hires documentation for TAMradar API.
Notifies you when new employees are detected joining your tracked companies. This radar type helps you monitor team growth and talent acquisition trends.
### Content Structure
The `content` object for new hires contains:
| Field | Type | Required | Nullable | Description |
| ------------ | ------ | -------- | -------- | ---------------------------------------- |
| full\_name | string | Yes | No | Full name of the new hire |
| first\_name | string | Yes | No | First name of the new hire |
| last\_name | string | Yes | No | Last name of the new hire |
| profile\_url | string | Yes | No | URL to the person's professional profile |
| title | string | Yes | No | Job title of the new hire |
| start\_date | string | Yes | No | Best estimated start date (YYYY-MM-DD) |
| country | string | Yes | Yes | Country of employment |
### Example
This example includes [base webhook fields](/webhooks/overview) along with new-hire-specific content:
```json theme={null}
{
"update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
"update_type": "radar_finding",
"record_id": "b77af154-c369-4446-b70a-f328880c3a48",
"discovered_at": "2024-03-20T12:30:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_new_hires",
"domain": "domain.com",
"next_charge_at": "2024-04-20T12:30:00Z",
"custom_fields": {
"priority": "high",
"account_id": "0011U00000TFV7MQAX"
}
},
"content": {
"full_name": "Joe Doe",
"first_name": "Joe",
"last_name": "Doe",
"profile_url": "https://www.linkedin.com/in/joedoe/",
"title": "Senior Product Designer",
"start_date": "2024-03-15",
"country": "United States"
}
}
```
# Company Promotions
Source: https://docs.tamradar.com/webhooks/company/promotions
Company Promotions documentation for TAMradar API.
Notifies you when employees are promoted at your tracked companies. This radar type helps you monitor career progression and organizational changes.
### Content Structure
The `content` object for promotions contains:
| Field | Type | Required | Nullable | Description |
| --------------- | ------ | -------- | -------- | --------------------------------------------- |
| full\_name | string | Yes | No | Full name of the promoted person |
| first\_name | string | Yes | No | First name of the promoted person |
| last\_name | string | Yes | No | Last name of the promoted person |
| profile\_url | string | Yes | No | URL to the person's professional profile |
| previous\_title | string | Yes | No | Job title before the promotion |
| current\_title | string | Yes | No | New job title after the promotion |
| promotion\_date | string | Yes | No | Best estimated date of promotion (YYYY-MM-DD) |
| country | string | Yes | Yes | Country of employment |
### Example
This example includes [base webhook fields](/webhooks/overview) along with promotion-specific content:
```json theme={null}
{
"update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
"update_type": "radar_finding",
"record_id": "b77af154-c369-4446-b70a-f328880c3a48",
"discovered_at": "2024-03-17T08:30:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_promotions",
"domain": "domain.com",
"next_charge_at": "2024-04-20T15:45:00Z",
"custom_fields": {
"priority": "high",
"account_id": "0011U00000TFV7MQAX"
}
},
"content": {
"full_name": "Joe Doe",
"first_name": "Joe",
"last_name": "Doe",
"profile_url": "https://www.linkedin.com/in/joe-doe-755717101/",
"previous_title": "Global Prospecting Specialist",
"current_title": "Analyst, Global Service Center",
"promotion_date": "2024-03-01",
"country": "United States"
}
}
```
# Company Reviews
Source: https://docs.tamradar.com/webhooks/company/reviews
Company Reviews documentation for TAMradar API.
Notifies you when new customer reviews are detected for your tracked companies. This radar type helps you monitor customer feedback and sentiment across various review platforms.
### Content Structure
The `content` object contains the review information:
| Field | Type | Required | Description |
| --------------- | ------ | -------- | -------------------------------------------- |
| review\_content | string | Yes | Text content of the review |
| review\_url | string | Yes | URL to the original review |
| review\_source | string | Yes | Platform where reviewed (e.g., G2, Capterra) |
| review\_rating | string | Yes | Rating as string ("5", "4.5", etc.) |
| reviewed\_at | string | Yes | Best estimated date of review (YYYY-MM-DD) |
### Example
This example includes [base webhook fields](/webhooks/overview) along with review-specific content:
```json theme={null}
{
"update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
"update_type": "radar_finding",
"record_id": "b77af154-c369-4446-b70a-f328880c3a48",
"discovered_at": "2024-03-18T09:15:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_reviews",
"domain": "domain.com",
"next_charge_at": "2024-04-18T09:15:00Z",
"custom_fields": {
"priority": "high",
"account_id": "0011U00000TFV7MQAX"
}
},
"content": {
"review_content": "Great product with excellent customer support. The onboarding process was smooth and the team was very responsive to our needs. Would highly recommend!",
"review_url": "https://www.g2.com/products/domain/reviews/123456",
"review_source": "G2",
"review_rating": "5",
"reviewed_at": "2024-03-18"
}
}
```
# Company Social Engagements
Source: https://docs.tamradar.com/webhooks/company/social-engagements
Company Social Engagements documentation for TAMradar API.
Notifies you when people engage with posts from companies you're tracking on social channels. This includes reactions (like, celebrate, support, etc.), comments, and reposts on the tracked company's posts.
### Content Structure
The `content` object contains the engagement information:
| Field | Type | Required | Description |
| ------------------------ | ------ | -------- | ----------------------------------------------------------------------------- |
| post\_url | string | Yes | URL of the social post that received engagement |
| post\_content | string | Yes | Text content of the original post |
| post\_channel | string | Yes | Social media channel |
| post\_posted\_at | string | Yes | Date when the post was published (`YYYY-MM-DD`) |
| engagement\_type | string | Yes | Type of engagement: `"reaction"`, `"comment"`, or `"repost"` |
| engagement\_content | string | No | Content of the engagement (comment text or repost text). Null for reactions. |
| engagement\_content\_url | string | No | URL of the engagement content (for reposts). Null for reactions and comments. |
| author | object | Yes | Details of the person who engaged with the post |
#### Author Object Structure
The `author` object contains details about the person who engaged:
| Field | Type | Nullable | Description |
| --------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------ |
| profile\_type | string | Yes | Type of profile: `"contact"` or `"company"` |
| first\_name | string | Yes | First name of the person who engaged - `null` if `profile_type` is `"company"` |
| last\_name | string | Yes | Last name of the person - `null` if `profile_type` is `"company"` |
| profile\_url | string | No | Social profile URL of the person or company who engaged |
| title | string | Yes | Job title or position - `null` if `profile_type` is `"company"` |
| company\_name | string | Yes | Company name where the person works, or the engaging company name if `profile_type` is `"company"` |
| company\_domain | string | Yes | Company domain where the person works, or the engaging company domain if `profile_type` is `"company"` |
| country | string | Yes | Country location |
### Examples
These examples include [base webhook fields](/webhooks/overview) along with company-social-engagement-specific content:
#### Contact Engagement Example
When an individual person engages with a company's post:
```json theme={null}
{
"update_id": "fe00b8e8-ae51-4e63-b9af-fc45dbf9517b",
"update_type": "radar_finding",
"record_id": "ae3929d4-d2c6-4008-a756-d5669c919451",
"discovered_at": "2025-10-11T23:20:16Z",
"data": {
"radar_id": "05c4db36-3d28-4475-a4e8-52e0fcf58477",
"radar_type": "company_social_engagements",
"domain": "stripe.com",
"next_charge_at": "2025-11-10T23:21:41Z",
"custom_fields": {
"campaign": "enterprise-accounts",
"priority": "high"
}
},
"content": {
"post_url": "https://www.linkedin.com/posts/stripe_activity-123456789",
"post_content": "Excited to announce our new payment infrastructure for global enterprises! #Payments #Fintech",
"post_channel": "LinkedIn",
"post_posted_at": "2025-10-10",
"engagement_type": "Comment",
"engagement_content": "This looks great! We've been looking for exactly this kind of solution.",
"engagement_content_url": null,
"author": {
"profile_type": "contact",
"first_name": "Sarah",
"last_name": "Johnson",
"profile_url": "https://www.linkedin.com/in/sarahjohnson",
"title": "VP of Finance",
"company_name": "TechCorp Inc",
"company_domain": "techcorp.com",
"country": "United States"
}
}
}
```
#### Company Engagement Example
When a company account engages with another company's post:
```json theme={null}
{
"update_id": "8a1c3f2e-9d4b-4e7f-a5c8-1b3e5f7g9h2j",
"update_type": "radar_finding",
"record_id": "c2f8a9d1-4e6b-4a8c-9f7e-3d5a7b9c1e2f",
"discovered_at": "2025-10-11T15:30:45Z",
"data": {
"radar_id": "05c4db36-3d28-4475-a4e8-52e0fcf58477",
"radar_type": "company_social_engagements",
"domain": "stripe.com",
"next_charge_at": "2025-11-10T23:21:41Z",
"custom_fields": {
"campaign": "enterprise-accounts",
"priority": "high"
}
},
"content": {
"post_url": "https://www.linkedin.com/posts/stripe_activity-987654321",
"post_content": "Stripe continues to lead in payment innovation with our latest security features!",
"post_channel": "LinkedIn",
"post_posted_at": "2025-10-10",
"engagement_type": "reaction",
"engagement_content": null,
"engagement_content_url": null,
"author": {
"profile_type": "company",
"first_name": null,
"last_name": null,
"profile_url": "https://www.linkedin.com/company/techcorp",
"title": null,
"company_name": "TechCorp Inc",
"company_domain": "techcorp.com",
"country": "United States"
}
}
}
```
### Use Cases
**Competitor Intelligence**\
When executives from target accounts engage with your competitors' posts about new product launches or company updates, this signals interest in specific solutions. The engagement data reveals which competitors are getting attention from your prospects and what messaging resonates.
**Market Validation**\
Track how your target market responds to competitor announcements, funding news, or product updates. High engagement from your ideal customer profile validates market demand for specific solutions and helps inform your product strategy.
**Lead Scoring & Timing**\
When someone from a target account actively engages with competitor content through comments and reactions, they're signaling active buying interest. These engaged prospects become high-priority leads for competitive positioning and timely outreach.
**Content Strategy Intelligence**\
Analyze which types of competitor content (product launches, thought leadership, company updates) generate the most engagement from valuable prospects. This intelligence guides your own content strategy and competitive messaging.
### Technical Notes
* **Engagement Limits**: We capture up to 200 engagements per post to ensure timely delivery while covering the most engaged audience
* **Post Monitoring Window**: We look back 48 hours for existing posts and monitor each post for 48 hours total
* **Incremental Updates**: We continuously check for new engagements during the monitoring window, sending webhooks only for net-new activities (no duplicates)
* **Lookback Period for First Day**: If no post is found from the past 48 hours when you created the radar, TAMradar will still try to fetch the company's last post. Keep in mind that this might return posts from the past, so check the `post_posted_at` field to ensure smooth processing
# Company Social Posts
Source: https://docs.tamradar.com/webhooks/company/social-posts
Company Social Posts documentation for TAMradar API.
Notifies you when new social media posts are detected from your tracked companies. This radar type helps you monitor company announcements, marketing campaigns, and social media presence.
### Content Structure
The `content` object contains the social media post information:
| Field | Type | Required | Description |
| ---------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| post\_text | string | No | Text content of the social media post. `null` when `post_type` is `Repost` and the company reshared without adding their own commentary. |
| post\_url | string | Yes | URL to the original social media post |
| post\_channel | string | Yes | Platform where posted (e.g., Twitter, LinkedIn) |
| post\_posted\_at | string | Yes | Best estimated date of posting (YYYY-MM-DD) |
| post\_type | string | Yes | Type of post (e.g., Post, Repost) |
### Example
This example includes [base webhook fields](/webhooks/overview) along with social-posts-specific content:
```json theme={null}
{
"update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
"update_type": "radar_finding",
"record_id": "b77af154-c369-4446-b70a-f328880c3a48",
"discovered_at": "2024-03-22T15:30:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_social_posts",
"domain": "domain.com",
"next_charge_at": "2024-04-20T08:00:00Z",
"custom_fields": {
"priority": "high",
"account_id": "0011U00000TFV7MQAX"
}
},
"content": {
"post_text": "We're excited to announce our new partnership with @TechCorp! Together, we'll be revolutionizing the way companies handle their data analytics. Stay tuned for more updates! #Innovation #Partnership",
"post_url": "https://twitter.com/domain/status/1234567890",
"post_channel": "Twitter",
"post_posted_at": "2024-03-22",
"post_type": "Post"
}
}
```
# Company Social Posts - CXO
Source: https://docs.tamradar.com/webhooks/company/social-posts-cxo
Company Social Posts - CXO documentation for TAMradar API.
Notifies you when new social media posts are detected from executives (CXO level) of your tracked companies. This radar type helps you monitor leadership communication and strategic announcements.
### Content Structure
The `content` object contains the social media post information:
| Field | Type | Nullable | Description |
| ---------------- | ------ | -------- | ----------------------------------------------- |
| post\_text | string | No | Text content of the social media post |
| post\_url | string | No | URL to the original social media post |
| post\_channel | string | No | Platform where posted (e.g., Twitter, LinkedIn) |
| post\_posted\_at | string | No | Best estimated date of posting (YYYY-MM-DD) |
| post\_type | string | No | Type of post (e.g., Post, Repost) |
| author | object | No | Information about the executive who posted |
#### Author Object
| Field | Type | Nullable | Description |
| --------------- | ------ | -------- | ------------------------------------------------------------------------------ |
| first\_name | string | No | First name of the executive (may be the full profile name from some platforms) |
| last\_name | string | Yes | Last name of the executive (can be null if platform provides only full name) |
| profile\_url | string | No | URL to the executive's professional profile |
| title | string | Yes | Current job title (can be null if unavailable on certain platforms) |
| company\_name | string | Yes | Name of the company where the executive works |
| company\_domain | string | Yes | Domain of the company where the executive works |
| country | string | Yes | Country of the executive (can be null if location unavailable) |
### Example
This example includes [base webhook fields](/webhooks/overview) along with CXO-posts-specific content:
```json theme={null}
{
"update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
"update_type": "radar_finding",
"record_id": "b77af154-c369-4446-b70a-f328880c3a48",
"discovered_at": "2024-03-21T09:45:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "company_social_posts_cxo",
"domain": "domain.com",
"next_charge_at": "2024-04-20T11:20:00Z",
"custom_fields": {
"priority": "high",
"account_id": "0011U00000TFV7MQAX"
}
},
"content": {
"post_text": "Thrilled to announce our latest AI breakthrough! Our team has developed a groundbreaking algorithm that will revolutionize how businesses handle data processing. This is just the beginning of our AI journey. #Innovation #AI #TechLeadership",
"post_url": "https://twitter.com/JohnDoeCEO/status/1234567890",
"post_channel": "Twitter",
"post_posted_at": "2024-03-21",
"post_type": "Post",
"author": {
"first_name": "John",
"last_name": "Doe",
"profile_url": "https://www.linkedin.com/in/johndoe/",
"title": "Chief Executive Officer",
"company_name": "Tech Innovations Inc",
"company_domain": "domain.com",
"country": "United States"
}
}
}
```
# Contact Job Changes
Source: https://docs.tamradar.com/webhooks/contact/job-changes
Contact Job Changes documentation for TAMradar API.
Notifies you when tracked individuals experience career changes such as job departures, promotions, additional roles, or moves to new companies. This radar type helps you monitor contact movements and relationship changes.
### Content Structure
The `content` object contains the job change information:
| Field | Type | Required | Description |
| -------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| change\_type | string | Yes | Type of change: "departure", "promotion", "additional\_role", "new\_company\_role" |
| profile\_url | string | Yes | LinkedIn profile URL of the contact for identification |
| new\_role | object | Yes | Details of the new role. Fields contain null values for 'departure' type changes. |
| previous\_role | object | Yes | Details of the previous role at the tracked domain. Fields contain null values for 'additional\_role' type changes and when no previous role exists. |
#### Change Types
| Type | Description |
| ------------------ | ------------------------------------------------------------------------------------------ |
| `departure` | Contact left the tracked company; no new role detected yet |
| `promotion` | Contact received a promotion or role change within the same company |
| `additional_role` | Contact gained an additional role (advisor, board member) while staying at tracked company |
| `new_company_role` | Contact left the tracked company and joined another company |
#### Change Type Examples
| Change Type | new\_role | previous\_role | Use Case |
| ------------------ | ------------------------------------- | ----------------------------------------- | ---------------------------------------- |
| `departure` | All fields are null | Populated (last role at tracked company) | Contact left your tracked company |
| `promotion` | Populated (new role at same company) | Populated (previous role at same company) | Title/role change within tracked company |
| `additional_role` | Populated (role at different company) | All fields are null | Added advisor/board role while staying |
| `new_company_role` | Populated (role at different company) | Populated (last role at tracked company) | Left tracked company for new position |
#### Role Object Structure
Both `new_role` and `previous_role` objects contain:
| Field | Type | Description |
| --------------- | ------ | ------------------------------------------- |
| title | string | Job title or position |
| company\_name | string | Company name |
| company\_domain | string | Company domain |
| start\_date | string | Role start date (YYYY-MM-DD) |
| end\_date | string | Role end date (YYYY-MM-DD), null if current |
Both role objects are always present with consistent structure. Individual fields contain null values when information is not available or not applicable.
### Example
This example includes [base webhook fields](/webhooks/overview) along with contact-job-change-specific content:
```json theme={null}
{
"update_id": "15c83d05-f81a-4b6c-a64d-e594b13d11a7",
"update_type": "radar_finding",
"record_id": "b8b9c213-4ed2-4b7f-8e46-7a1398d4e2fa",
"discovered_at": "2025-07-02T19:03:43Z",
"data": {
"radar_id": "4e8f0a7b-6c52-4a4b-b15c-3a7f9b5c1d9d",
"radar_type": "contact_job_changes",
"domain": "acme.com",
"next_charge_at": "2025-08-02T19:03:43Z",
"custom_fields": {
"contact_tier": "enterprise",
"account_id": "0011U00000ABCDEF"
},
"profile_url": "https://www.linkedin.com/in/jane-smith-marketing/",
"email": "jane.smith@acme.com",
"full_name": "Jane Smith"
},
"content": {
"change_type": "new_company_role",
"profile_url": "https://www.linkedin.com/in/jane-smith-marketing/",
"new_role": {
"title": "Director of Marketing Operations",
"company_name": "ExampleCorp",
"company_domain": "example.com",
"start_date": "2024-12-01",
"end_date": null
},
"previous_role": {
"title": "Senior Marketing Manager",
"company_name": "ACME Inc",
"company_domain": "acme.com",
"start_date": "2021-07-01",
"end_date": "2024-12-01"
}
}
}
```
### Important Notes
**Contact Identification**: Contacts are identified during radar creation using email, LinkedIn profile URL, or full name.
**Multiple Contacts**: You can track multiple individuals at the same company with separate radars.
**Privacy**: Profile URLs are included for contact identification; other personal identifiers remain excluded.
**Tracking Period**: Changes are detected based on publicly available professional information.
**Previous Role Context**: The `previous_role` object contains the role at the company you originally tracked, not necessarily the most recent role the person held. For example, if you're tracking someone at Microsoft and they move to Meta, then later move to Google, the `previous_role` will show their Microsoft role (your tracked company), not their Meta role.
**Tracking Continuity**: If you do not want to track contacts at new companies that TAMradar identifies, you should deactivate the radar. For example, if you're tracking someone at Microsoft and they move to Meta, TAMradar will automatically start tracking their Meta role. Deactivate the radar if you only want to monitor the original company.
# Contact Social Engagements
Source: https://docs.tamradar.com/webhooks/contact/social-engagements
Contact Social Engagements documentation for TAMradar API.
Notifies you when people engage with posts from contacts you're tracking on social channels. This includes reactions (like, celebrate, support, etc.), comments, and reposts on the tracked contact's posts.
### Content Structure
The `content` object contains the engagement information:
| Field | Type | Required | Description |
| ------------------------ | ------ | -------- | ----------------------------------------------------------------------------- |
| post\_url | string | Yes | URL of the social post that received engagement |
| post\_content | string | Yes | Text content of the original post |
| post\_channel | string | Yes | Social media channel (always "linkedin") |
| post\_posted\_at | string | Yes | Date when the post was published (YYYY-MM-DD) |
| engagement\_type | string | Yes | Type of engagement: "reaction", "comment", or "repost" |
| engagement\_content | string | No | Content of the engagement (comment text or repost text). Null for reactions. |
| engagement\_content\_url | string | No | URL of the engagement content (for reposts). Null for reactions and comments. |
| author | object | Yes | Details of the person who engaged with the post |
#### Author Object Structure
The `author` object contains details about the person who engaged:
| Field | Type | Nullable | Description |
| --------------- | ------ | -------- | --------------------------------------------------------------- |
| profile\_type | string | Yes | Type of profile: "contact" or "company" |
| first\_name | string | No | First name of the person who engaged, or company name |
| last\_name | string | Yes | Last name of the person - `null` if `profile_type` is `company` |
| profile\_url | string | No | Social profile URL of the person who engaged |
| title | string | Yes | Job title or position |
| company\_name | string | Yes | Company name where they work |
| company\_domain | string | Yes | Company domain where they work |
| country | string | Yes | Country location |
### Example
This example includes [base webhook fields](/webhooks/overview) along with contact-social-engagement-specific content:
```json theme={null}
{
"update_id": "fe00b8e8-ae51-4e63-b9af-fc45dbf9517b",
"update_type": "radar_finding",
"record_id": "ae3929d4-d2c6-4008-a756-d5669c919451",
"discovered_at": "2025-10-11T23:20:16Z",
"data": {
"radar_id": "05c4db36-3d28-4475-a4e8-52e0fcf58477",
"radar_type": "contact_social_engagements",
"domain": null,
"next_charge_at": "2025-11-10T23:21:41Z",
"custom_fields": {
"account_manager": "sarah.smith",
"priority": "high"
}
},
"content": {
"post_url": "https://www.linkedin.com/posts/someone_activity-123456789",
"post_content": "Just launched our new AI product! #AI #ProductLaunch",
"post_channel": "LinkedIn",
"post_posted_at": "2025-10-10",
"engagement_type": "Reaction",
"engagement_content": null,
"engagement_content_url": null,
"author": {
"profile_type": "contact",
"first_name": "John",
"last_name": "Smith",
"profile_url": "https://www.linkedin.com/in/johnsmith",
"title": "VP of Engineering",
"company_name": "Tech Corp",
"company_domain": "techcorp.com",
"country": "United States"
}
}
}
```
### Use Cases
**Sales Intelligence**\
When a VP or C-level executive from a target account engages with your tracked contact's post about industry trends, this signals interest and creates a warm introduction opportunity. The engagement data provides context for outreach - you know what content resonated with them.
**Relationship Mapping**\
By tracking who engages with your key contacts' posts, you can identify hidden connections and influencers within target organizations. If multiple people from the same company engage with content, it reveals the internal network and decision-making structure.
**Engagement Pattern Analysis**\
Track which types of content (product launches, thought leadership, company updates) generate the most engagement from valuable contacts. This helps understand what messaging resonates with your target audience and when they're most active.
**Warm Lead Identification**\
When someone consistently engages with your tracked contact's content through comments and reactions, they're demonstrating active interest. These engaged individuals become warm leads for outreach, as they're already familiar with your contact's expertise and perspectives.
### Technical Notes
* **Engagement Limits**: We capture up to 200 engagements per post to ensure timely delivery while covering the most engaged audience
* **Post Monitoring Window**: We look back 48 hours for existing posts and monitor each post for 48 hours total
* **Incremental Updates**: We continuously check for new engagements during the monitoring window, sending webhooks only for net-new activities (no duplicates)
* **Lookback Period for First Day**: If no post is found from the past 48 hours when you created the radar, TAMradar will still try to fetch the person's last post. Keep in mind that this might return posts from the past, so check the `post_posted_at` field to ensure smooth processing
# Contact Social Posts
Source: https://docs.tamradar.com/webhooks/contact/social-posts
Contact Social Posts documentation for TAMradar API.
Notifies you when new social media posts are published BY specific contacts (individuals) you're tracking. This radar type helps you monitor personal communication and updates from key contacts at target companies or industry professionals.
**Key difference**: This radar tracks posts authored BY your contacts, while company social radars track posts ABOUT companies.
### Content Structure
The `content` object contains the social media post information:
| Field | Type | Nullable | Description |
| ---------------- | ------ | -------- | --------------------------------------------------- |
| post\_text | string | No | Text content of the social media post |
| post\_url | string | No | URL to the original social media post |
| post\_channel | string | No | Platform where posted (e.g., "LinkedIn", "Twitter") |
| post\_posted\_at | string | No | Best estimated date of posting (YYYY-MM-DD) |
| post\_type | string | No | Type of post (e.g., Post, Repost) |
| author | object | No | Information about the contact who posted |
#### Author Object
| Field | Type | Nullable | Description |
| --------------- | ------ | -------- | ---------------------------------------------------------------------------- |
| first\_name | string | No | First name of the contact (may be the full profile name from some platforms) |
| last\_name | string | Yes | Last name of the contact (can be null if platform provides only full name) |
| profile\_url | string | No | URL to the contact's professional profile |
| title | string | Yes | Current job title (can be null if unavailable on certain platforms) |
| company\_name | string | Yes | Name of the company where the contact works |
| company\_domain | string | Yes | Domain of the company where the contact works |
| country | string | Yes | Country of the contact (can be null if location unavailable) |
### Example
This example includes [base webhook fields](/webhooks/overview) along with contact-social-posts-specific content:
```json theme={null}
{
"update_id": "41991828-ed31-4bd9-98d2-44b2763f9f35",
"update_type": "radar_finding",
"record_id": "b77af154-c369-4446-b70a-f328880c3a48",
"discovered_at": "2024-03-21T09:45:00Z",
"data": {
"radar_id": "c70813b3-7e87-4ca7-90fd-8c64574d911b",
"radar_type": "contact_social_posts",
"domain": "stripe.com",
"next_charge_at": "2024-04-20T11:20:00Z",
"custom_fields": {
"priority": "high",
"account_id": "0011U00000TFV7MQAX"
},
"profile_url": "https://www.linkedin.com/in/sarahjohnson/"
},
"content": {
"post_text": "Just wrapped up an amazing quarter! Our team's dedication to innovation continues to drive incredible results. Excited to share what we're building next. #TeamWork #Innovation #Growth",
"post_url": "https://www.linkedin.com/feed/update/activity:1234567890123456789/",
"post_channel": "LinkedIn",
"post_posted_at": "2024-03-21",
"post_type": "Post",
"author": {
"first_name": "Sarah",
"last_name": "Johnson",
"profile_url": "https://www.linkedin.com/in/sarahjohnson/",
"title": "VP of Engineering",
"company_name": "Stripe",
"company_domain": "stripe.com",
"country": "United States"
}
}
}
```
# Failure Webhook Payload Example
Source: https://docs.tamradar.com/webhooks/failure
Failure Webhook Payload Example documentation for TAMradar API.
```json theme={null}
{
"update_id": "550e8400-e29b-41d4-a716-446655440000",
"update_type": "radar_failure",
"record_id": null,
"discovered_at": "2024-03-20T12:30:00Z",
"data": {
"radar_id": "54e32291-25a4-459f-8bd6-4f79489ea931",
"radar_type": "company_job_openings",
"domain": "example.com",
"next_charge_at": null,
"custom_fields": {
"priority": "high"
}
},
"status": "error",
"code": 400,
"message": "Radar creation failed: missing prerequisites.",
"errors": [
{
"field": "radar",
"reason": "missing_prerequisites"
}
],
"refund_amount_usd": 0.1,
"completed_at": "2024-03-20T12:30:00Z",
"timestamp": "2024-03-20T12:30:00Z",
"error_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
# Industry Funding Rounds
Source: https://docs.tamradar.com/webhooks/industry/funding-rounds
Industry Funding Rounds documentation for TAMradar API.
The Industry Funding Rounds radar tracks venture capital and private equity investments globally. Unlike company-specific radars, this monitors ALL funding activity and delivers it to your webhook endpoint. You can activate a `industry_funding_rounds`radar via the industry radar [endpoint](/api-reference/post-radars-industry).
### Content Structure
The `content` object for industry funding rounds contains:
| Field | Type | Required | Nullable | Description |
| ------------- | ------ | -------- | -------- | ------------------------------------------- |
| amount\_usd | number | Yes | No | Funding amount in USD |
| announced\_at | string | Yes | No | Date announced (YYYY-MM-DD format) |
| round | string | Yes | No | Funding round type (see Round Types below) |
| company | object | Yes | No | Information about the company raising funds |
| references | array | Yes | No | Source URLs for the funding announcement |
#### Company Object
| Field | Type | Required | Nullable | Description |
| ------------ | ------ | -------- | -------- | ---------------------------- |
| name | string | Yes | No | Company name |
| domain | string | Yes | No | Company website domain |
| profile\_url | string | No | Yes | Company social media profile |
| hq\_country | string | No | Yes | Company headquarters country |
#### Round Types
The `round` field can contain one of the following values:
* `Pre-seed`
* `Seed`
* `Series A`
* `Series B`
* `Series C`
* `Series D`
* `Series E`
* `Series F`
* `Series G`
* `Bridge`
* `Bridge Financing`
* `Debt Financing`
* `Growth Financing`
* `Unknown`
* `Crowdfunding`
* `Initial Public Offering`
* `Initial Coin Offering`
* `Private Equity`
### Example
This example includes base webhook fields along with industry-funding-rounds-specific content.
```json theme={null}
{
"update_id": "123e4567-e89b-12d3-a456-426614174000",
"update_type": "radar_finding",
"record_id": "456e7890-e89b-12d3-a456-426614174001",
"discovered_at": "2025-09-29T14:30:00Z",
"data": {
"radar_id": "789e0123-e89b-12d3-a456-426614174002",
"radar_type": "industry_funding_rounds",
"domain": null,
"next_charge_at": "2025-10-29T14:30:00Z",
"custom_fields": {
"client_ref": "ACME-001",
"region": "north-america"
}
},
"content": {
"amount_usd": 50000000,
"announced_at": "2025-09-28",
"round": "Series B",
"company": {
"name": "TechStartup Inc",
"domain": "techstartup.com",
"profile_url": "https://linkedin.com/company/techstartup-inc",
"hq_country": "United States"
},
"references": [
"https://techcrunch.com/2025/09/28/techstartup-raises-50m-series-b",
"https://venturebeat.com/2025/09/28/techstartup-funding"
]
}
}
```
# Industry Job Openings
Source: https://docs.tamradar.com/webhooks/industry/job-openings
Industry Job Openings documentation for TAMradar API.
Notifies you when job postings matching your keyword and location filters are discovered across the job market. This radar type helps you identify companies using certain technologies (e.g. Ramp), working on specific projects (e.g. cost cutting), or track hiring patterns based on job titles (e.g. GTM engineer).
### Content Structure
The `content` object contains the job opening and company information:
| Field | Type | Required | Description |
| ------------ | ------ | -------- | ---------------------------------------- |
| job\_opening | object | Yes | Details about the discovered job posting |
| company | object | Yes | Information about the hiring company |
#### Job Opening Object Structure
| Field | Type | Required | Description |
| ----------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| title | string | Yes | Job title or position name |
| url | string | Yes | Direct URL to the job posting |
| posted\_at | string | No | Date when job was posted (YYYY-MM-DD format), null when the exact job posting date cannot be reliably determined |
| description | string | Yes | Full job description text |
| source | string | Yes | Platform where job was found (e.g., "LinkedIn", "Indeed") |
| country | string | No | Country where job is located |
| seniority | string | No | Seniority level: "Entry", "Senior", "Manager", "Director", "CXO", "Vice President", "Partner", "Owner", "Other" |
| department | string | No | Department category: "Engineering", "Sales", "Marketing", "Product", "Customer Success", "Operations", "Finance", "HR", "Legal", "Design", "Data" |
#### Company Object Structure
| Field | Type | Required | Description |
| ------------ | ------ | -------- | -------------------------------------------------------------------------------------------------------------- |
| name | string | Yes | Company name |
| domain | string | Yes | Company website domain |
| hq\_country | string | No | Company headquarters country |
| profile\_url | string | No | LinkedIn company profile URL |
| size | string | No | Company size range: "1", "2-10", "11-50", "51-200", "201-500", "501-1000", "1001-5000", "5001-10000", "10001+" |
| industry | string | No | Company industry category |
### Filtering Behavior
* **Keyword Matching**: Jobs are filtered by the keyword appearing in the job title or description
* **Location Filtering**: When countries are specified, only jobs in those locations are included
* **Deduplication**: Same jobs found on different platforms or with slight variations are automatically deduplicated
* **Freshness**: Jobs older than 2 days are still processed but marked with their original posting date
### Example
This example includes [base webhook fields](/webhooks/overview) along with industry-job-openings-specific content:
```json theme={null}
{
"update_id": "7f2f1b8e-2c3d-4e91-b6a5-9f0d1a2b3c4d",
"update_type": "radar_finding",
"record_id": "3a9b1b0e-8d3a-4f0b-9237-1f2c3d4e5f6a",
"discovered_at": "2024-03-19T10:15:30Z",
"data": {
"radar_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"radar_type": "industry_job_openings",
"keyword": "growth marketing",
"countries": ["United States", "Canada"],
"domain": null,
"next_charge_at": "2024-04-20T12:30:00Z",
"custom_fields": {
"campaign_id": "q4-2024-hiring",
"priority": "high"
}
},
"content": {
"job_opening": {
"title": "Senior Growth Marketing Manager",
"url": "https://www.acme.com/jobs/123456789",
"posted_at": "2024-03-20",
"description": "We are seeking a Senior Growth Marketing Manager to drive user acquisition and retention strategies for our SaaS platform.",
"source": "Company Website",
"country": "United States",
"seniority": "Senior",
"department": "Marketing"
},
"company": {
"name": "Acme Inc",
"domain": "acme.com",
"hq_country": "United States",
"profile_url": "https://linkedin.com/company/acme",
"size": "201-500",
"industry": "Computer Software"
}
}
}
```
### Use Cases
* **Technology Adoption Tracking**: Monitor companies hiring for specific technologies ("React developer", "Salesforce admin", "Snowflake engineer") to identify potential customers
* **Project Signal Detection**: Discover companies working on specific initiatives through job descriptions ("cost optimization", "digital transformation", "AI implementation")
* **Emerging Role Monitoring**: Track new or specialized roles that indicate market trends ("GTM engineer", "Developer Relations", "Revenue Operations")
* **Sales Prospecting**: Find companies hiring roles that indicate budget/growth/pain points relevant to your solution
### Important Notes
* **Company Domain Identification**: Company website domains are identified through best-effort matching and enrichment. In some cases, the domain may be estimated or unavailable, particularly for newer companies or those without strong online presence
* **Data Timing**: Job posting dates are sourced from the platform when available, or estimated based on discovery timing when not provided by the source. Rest assured these are recent postings - we focus on fresh job opportunities and share the most accurate timing information availablewa
# Industry Mentions
Source: https://docs.tamradar.com/webhooks/industry/mentions
Industry Mentions documentation for TAMradar API.
### Content Structure
The `content` object for industry mentions contains:
| Field | Type | Nullable | Description |
| ------------------- | ------ | -------- | -------------------------------------------------- |
| mention\_content | string | No | Content of the post or article |
| mention\_url | string | No | URL to the original post or article |
| mention\_channel | string | No | Platform or source (e.g., Twitter, LinkedIn, News) |
| mention\_posted\_at | string | Yes | Best estimated date of the mention (YYYY-MM-DD) |
| author | object | Yes | Information about the author of the mention |
#### Author Object
| Field | Type | Nullable | Description |
| --------------- | ------ | -------- | --------------------------- |
| first\_name | string | No | Author's first name |
| last\_name | string | Yes | Author's last name |
| profile\_url | string | No | URL to the author's profile |
| title | string | Yes | Author's current title |
| company\_domain | string | Yes | Author's company domain |
| company\_name | string | Yes | Author's company name |
| country | string | Yes | Author's country |
### Example
This example includes base webhook fields along with industry-mentions-specific content.
```json theme={null}
{
"update_id": "7f2f1b8e-2c3d-4e91-b6a5-9f0d1a2b3c4d",
"update_type": "radar_finding",
"record_id": "3a9b1b0e-8d3a-4f0b-9237-1f2c3d4e5f6a",
"discovered_at": "2024-03-19T10:15:30Z",
"data": {
"radar_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"radar_type": "industry_mentions",
"keyword": "artificial intelligence",
"domain": null,
"next_charge_at": "2024-04-20T12:30:00Z",
"custom_fields": {
"priority": "high",
"campaign": "ai-trends"
}
},
"content": {
"mention_content": "The latest advances in artificial intelligence are transforming enterprise software, particularly in areas of automation and predictive analytics.",
"mention_url": "https://www.linkedin.com/posts/joedoe-top-7360626072800903169-s4NE",
"mention_channel": "LinkedIn",
"mention_posted_at": "2024-03-18",
"author": {
"first_name": "Joe",
"last_name": "Doe",
"profile_url": "https://www.linkedin.com/in/joedoe",
"title": "Director of Sales",
"company_domain": "abc.com",
"company_name": "ABC Corporation",
"country": "United States"
}
}
}
```
# Industry New Companies
Source: https://docs.tamradar.com/webhooks/industry/new-companies
Industry New Companies documentation for TAMradar API.
The Industry New Companies radar tracks newly launched startups and entrepreneurs coming out of stealth mode globally. Unlike company-specific radars, this monitors ALL new company formations and delivers them to your webhook endpoint. You can activate an `industry_new_companies` radar via the industry radar [endpoint](/api-reference/post-radars-industry).
### Content Structure
The `content` object for industry new companies contains:
| Field | Type | Required | Description |
| ------- | ------ | -------- | -------------------------------------------- |
| person | object | Yes | Information about the founder/entrepreneur |
| company | object | Yes | Information about the newly launched company |
#### Person Object
| Field | Type | Required | Description |
| ------------------- | ------- | -------- | ---------------------------------------------------------- |
| first\_name | string | Yes | Founder's first name |
| last\_name | string | Yes | Founder's last name |
| about | string | No | Brief description or bio of the founder |
| profile\_url | string | Yes | URL to founder's professional profile |
| country | string | No | Founder's country |
| ex\_founder | boolean | Yes | Whether this person was a founder before |
| became\_founder\_at | string | Yes | Date when they became founder of this company (YYYY-MM-DD) |
| roles | array | Yes | Array of role objects (current and previous) |
#### Role Object
| Field | Type | Required | Description |
| --------------- | ------- | -------- | -------------------------------- |
| title | string | Yes | Job title |
| location | string | No | Job location |
| company\_name | string | No | Company name |
| company\_domain | string | No | Company website domain |
| start\_date | string | No | Role start date (YYYY-MM-DD) |
| end\_date | string | No | Role end date (YYYY-MM-DD) |
| is\_current | boolean | Yes | Whether this is the current role |
| description | string | No | Role description |
#### Company Object
| Field | Type | Required | Description |
| ------------- | ------- | -------- | ---------------------------------------- |
| name | string | Yes | Company name |
| domain | string | No | Company website domain |
| profile\_url | string | No | Company social media profile |
| founded\_at | number | No | Year company was founded |
| description | string | No | Company description |
| industry | string | No | Industry category |
| categories | array | No | Array of business category strings |
| hq\_country | string | No | Company headquarters country |
| stealth\_mode | boolean | Yes | Whether company is still in stealth mode |
### Example
This example includes base webhook fields along with industry-new-companies-specific content.
```json theme={null}
{
"update_id": "123e4567-e89b-12d3-a456-426614174000",
"update_type": "radar_finding",
"record_id": "456e7890-e89b-12d3-a456-426614174001",
"discovered_at": "2025-09-29T14:30:00Z",
"data": {
"radar_id": "789e0123-e89b-12d3-a456-426614174002",
"radar_type": "industry_new_companies",
"domain": null,
"next_charge_at": "2025-10-29T14:30:00Z",
"custom_fields": {
"client_ref": "ACME-001",
"region": "north-america"
}
},
"content": {
"person": {
"first_name": "Sarah",
"last_name": "Chen",
"about": "Former Google ML engineer building the future of autonomous logistics",
"profile_url": "https://linkedin.com/in/sarahchen",
"country": "United States",
"ex_founder": false,
"became_founder_at": "2025-09-15",
"roles": [
{
"title": "Founder & CEO",
"location": null,
"company_name": "LogiFlow AI",
"company_domain": "logiflow.ai",
"start_date": "2025-09-15",
"end_date": null,
"is_current": true,
"description": "Building AI-powered autonomous logistics platform"
},
{
"title": "Senior ML Engineer",
"location": "Mountain View, CA",
"company_name": "Google",
"company_domain": "google.com",
"start_date": "2020-01-01",
"end_date": "2025-08-31",
"is_current": false,
"description": "Led machine learning initiatives for search algorithms"
}
]
},
"company": {
"name": "LogiFlow AI",
"domain": "logiflow.ai",
"profile_url": "https://linkedin.com/company/logiflow-ai",
"founded_at": 2025,
"description": "AI-powered autonomous logistics platform for last-mile delivery",
"industry": "Computer Software",
"categories": ["B2B", "SaaS", "AI/ML"],
"hq_country": "United States",
"stealth_mode": false
}
}
}
```
## Stealth Mode Behavior
When `stealth_mode: true`, company data is intentionally limited as the startup is hiding details:
**Typically null:**
* `domain` - No public website yet
* `profile_url` - No LinkedIn company page
* `description` - Not revealing business details
* `industry` - Keeping sector secret
* `categories` - Empty array `[]`, no classification
**Still available:**
* `name` - Some company name (may be generic)
* `founded_at` - Incorporation year
* `hq_country` - Legal incorporation location
**Founder data remains complete** since it comes from public professional profiles, not company disclosure.
# Webhook Payloads
Source: https://docs.tamradar.com/webhooks/overview
Webhook delivery model, payload envelope, update types, and testing.
## How TAMradar delivers updates
TAMradar supports two ways to receive radar findings:
* **Webhooks (push)** โ TAMradar POSTs events to your `webhook_url` in real-time. Set `webhook_url` when creating a radar to enable this.
* **Polling (pull)** โ Fetch findings on demand via `GET /v1/updates`. Works for all radars, including those created without a `webhook_url`.
Both delivery methods use the same payload format documented below.
## Payload Libraries by Category
### Company Payloads
* [Company Mentions](/webhooks/company/mentions)
* [Company Social Posts](/webhooks/company/social-posts)
* [Company Social Posts (CXO)](/webhooks/company/social-posts-cxo)
* [Company Social Engagements](/webhooks/company/social-engagements)
* [Company Job Openings](/webhooks/company/job-openings)
* [Company New Hires](/webhooks/company/new-hires)
* [Company Promotions](/webhooks/company/promotions)
* [Company Reviews](/webhooks/company/reviews)
### Contact Payloads
* [Contact Job Changes](/webhooks/contact/job-changes)
* [Contact Social Posts](/webhooks/contact/social-posts)
* [Contact Social Engagements](/webhooks/contact/social-engagements)
### Industry Payloads
* [Industry Mentions](/webhooks/industry/mentions)
* [Industry Funding Rounds](/webhooks/industry/funding-rounds)
* [Industry New Companies](/webhooks/industry/new-companies)
* [Industry Job Openings](/webhooks/industry/job-openings)
## Setting Up Webhook Delivery (optional)
If you want real-time push delivery, set up an HTTPS endpoint:
1. Create an HTTPS endpoint on your server that can receive POST requests
2. Pass `webhook_url` when creating your radar
3. Ensure your endpoint returns a 2xx status code to acknowledge receipt
> **No webhook?** Skip `webhook_url` entirely to create a poll-only radar. Findings are always available via `GET /v1/updates` regardless.
## Webhook Delivery & Reliability
### Timeouts
TAMradar waits **10 seconds** for your endpoint to respond. If no response is received within that window, the attempt is treated as a failure and retried.
### Retries & backoff
If your endpoint returns a non-2xx status (including 5xx) or times out, TAMradar retries up to **3 times** with exponential backoff:
| Attempt | Delay before retry |
| --------- | ------------------ |
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 15 minutes |
After 4 total attempts (1 initial + 3 retries), delivery is permanently abandoned.
### Data is never lost
Push delivery failure does not mean data loss. Every event is written to our internal store before delivery is attempted โ findings are always retrievable via `GET /v1/updates`, regardless of webhook delivery outcome. Use polling as a fallback if your endpoint experiences downtime.
### Deduplication
On retry, the same `update_id` is resent. Use `update_id` as your idempotency key to deduplicate events safely.
## Webhook Payload Format
All webhook payloads follow this structure:
```json theme={null}
{
"update_id": "550e8400-e29b-41d4-a716-446655440000",
"update_type": "radar_finding",
"record_id": "c4d5e6f7-a8b9-4c0d-b1e2-f3a4b5c6d7e8",
"discovered_at": "2024-03-20T12:30:00Z",
"data": {
"radar_id": "54e32291-25a4-459f-8bd6-4f79489ea931",
"radar_type": "company_job_openings",
"domain": "example.com",
"custom_fields": {
"priority": "high",
"target_account_id": "0011U00000TFV7MQAX"
}
},
"content": {
// Event-specific data
}
}
```
### Base Fields
| Field | Type | Required | Description |
| -------------- | ------ | -------- | ------------------------------------------------ |
| update\_id | string | Yes | Unique identifier for this webhook update |
| update\_type | string | Yes | Type of update (see Update Types below) |
| record\_id | string | No | Unique identifier for the detected record |
| radar\_type | string | Yes | Type of radar that generated the event |
| discovered\_at | string | Yes | ISO timestamp (UTC) when the update was detected |
### Update Types
| Update Type | Description |
| ---------------- | ------------------------------------------------------------ |
| `radar_finding` | New data detected by a radar (default) |
| `radar_failure` | Radar setup or billing failure (may include refund metadata) |
| `radar_created` | Async bulk item was created successfully |
| `bulk_completed` | Async bulk request finished processing all items |
### Per-item bulk webhook events
Before the final `bulk_completed`, each item delivers its own webhook as it finishes:
* **`radar_created`** โ item was created successfully. Includes `code: 201`, `message: "Radar created successfully"`, `completed_at`, and a full `data` block matching the single-radar creation response.
* **`radar_failure`** โ item failed validation, conflict, or billing. Includes `completed_at`, `code`, `errors[]`, and `message` matching the sync API error shape. Also emits `discovered_at` (same value as `completed_at`) for backwards compatibility โ prefer `completed_at` going forward.
`radar_created` example:
```json theme={null}
{
"update_id": "efbf7c77-c1ae-4a0d-85b6-116674e9e0b1",
"update_type": "radar_created",
"record_id": null,
"code": 201,
"message": "Radar created successfully",
"completed_at": "2026-05-25T11:17:20Z",
"bulk_id": "5f9da22e-4778-427c-b7e4-dab2a8d9d709",
"item_index": 0,
"custom_fields": { "crm_account_id": "acc_001" },
"status": "success",
"data": {
"radar_id": "2ffb1a07-ac95-4a6d-bc34-39f479f1c856",
"radar_type": "company_job_openings",
"domain": "stripe.com",
"radar_status": "active",
"webhook_url": "https://your-domain.com/webhooks/tamradar",
"created_at": "2026-05-25T11:17:18Z",
"deactivated_at": null,
"next_charge_at": "2026-06-25T11:17:18Z",
"custom_fields": { "crm_account_id": "acc_001" }
}
}
```
`radar_failure` example:
```json theme={null}
{
"update_id": "a1b2c3d4-0001-4000-b000-000000000001",
"update_type": "radar_failure",
"record_id": null,
"completed_at": "2026-05-25T11:17:26Z",
"discovered_at": "2026-05-25T11:17:26Z",
"bulk_id": "5f9da22e-4778-427c-b7e4-dab2a8d9d709",
"item_index": 1,
"custom_fields": {},
"status": "error",
"code": 409,
"message": "Conflict: Radar already exists",
"errors": [
{
"field": "domain",
"reason": "A radar with this domain and type already exists for your account. Conflicting radar_id: 3f1d8e8a-39c4-47e8-a4b4-f7db6a2e50f0. You can view it using GET /radars/{radar_id}",
"conflicting_radar_id": "3f1d8e8a-39c4-47e8-a4b4-f7db6a2e50f0"
}
],
"refund_amount_usd": 0,
"error_id": "45ed35e5-986c-4496-afd8-9a84f6baa410",
"timestamp": "2026-05-25T11:17:26.000Z"
}
```
### Bulk `update_id` semantics
For `bulk_completed` events, two ID levels can appear and they serve different purposes:
* Top-level `update_id`: idempotency key for the whole `bulk_completed` webhook event.
* Per-item `radars[].update_id`: correlation key for each item's earlier `radar_created` or `radar_failure` event.
Store both when you consume bulk webhooks:
* Deduplicate `bulk_completed` retries by top-level `update_id`.
* Correlate each `radars[]` item back to its individual item event with `radars[].update_id`.
`bulk_completed` payloads use a batch envelope with `completed_at`, `summary`, and `radars[]`. Each entry in `radars[]` is the verbatim payload from that item's per-item webhook:
```json theme={null}
{
"update_id": "9a2f0f77-5c8b-4b3e-b636-3ad539f4de65",
"update_type": "bulk_completed",
"bulk_id": "5f9da22e-4778-427c-b7e4-dab2a8d9d709",
"completed_at": "2026-05-25T11:17:33Z",
"summary": {
"total": 3,
"created": 2,
"failed": 1
},
"radars": [
{
"update_id": "efbf7c77-c1ae-4a0d-85b6-116674e9e0b1",
"update_type": "radar_created",
"code": 201,
"message": "Radar created successfully",
"completed_at": "2026-05-25T11:17:20Z",
"bulk_id": "5f9da22e-4778-427c-b7e4-dab2a8d9d709",
"item_index": 0,
"custom_fields": { "crm_account_id": "acc_001" },
"status": "success",
"data": { "radar_id": "2ffb1a07-ac95-4a6d-bc34-39f479f1c856" }
},
{
"update_id": "a1b2c3d4-0001-4000-b000-000000000001",
"update_type": "radar_failure",
"record_id": null,
"completed_at": "2026-05-25T11:17:26Z",
"discovered_at": "2026-05-25T11:17:26Z",
"bulk_id": "5f9da22e-4778-427c-b7e4-dab2a8d9d709",
"item_index": 1,
"custom_fields": {},
"status": "error",
"code": 409,
"message": "Conflict: Radar already exists",
"errors": [
{
"field": "domain",
"reason": "A radar with this domain and type already exists for your account. Conflicting radar_id: 3f1d8e8a-39c4-47e8-a4b4-f7db6a2e50f0.",
"conflicting_radar_id": "3f1d8e8a-39c4-47e8-a4b4-f7db6a2e50f0"
}
],
"refund_amount_usd": 0,
"error_id": "45ed35e5-986c-4496-afd8-9a84f6baa410",
"timestamp": "2026-05-25T11:17:26Z"
}
]
}
```
### Radar Types
| Radar Type | Description |
| ---------------------------- | ------------------------------------------------------------------------ |
| `company_mentions` | Social media and article mentions of your company |
| `company_social_posts` | Company's own social media activity |
| `company_social_posts_cxo` | Social media posts from company executives |
| `company_social_engagements` | Social media engagements (likes, comments) on company posts |
| `company_job_openings` | New job postings from tracked companies |
| `company_new_hires` | New employee detections |
| `company_promotions` | Employee promotions and role changes |
| `company_reviews` | Customer reviews across platforms |
| `contact_job_changes` | Individual contact career changes (departures, promotions, role changes) |
| `contact_social_posts` | Social media posts from tracked contacts |
| `contact_social_engagements` | Social media engagements (likes, comments) by tracked contacts |
| `industry_mentions` | Industry-wide mentions and news articles |
| `industry_funding_rounds` | Funding rounds across companies in your industry |
| `industry_job_openings` | Job openings across companies in your industry |
| `industry_new_companies` | New companies emerging in your industry |
## Radar Failure Notifications
When a radar fails setup or is deactivated due to billing, you'll receive a `radar_failure` notification:
```json theme={null}
{
"update_id": "550e8400-e29b-41d4-a716-446655440000",
"update_type": "radar_failure",
"record_id": null,
"discovered_at": "2024-03-20T12:30:00Z",
"completed_at": "2024-03-20T12:30:00Z",
"data": {
"radar_id": "54e32291-25a4-459f-8bd6-4f79489ea931",
"radar_type": "company_job_openings",
"domain": "example.com",
"custom_fields": {
"priority": "high"
}
},
"status": "error",
"code": 402,
"message": "Radar was deactivated due to insufficient credits.",
"errors": [{ "field": "radar", "reason": "insufficient_funds" }],
"refund_amount_usd": 0,
"timestamp": "2024-03-20T12:30:00Z",
"error_id": "550e8400-e29b-41d4-a716-446655440000"
}
```
When setup fails, the same `radar_failure` shape is used with a different `code` and refund value:
```json theme={null}
{
"update_id": "550e8400-e29b-41d4-a716-446655440001",
"update_type": "radar_failure",
"record_id": null,
"discovered_at": "2024-03-20T12:30:00Z",
"completed_at": "2024-03-20T12:30:00Z",
"data": {
"radar_id": "54e32291-25a4-459f-8bd6-4f79489ea931",
"radar_type": "company_social_posts",
"domain": "example.com",
"custom_fields": {
"priority": "high"
}
},
"status": "error",
"code": 400,
"message": "Radar creation failed because the domain does not have required social media accounts.",
"errors": [{ "field": "radar", "reason": "missing_prerequisites" }],
"refund_amount_usd": 0.10,
"timestamp": "2024-03-20T12:30:00Z",
"error_id": "550e8400-e29b-41d4-a716-446655440001"
}
```
### Failure Reasons
Identify the failure type from `code` and `message`. The `errors[].reason` field contains the machine-readable reason key.
| `code` | `errors[].reason` | Description |
| ------ | ----------------------- | ------------------------------------------------------------------------ |
| `400` | `missing_prerequisites` | Required accounts or data sources not found |
| `400` | `setup_failure` | General setup failure during radar initialization |
| `400` | `source_inaccessible` | Target source (e.g. LinkedIn profile, domain) is inaccessible or private |
| `402` | `insufficient_funds` | Radar deactivated โ balance too low to cover the charge |
## Best Practices for Webhook Handling
1. **Respond Quickly**: Return a 2xx status code immediately, then process asynchronously
2. **Implement Retry Logic**: Be prepared to handle the same event multiple times
3. **Verify Signatures**: Check webhook signatures to validate authenticity (documentation coming soon)
4. **Track Failure Events**: Monitor `radar_failure` to handle billing/setup problems quickly
5. **Handle Null Fields**: Some fields like `record_id` may be null for certain event types
## Testing Your Webhook
### Quick test
Trigger a sample payload to your endpoint:
```bash theme={null}
curl -X POST https://api.tamradar.com/v1/webhooks/test \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhook_url": "https://your-domain.com/webhook",
"radar_type": "company_job_openings" // any supported radar_type
// "update_type": "radar_failure" // optional, defaults to radar_finding
}'
```
The test event is fire-and-forget, with no retries or signatures, and uses the same payload format you'll receive in production.
Need help? [Contact our team](mailto:support@tamradar.com)