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

# Quickstart Guide

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