> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tamradar.com/llms.txt
> Use this file to discover all available pages before exploring further.

# TAMradar Agent Instructions

> 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:** <a href="https://docs.tamradar.com/agentic/tamradar-agent.md" target="_blank">[https://docs.tamradar.com/agentic/tamradar-agent.md](https://docs.tamradar.com/agentic/tamradar-agent.md)</a>

***

***

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. |
| `updates_since` | Optional | Use only when the user explicitly requests historical data or names an earliest event date. Must be now or in the past. |

### 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

`updates_since` is optional. Never invent or infer it. Include it only when the
user explicitly asks for historical data or supplies an earliest date. Require
an RFC 3339 timestamp with timezone; future scheduling is not supported. Omit it
to use radar creation time. Do not use the deprecated `include_historical`
field.

**`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", "updates_since": null }
```

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. Read `errors[0].conflicting_radar_id`, store it, and 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.

For multiple radars, send one synchronous request:

```bash theme={null}
curl -s -X DELETE "https://api.tamradar.com/v1/radars/bulk" \
  -H "x-api-key: $TAMRADAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"radar_ids":["RADAR_UUID_1","RADAR_UUID_2"]}'
```

Use single DELETE for one radar and bulk DELETE for 2–500 known IDs. Check
every `radars[]` item: the top-level response is `200` even when some IDs are
missing or already inactive.

***

## 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

For `contact_job_changes`, LinkedIn company, school, and legacy `/pub/` URLs are
rejected; only person profiles under `/in/` are accepted. Email must identify an
individual, so role-based, shared, and placeholder addresses are rejected.

For `contact_job_changes`, uniqueness is evaluated within the same account,
domain, and radar type. An exact case-insensitive email match or a normalized
LinkedIn profile URL match returns `409`; `full_name` alone is not a duplicate
key. The error field is `identifiers.email` or `identifiers.profile_url`, showing
which identifier matched. 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
* Company uniqueness: one radar per `domain + radar_type` per account — duplicates get `409`
* Contact job-change uniqueness: within the same `domain + radar_type`, an email or normalized profile URL match gets `409`; `full_name` is not a duplicate key
* Different contact radar types for the same person are allowed
* Persistence: save mappings to `./tamradar-radars.jsonl` by default


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.