# Data Reaktor — Agent Reference

> This document is written for AI agents, LLM-based systems, and automated workflows that want to use Data Reaktor as a tool. Humans can read it too.

## What is Data Reaktor?

Data Reaktor is an **agentic B2B intelligence factory**. You give it a lead (name + company + context) and it returns structured, production-ready B2B intelligence: lead scores, personalized LinkedIn messages, cold emails, outreach sequences, and call scripts.

Every capability is available via REST API. You do not need a browser or a human. You need:
1. An API key (get one at https://www.datareaktor.ai after creating a free account)
2. The ability to make HTTP POST requests with JSON bodies

## Authentication

All API requests must include your API key in the `X-API-Key` header:

```
X-API-Key: drk_YOUR_KEY_HERE
```

Alternatively, pass it as a Bearer token:

```
Authorization: Bearer drk_YOUR_KEY_HERE
```

API keys are created via the agent dashboard at `/agents`. Each key is scoped to one agent identity. Keys expire after 365 days and can be rotated at any time.

## Provisioning Sub-Agents Programmatically

Autonomous agents can mint sub-agent keys, top up credits, and revoke keys without a human in the loop. This is useful when one agent needs to spawn specialised workers (e.g. one key per customer or per pipeline stage) and bill them separately.

> All five endpoints in this section are also documented in the machine-readable OpenAPI 3.1 spec at `GET /api/v1/openapi.json` — point Postman, Speakeasy, or any code-generator at it to scaffold a typed SDK with full request/response schemas and error-code enums.

**Who can call these endpoints?**

- A signed-in human (Replit Auth session, browser flow), OR
- An agent API key whose `canProvision` flag is `true` — call these *owner keys*. Owner status is granted by the human account holder via `PATCH /api/agents/:id` with `{ "canProvision": true }`. By default new agents are *not* owners.

**Rate limit:** 20 sub-agent creates per hour per owning user (the standard 60 req/min API rate limit applies to the rest).

### Create a sub-agent

```
POST /api/v1/agents
X-API-Key: drk_OWNER_KEY
Content-Type: application/json

{
  "name": "Outreach worker — Acme pipeline",
  "tier": "free",
  "callbackUrl": "https://your.app/webhooks/datareaktor",
  "monthlyCreditCap": 0
}
```

Response (`201 Created`):

```json
{
  "agentId": 42,
  "name": "Outreach worker — Acme pipeline",
  "apiKeyPrefix": "drk_xxxxxxxxxxxx",
  "apiKey": "drk_THIS_IS_SHOWN_EXACTLY_ONCE_STORE_IT_NOW",
  "tier": "free",
  "canProvision": false,
  "apiKeyExpiresAt": "2027-04-26T..."
}
```

The full `apiKey` is returned **once** — store it immediately. Sub-agents are always created with `canProvision=false`. Promote them later if needed.

### List, inspect, rotate, revoke

```
GET    /api/v1/agents             # list (humans/owners → all owned; non-owner key → just itself)
GET    /api/v1/agents/:id         # detail
POST   /api/v1/agents/:id/rotate-key   # returns new apiKey once
DELETE /api/v1/agents/:id         # revoke (an agent key cannot revoke itself)
```

### Top up credits (Stripe checkout)

```
POST /api/v1/billing/checkout
X-API-Key: drk_OWNER_KEY

{ "pack": "pack_basic", "agentId": 42 }
```

Valid `pack` values: `pack_basic` ($10 / 1,000 credits), `pack_plus` ($49 / 6,000 credits), `pack_max` ($149 / 20,000 credits), or any subscription plan id (`starter`, `growth`, `scale`).

Returns `{ checkoutUrl, sessionId }`. Open `checkoutUrl` in a browser to complete payment.

> **Beta-mode caveat:** While Data Reaktor is in beta, this endpoint returns `HTTP 503` with `{ "error": "beta_mode_active", "code": "BETA_CHECKOUT_DISABLED" }`. During beta, request credit grants by emailing `support@datareaktor.app`. Calling agents should detect the `BETA_CHECKOUT_DISABLED` code and surface it to their operator instead of retrying.

### Transfer credits to a sub-agent

```
POST /api/v1/billing/transfer
X-API-Key: drk_OWNER_KEY

{ "agentId": 42, "credits": 500 }
```

Atomically debits the owning user's `purchased_credits` balance and credits the target agent. Refuses with `402` (`INSUFFICIENT_CREDITS`) if you can't cover the move, or `404` (`AGENT_NOT_OWNED`) if the agent isn't yours.

Response:

```json
{
  "success": true,
  "agentId": 42,
  "creditsTransferred": 500,
  "userPurchasedCredits": 4500,
  "agentBalance": 500
}
```

### Check balances

```
GET /api/v1/billing/credits                # principal's own balance
GET /api/v1/billing/credits?agentId=42     # specific sub-agent (must be owned)
```

For an agent caller this returns its own credit balance + monthly cap. For a human/owner caller it returns the user-level `purchasedCredits` plus monthly records remaining.

### Audit trail

Every create, rotate-key, delete, and credit transfer writes a structured line to the server log prefixed `[Audit][provision]` and (for transfers) a row to `billing_events` with `eventType: "credit_purchased"`.

## Base URL

```
https://www.datareaktor.ai
```

## Machine-Readable Manifests

- **OpenAPI 3.1 spec**: `GET /api/v1/openapi.json`
- **A2A capability manifest**: `GET /.well-known/agent.json` (and the byte-identical root alias `GET /agent.json`)
- **Capability catalog**: `GET /api/v1/capabilities`
- **Skill registry with JSON Schemas**: `GET /api/v1/skills`
- **Workflow registry**: `GET /api/v1/workflows`
- **Error registry**: `GET /api/v1/errors`
- **Pre-flight cost estimate**: `POST /api/v1/tasks/estimate`
- **LLM-friendly site map**: `GET /llms.txt` and `GET /llms-full.txt` (text/plain, llmstxt.org convention; the `-full` variant inlines AGENTS.md and CONTEXT.md for one-shot context loads)
- **MCP endpoint**: `POST /mcp` (Streamable HTTP — see "Connect via MCP" below)

The agent manifest carries enriched per-capability metadata: `description`, `inputSchema` (live JSON Schema generated from the same Zod definitions the submission endpoint validates against), `outputSchema`, `isNative`, `mode` (`sync`/`async`), `creditCost`, `rateLimit`, and an `examplePayload`. It also exposes:

- `links` — shortcuts to every discovery surface above
- `status` — `{ version, beta, deprecatedTaskTypes, changelogUrl }`
- `pricing.creditPacks` — pay-as-you-go top-up packs callable via `POST /api/v1/billing/checkout`
- `rateLimits` — the same per-minute / per-hour limits enforced by the server, returned with `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers on every `/api/v1/*` response.

Credit-bearing endpoints additionally return an `X-Credits-Remaining` header so callers can keep a live balance without polling `GET /api/v1/billing/credits`.

---

## Connect via MCP

Data Reaktor speaks the **Model Context Protocol** at `POST /mcp` over the Streamable HTTP transport. Every REST capability is auto-exposed as an MCP tool, plus three meta-tools:

- `list_capabilities` — full skill + workflow catalog (mirrors `/api/v1/capabilities`)
- `check_credits` — your current credit balance
- `get_task_status` — poll an async task by `taskId`

Synchronous skills (e.g. `lead_scoring`, `personalized_email`) return their output inline. Asynchronous skills and workflows return `{ taskId, status: "queued" }` — fetch the result with `get_task_status`.

**Auth:** the same `X-API-Key: drk_live_...` header used for the REST API.

### MCP Resources

Static catalog data is also exposed as **MCP resources** so clients (like Claude Desktop) can browse the catalog without invoking a tool:

| URI | Mirrors | Format |
|---|---|---|
| `drk://catalog/skills` | `GET /api/v1/skills` | JSON |
| `drk://catalog/workflows` | `GET /api/v1/workflows` | JSON |
| `drk://docs/openapi` | `GET /api/v1/openapi.json` | JSON |
| `drk://docs/agents-md` | this document | Markdown |

Use your client's `resources/list` and `resources/read` calls (or the equivalent UI button) to read them.

### Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_config.json`)

```json
{
  "mcpServers": {
    "data-reaktor": {
      "transport": "streamable-http",
      "url": "https://www.datareaktor.ai/mcp",
      "headers": { "X-API-Key": "drk_live_YOUR_KEY_HERE" }
    }
  }
}
```

### Cursor (`~/.cursor/mcp.json`)

```json
{
  "mcpServers": {
    "data-reaktor": {
      "url": "https://www.datareaktor.ai/mcp",
      "headers": { "X-API-Key": "drk_live_YOUR_KEY_HERE" }
    }
  }
}
```

### Replit Agent / generic MCP client

```json
{
  "name": "data-reaktor",
  "transport": "streamable-http",
  "url": "https://www.datareaktor.ai/mcp",
  "headers": { "X-API-Key": "drk_live_YOUR_KEY_HERE" }
}
```

---

## Core Task Endpoint

### Submit a Task

```
POST /api/v1/tasks
Content-Type: application/json
X-API-Key: drk_YOUR_KEY
```

**Request body:**
```json
{
  "taskType": "lead_scoring",
  "inputPayload": { ... },
  "callbackUrl": "https://your-server.com/webhook"
}
```

`callbackUrl` is optional. If provided, the result is POST-ed to your URL when complete (async). If omitted, the result is returned inline (synchronous for native tasks).

### Poll Task Status

```
GET /api/v1/tasks/{taskId}
X-API-Key: drk_YOUR_KEY
```

### List Recent Tasks

```
GET /api/v1/tasks
X-API-Key: drk_YOUR_KEY
```

Query params: `limit` (default 50, max 200), `offset` (default 0)

### Star / Unstar a Task

```
PATCH /api/v1/tasks/{taskId}/star
Content-Type: application/json
X-API-Key: drk_YOUR_KEY

{ "starred": true }
```

### Cancel a Task

```
DELETE /api/v1/tasks/{taskId}
X-API-Key: drk_YOUR_KEY
```

Only tasks in `queued` status can be cancelled. Credits are refunded.

---

## JSON Batch Endpoint

Submit up to 100 contacts in a single request. One task is queued per contact. Credits are deducted upfront.

```
POST /api/v1/batch
Content-Type: application/json
X-API-Key: drk_YOUR_KEY
```

**Request body:**
```json
{
  "contacts": [
    {
      "name": "Jane Smith",
      "company": "Acme Corp",
      "title": "VP of Sales",
      "context": "Series B funded, scaling outbound",
      "taskType": "linkedin_message"
    },
    {
      "name": "Bob Jones",
      "company": "Beta Inc",
      "taskType": "lead_scoring"
    }
  ],
  "callbackUrl": "https://your-server.com/webhook"
}
```

**Contact fields:**
- `name` (required) — Contact's full name
- `company` (required) — Company name
- `title` (optional) — Job title / role
- `context` (optional) — Any additional signals or context
- `taskType` (optional, default `linkedin_message`) — Task type to run for this contact
- `campaignId` (optional) — Link to an existing campaign by ID

**Response:**
```json
{
  "taskIds": ["task_abc123", "task_def456"],
  "count": 2,
  "totalCreditsCharged": 10,
  "message": "2 task(s) queued. Poll GET /api/v1/tasks/{taskId} for results."
}
```

**curl example:**
```bash
curl -X POST https://www.datareaktor.ai/api/v1/batch \
  -H "X-API-Key: drk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contacts": [
      { "name": "Jane Smith", "company": "Acme Corp", "title": "VP of Sales", "taskType": "linkedin_message" },
      { "name": "Bob Jones", "company": "Beta Inc", "taskType": "lead_scoring" }
    ]
  }'
```

---

## Output History Endpoints

### List Outputs

```
GET /api/v1/outputs
X-API-Key: drk_YOUR_KEY
```

Query params:
- `taskType` — Filter by task type (e.g. `linkedin_message`)
- `search` — Search by contact name or company (partial match)
- `starred` — `true` or `false` to filter starred outputs
- `limit` — Max results (default 50, max 200)
- `offset` — Pagination offset (default 0)

**curl example:**
```bash
curl "https://www.datareaktor.ai/api/v1/outputs?taskType=linkedin_message&starred=true&limit=20" \
  -H "X-API-Key: drk_YOUR_KEY"
```

**Response:**
```json
{
  "outputs": [
    {
      "taskId": "task_abc123",
      "taskType": "linkedin_message",
      "status": "completed",
      "starred": true,
      "creditsCharged": 5,
      "inputPayload": { ... },
      "outputPayload": { ... },
      "createdAt": "2026-04-11T12:00:00Z",
      "completedAt": "2026-04-11T12:00:05Z"
    }
  ],
  "pagination": { "limit": 20, "offset": 0, "total": 42, "count": 20 }
}
```

### Get Output Detail

```
GET /api/v1/outputs/{taskId}
X-API-Key: drk_YOUR_KEY
```

Returns the full output for a single task, including `outputPayload`.

**curl example:**
```bash
curl https://www.datareaktor.ai/api/v1/outputs/task_abc123 \
  -H "X-API-Key: drk_YOUR_KEY"
```

### Star / Unstar an Output

```
POST /api/v1/outputs/{taskId}/star
Content-Type: application/json
X-API-Key: drk_YOUR_KEY

{ "starred": true }
```

Omit the body to toggle. Returns `{ "taskId": "task_abc123", "starred": true }`.

**curl example:**
```bash
curl -X POST https://www.datareaktor.ai/api/v1/outputs/task_abc123/star \
  -H "X-API-Key: drk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "starred": true }'
```

---

## Campaign & ICP Read Endpoints

Agents can look up existing campaigns and ICPs by ID to reference them when submitting tasks. Campaign and ICP *creation* remains a human setup step.

### List Campaigns

```
GET /api/v1/campaigns
X-API-Key: drk_YOUR_KEY
```

**curl example:**
```bash
curl https://www.datareaktor.ai/api/v1/campaigns \
  -H "X-API-Key: drk_YOUR_KEY"
```

**Response:**
```json
{
  "campaigns": [
    {
      "id": 1,
      "name": "Q2 Enterprise Outreach",
      "clientName": "Acme Corp",
      "status": "active",
      "icpId": 2,
      "totalContacts": 50,
      "processedContacts": 30,
      "successfulOutputs": 28,
      "sequenceMode": false,
      "createdAt": "2026-01-15T09:00:00Z"
    }
  ],
  "count": 1
}
```

### Get Campaign Detail

```
GET /api/v1/campaigns/{id}
X-API-Key: drk_YOUR_KEY
```

**curl example:**
```bash
curl https://www.datareaktor.ai/api/v1/campaigns/1 \
  -H "X-API-Key: drk_YOUR_KEY"
```

Returns full campaign detail including `objective` and `campaignPrompt`.

### List ICPs

```
GET /api/v1/icps
X-API-Key: drk_YOUR_KEY
```

**curl example:**
```bash
curl https://www.datareaktor.ai/api/v1/icps \
  -H "X-API-Key: drk_YOUR_KEY"
```

**Response:**
```json
{
  "icps": [
    {
      "id": 2,
      "name": "SaaS VP Sales ICP",
      "targetIndustry": "B2B SaaS",
      "companySizeRange": "51-500",
      "targetSeniority": "VP, Director",
      "tonePreference": "professional",
      "userValueProp": "Fractional VP of Sales for post-Series-A SaaS",
      "createdAt": "2026-01-10T08:00:00Z"
    }
  ],
  "count": 1
}
```

### Get ICP Detail

```
GET /api/v1/icps/{id}
X-API-Key: drk_YOUR_KEY
```

**curl example:**
```bash
curl https://www.datareaktor.ai/api/v1/icps/2 \
  -H "X-API-Key: drk_YOUR_KEY"
```

Returns full ICP detail including `targetPersonas`, `userDifferentiators`.

---

## Available Task Types

### `lead_scoring`
Score a lead against your configured ICP. Returns a numeric score (0–100), a tier (A/B/C/D), and a structured reasoning breakdown.

**Cost:** 5 credits

**Input:**
```json
{
  "contactName": "Jane Smith",
  "contactRole": "VP of Sales",
  "company": "Acme Corp",
  "companySize": "51-200",
  "signals": ["Series B funded", "Hiring 12 SDRs"],
  "userValueProp": "Fractional VP of Sales for post-Series-A SaaS"
}
```

**Output:**
```json
{
  "score": 87,
  "tier": "A",
  "reasoning": "Strong ICP fit: VP-level, SaaS, active growth signals...",
  "icpAlignment": { "industry": true, "seniority": true, "size": true }
}
```

---

### `linkedin_message`
Generate a LinkedIn connection request and a follow-up message for a single prospect.

**Cost:** 5 credits

**Input:**
```json
{
  "contactName": "Jane Smith",
  "contactRole": "VP of Sales",
  "company": "Acme Corp",
  "context": "Recently hired 10 SDRs, just raised Series B"
}
```

**Output:**
```json
{
  "connect_request": "Hi Jane — noticed Acme's Series B announcement...",
  "followup_message": "Following up on my earlier connection request..."
}
```

---

### `personalized_email`
Generate a hyper-personalized cold email for a single lead.

**Cost:** 5 credits

**Input:**
```json
{
  "contactName": "Jane Smith",
  "contactRole": "VP of Sales",
  "company": "Acme Corp",
  "context": "Scaling their outbound team post-Series-B",
  "userValueProp": "Fractional VP of Sales"
}
```

**Output:**
```json
{
  "subject": "Acme's outbound scaling challenge",
  "body": "Hi Jane,\n\nSaw Acme just closed your Series B...",
  "previewText": "Quick question about your SDR ramp..."
}
```

---

### `cold_call_script`
Generate a complete call framework: opener, discovery questions, value bridge, and objection handlers.

**Cost:** 5 credits

**Input:** Same as `personalized_email`

**Output:**
```json
{
  "opener": "Hi Jane, this is [your name] — I saw...",
  "discoveryQuestions": ["What does your current SDR ramp look like?", ...],
  "valueBridge": "Based on what you just shared...",
  "objectionHandlers": [
    { "objection": "We already have a VP of Sales", "response": "..." }
  ]
}
```

---

### `outreach_sequence`
Generate a full 4-step multi-touch outreach sequence.

**Cost:** 5 credits

**Input:** Same as `linkedin_message`

**Output:**
```json
{
  "step1": { "channel": "linkedin", "timing": "Day 1", "body": "..." },
  "step2": { "channel": "linkedin", "timing": "Day 3", "body": "..." },
  "step3": { "channel": "email", "timing": "Day 7", "subject": "...", "body": "..." },
  "step4": { "channel": "email", "timing": "Day 14", "subject": "...", "body": "..." }
}
```

---

### `thought_leadership`
Generate a content pack: LinkedIn post, email newsletter intro, and a cold outreach hook.

**Cost:** 5 credits

**Input:**
```json
{
  "topic": "Why outbound is changing in 2025",
  "expertiseArea": "Fractional VP of Sales",
  "targetAudience": "Series A SaaS founders",
  "tone": "professional"
}
```

---

### `research` (async)
Full prospect research brief. Returns a structured brief plus two InMail drafts.

**Cost:** 5 credits  
**Mode:** Asynchronous (returns `taskId`, poll `/api/v1/tasks/{taskId}` or use `callbackUrl`)

**Input:**
```json
{
  "name": "Jane Smith",
  "context": "VP of Sales, acmecorp.com, B2B SaaS"
}
```

---

### `outreach_generation` (async)
Batch outreach generation for multiple leads. Submit an array of lead objects.

**Cost:** 5 credits per contact  
**Mode:** Asynchronous

**Input:**
```json
{
  "leads": [
    { "first_name": "Jane", "company": "Acme", "industry": "SaaS", "email": "jane@acme.com" }
  ]
}
```

---

### `abm_content_brief`
Generate an account-based marketing content brief for a target account.

**Cost:** 5 credits

---

### `domain_to_company`
Resolve a domain name to company intelligence (name, industry, size, tech stack signals).

**Cost:** 5 credits

**Input:**
```json
{ "domain": "acmecorp.com" }
```

---

### `contact_lookup`
Look up a contact's role and signals given their name and company.

**Cost:** 5 credits

**Input:**
```json
{ "name": "Jane Smith", "company": "Acme Corp" }
```

---

## Analytics Endpoints

### Performance Snapshot

```
GET /api/v1/analytics/performance
X-API-Key: drk_YOUR_KEY
```

Returns aggregate stats: task counts by type, success rates, credit spend.

### Per-Agent Breakdown

```
GET /api/v1/analytics/agents/{agentId}
X-API-Key: drk_YOUR_KEY
```

---

## Knowledge Graph Endpoints

### Company Lookup (1 credit)

```
GET /api/v1/knowledge/company/{domain}
X-API-Key: drk_YOUR_KEY
```

**curl example:**
```bash
curl https://www.datareaktor.ai/api/v1/knowledge/company/acmecorp.com \
  -H "X-API-Key: drk_YOUR_KEY"
```

### Industry Signals (free)

```
GET /api/v1/knowledge/signals/{industry}
X-API-Key: drk_YOUR_KEY
```

---

## Workflow Orchestration Endpoints

### List Workflows

```
GET /api/v1/workflows
X-API-Key: drk_YOUR_KEY
```

### Submit a Workflow

```
POST /api/v1/workflows
Content-Type: application/json
X-API-Key: drk_YOUR_KEY

{
  "workflowId": "prospect_and_outreach",
  "inputPayload": { ... },
  "callbackUrl": "https://your-server.com/webhook"
}
```

---

## Profile API

Read or update the account owner's profile (expertise, industry, tone, value prop, etc.) from your agent. These fields are injected into every AI task execution, so setting them via API before running tasks lets you configure the AI persona programmatically.

### Read Profile

```
GET /api/v1/profile
X-API-Key: drk_YOUR_KEY
```

**Response:**
```json
{
  "expertiseArea": "Fractional VP of Sales",
  "industryFocus": "B2B SaaS",
  "targetAudience": "Series A-B SaaS founders",
  "targetPersona": "VP of Sales at Series A-B companies",
  "preferredTone": "professional",
  "valueProp": "I help Series A SaaS companies build outbound motion from scratch",
  "uniquePOV": "Most outbound fails because of bad data, not bad copy",
  "contentTopics": ["outbound sales", "SDR hiring", "pipeline building"]
}
```

---

### Update Profile

```
PATCH /api/v1/profile
Content-Type: application/json
X-API-Key: drk_YOUR_KEY
```

**Request body** (all fields optional — only provided fields are updated):
```json
{
  "valueProp": "I help CFOs automate month-end close in 48 hours",
  "targetAudience": "Mid-market CFOs",
  "preferredTone": "direct"
}
```

**Response:** Updated profile object (same shape as GET).

**Use case example:** An n8n workflow sets `valueProp` and `targetAudience` for a specific campaign before triggering outreach generation tasks, then resets them afterward.

---

## Webhook Callbacks

When you submit a task with a `callbackUrl`, Data Reaktor will POST the result to your URL when the task completes.

**Payload shape:**
```json
{
  "taskId": "task_abc123",
  "status": "completed",
  "taskType": "lead_scoring",
  "output": { ... },
  "creditsCharged": 5
}
```

**Verification:** Every webhook includes an `X-DR-Signature` header — an HMAC-SHA256 of the raw request body using your agent's secret. Verify before processing.

**Requirements:**
- Must be a publicly accessible HTTPS URL
- Must respond with HTTP 2xx within 10 seconds
- Data Reaktor retries up to 3 times on failure

---

## Inbound Trigger (Zero-Config A2A)

If you want to push a lead TO Data Reaktor and receive an outreach sequence back — without creating an API agent — use the Trigger URL:

```
POST /trigger/{userId}
Authorization: Bearer drk_trigger_YOUR_TRIGGER_KEY

{
  "name": "Jane Smith",
  "company": "Acme Corp",
  "title": "VP of Sales"
}
```

Generate your trigger key in the Automations section of the dashboard.

---

## Credit System

All tasks cost 5 credits each (flat pricing). Credits are consumed per task.

| Task | Credits |
|------|---------|
| Lead Scoring | 5 |
| LinkedIn Message | 5 |
| Personalized Email | 5 |
| Cold Call Script | 5 |
| Outreach Sequence (4-step) | 5 |
| Thought Leadership Pack | 5 |
| Research (single, async) | 5 |
| Batch Outreach (per contact) | 5 |
| ABM Content Brief | 5 |
| Domain to Company | 5 |
| Contact Lookup | 5 |
| Knowledge Graph Company Lookup | 1 |
| Industry Signals | 0 |

---

## Rate Limits

The server enforces these per-caller limits and reflects them on every `/api/v1/*` response via `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers:

- **60 requests/minute** per API key (all `/api/v1/*` traffic)
- **20 task submissions/minute** per caller (`POST /api/v1/tasks`, `POST /api/v1/batch`, `POST /api/v1/workflows`)
- **20 sub-agent creates/hour** per owner (`POST /api/v1/agents`)
- **10 research submissions/minute** per user (`POST /api/research/submit`)

Usage caps additionally apply per subscription tier (records and research lookups per month) — see the agent manifest for the live numbers.

---

## Cost Estimation

Use `POST /api/v1/tasks/estimate` to dry-run a single task or a batch and learn the credit cost without paying it. Same auth as other v1 endpoints. Does not deduct credits, does not count against the task rate limit, and does not touch the daily token budget.

```bash
curl -X POST https://www.datareaktor.ai/api/v1/tasks/estimate \
  -H "X-API-Key: drk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "tasks": [
      { "taskType": "personalized_email" },
      { "taskType": "lead_scoring" }
    ]
  }'
```

Response:

```json
{
  "wouldSucceed": true,
  "totalCredits": 10,
  "currentBalance": 1500,
  "balanceSource": "agent",
  "perTask": [
    { "taskType": "personalized_email", "valid": true, "creditCost": 5 },
    { "taskType": "lead_scoring",       "valid": true, "creditCost": 5 }
  ]
}
```

A single-task body (`{ "taskType": "lead_scoring", "inputPayload": { ... } }`) is also accepted.

---

## Error Handling

All errors return a structured JSON body:

```json
{
  "error": "Insufficient credits",
  "code": "INSUFFICIENT_CREDITS"
}
```

Codes are stable. The full registry — code, HTTP status, retryability — is published at `GET /api/v1/errors`. Common entries:

| Code | Status | Retryable | Meaning |
|------|--------|-----------|---------|
| `AUTH_REQUIRED` | 401 | no | No valid session or API key |
| `INVALID_API_KEY` | 401 | no | The API key was not recognised or has been revoked |
| `FORBIDDEN` | 403 | no | Caller is authenticated but lacks permission |
| `INSUFFICIENT_CREDITS` | 402 | no | The agent does not hold enough credits |
| `RATE_LIMIT_EXCEEDED` | 429 | yes | Inspect `X-RateLimit-Reset` and back off |
| `VALIDATION_ERROR` | 400 | no | The request body failed schema validation |
| `UNSUPPORTED_TASK_TYPE` | 400 | no | The task type is not in the capability catalog |
| `BETA_CHECKOUT_DISABLED` | 503 | no | Self-service checkout is off; contact support |
| `WORKFLOW_NOT_FOUND` | 404 | no | The named workflow definition was not found |
| `INTERNAL_ERROR` | 500 | yes | An unhandled exception — check server logs |

---

## Related Resources

- OpenAPI 3.1 spec: `/api/v1/openapi.json`
- A2A capability manifest: `/.well-known/agent.json`
- Skill registry: `/api/v1/skills`
- Human dashboard: `/` (requires login)
- Human help docs: `/help`
- Product context for LLMs: `/CONTEXT.md`

---

## Changelog

| Date | Change |
|------|--------|
| 2026-04-27 | **v1.4.0** — `/agent.json` root alias serves byte-identical payload to `/.well-known/agent.json`; both now cache for 5 min. Added `/llms.txt` and `/llms-full.txt` (text/plain, llmstxt.org). Manifest enriched with `links`, `status`, `pricing.creditPacks`, and per-capability `description`/`mode`/`creditCost`/`rateLimit`/`examplePayload`/JSON Schemas (live, generated from Zod via `zod-to-json-schema`). Added `POST /api/v1/tasks/estimate`, `GET /api/v1/errors`, `X-RateLimit-*` and `X-Credits-Remaining` response headers. OpenAPI version bumped to `1.4.0` in lockstep with the manifest. |
| 2026-04-11 | Added `GET /api/v1/campaigns`, `GET /api/v1/campaigns/:id`, `GET /api/v1/icps`, `GET /api/v1/icps/:id` — agents can now look up campaigns and ICPs by ID |
| 2026-04-11 | Added `GET /api/v1/outputs`, `GET /api/v1/outputs/:taskId`, `POST /api/v1/outputs/:taskId/star` — output history search and starring |
| 2026-04-11 | Added `POST /api/v1/batch` — JSON batch input for up to 100 contacts; removes CSV-only batch path |
| 2026-04-11 | Updated credit costs table — all tasks now cost 5 credits (flat pricing) |
| 2026-04-11 | Added workflow orchestration endpoints (`GET /api/v1/workflows`, `POST /api/v1/workflows`) |
| 2026-04-11 | Added analytics endpoints (`GET /api/v1/analytics/performance`, `GET /api/v1/analytics/agents/:id`) |
| 2026-04-11 | Added knowledge graph endpoints (`GET /api/v1/knowledge/company/:domain`, `GET /api/v1/knowledge/signals/:industry`) |
