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

# Getting Started with TAMradar

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

### Choose a data starting point with `updates_since`

`updates_since` is optional. Use it when you want to pull qualifying historical
data or ensure that eligible source events start from a date you choose.

* Supply an RFC 3339 timestamp with a timezone, such as
  `2026-07-01T09:00:00-04:00`. TAMradar stores and returns the equivalent UTC
  instant.
* The value must represent now or a past instant and may be at most five calendar
  years old. A five-second server tolerance exists only for clock skew; it does
  not enable future scheduling.
* Omit the field to store `updates_since: null`. The radar's `created_at` is
  then the effective cutoff.
* Historical processing is asynchronous. Qualifying findings may continue
  arriving during the first 24 hours before the radar settles into its normal
  recent-update schedule.

```json theme={null}
{
  "domain": "stripe.com",
  "radar_type": "company_job_openings",
  "updates_since": "2026-07-01T09:00:00-04:00"
}
```

The stored response value for that input is `2026-07-01T13:00:00.000Z`.

<Note>
  For new-hire and promotion radars, source dates are evaluated at calendar-month
  precision even though the exact submitted timestamp is preserved in the radar
  response. During initial engagement history, the parent post's publication time
  sets eligibility; recurring engagement delivery uses when TAMradar first
  discovers the engagement.
</Note>

<Warning>
  `include_historical` is deprecated. It remains accepted for compatibility but
  has no effect on delivery behavior. Use `updates_since` instead.
</Warning>

### 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",
    "updates_since": "2024-03-01T09:00:00-05:00",
    "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`, `Leadership`, `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",
    "updates_since": "2024-03-01T14:00:00.000Z",
    "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)
* For `contact_job_changes`, email must identify an individual; role-based, shared, and placeholder addresses are rejected
* For `contact_job_changes`, `profile_url` must be a LinkedIn person profile (`linkedin.com/in/...`); company, school, and legacy `/pub/` URLs are rejected
* Within the same company and radar type, duplicate matching uses email or the normalized LinkedIn profile URL; `full_name` is not a duplicate key

***

### 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 | Number per page, max 500; higher values return `400` |
| `radar_status` | `active` \| `inactive` | — | Filter by status; `inactive` means any non-active state |
| `radar_type` | string | — | Single radar type value |
| `radar_id` | string | — | Comma-separated IDs, max 50 |
| `created_since` | ISO 8601 timestamp | — | Include radars created at or after this time; timezone required |
| `cursor` | string | — | Opaque `next_cursor` from the previous response |

**Response (200, with `limit=1`):**

```json theme={null}
{
  "status": "success",
  "code": 200,
  "message": "Radars retrieved successfully",
  "count": 1,
  "limit": 1,
  "has_more": true,
  "next_cursor": "eyJjIjoiMjAyNC0wMy0yMCAxMjowNTowMCswMCIsImkiOiJjNzA4MTNiMy03ZTg3LTRjYTctOTBmZC04YzY0NTc0ZDkxMWIifQ",
  "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",
      "updates_since": null,
      "deactivated_at": null,
      "next_charge_at": "2024-04-20T12:05:00Z"
    }
  ]
}
```

When `has_more` is `true`, repeat the same filters and pass `next_cursor` as
the `cursor` query parameter. The final page returns `has_more: false` and
`next_cursor: null`. The `count` value applies only to the current page.

Unknown query parameters return `400`; use `cursor`, not `next_cursor`, in the
request.

For recovery or reconciliation, follow every cursor to complete the traversal
and compare it with your local radar inventory. If radars may have been created
while you were paging, start a fresh request without a cursor to pick them up.
This lists radar records; use `GET /v1/updates` for findings and failures.

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`)

`updates_since` and the polling query parameter `since` serve different
purposes. Radar `updates_since` is the permanent eligibility boundary selected
at creation. `GET /v1/updates?since=` only filters TAMradar updates that have
already been created; it does not change the radar.

**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",
    "updates_since": "2024-03-01T14:00:00.000Z",
    "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 500 per page; higher values return `400` |
| List `radar_id` filter | Max 50 IDs |


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