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

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