# InstantFocus API Reference

> **Base URL:** `https://api.instantfocus.dev`
> **Version:** 1.0.0

## Authentication

All authenticated endpoints require a Bearer token in the `Authorization` header.

```
Authorization: Bearer if_your_api_key
Content-Type: application/json
```

API keys are prefixed with `if_` for easy identification. Get your key from the [dashboard](https://instantfocus.dev/dashboard).

---

## Endpoints

### POST /v1/evaluate

Run a synthetic panel study. This is the primary endpoint.

**Behavior:**
- Panel size ≤ 100 → synchronous response (200)
- Panel size > 100 → async job (202 with `job_id` — poll `/v1/jobs/:id`)

#### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `study_type` | string | Yes | One of: `concept_test`, `sentiment`, `nps`, `survey`, `ab_test` |
| `stimulus` | string | Yes (except ab_test) | Product description, copy, or concept to evaluate (10–5000 chars) |
| `variant_a` | string | ab_test only | First variant for A/B comparison (10–5000 chars) |
| `variant_b` | string | ab_test only | Second variant for A/B comparison (10–5000 chars) |
| `questions` | string[] | survey only | Array of 1–10 open-ended questions (max 500 chars each) |
| `panel_size` | number | No | Number of personas (1–10,000). Overrides `audience.size`. Default: 100 |
| `audience` | object | No | Panel configuration (see below) |
| `context` | string | No | Additional context for the panel (max 2000 chars) |
| `engine_mode` | string | No | `standard` (default) or `anchor` (pre-calibrated personas) |

#### Audience Object

| Field | Type | Default | Options |
|-------|------|---------|---------|
| `size` | number | 100 | 10–10,000 |
| `age_range` | [min, max] | [18, 80] | Min 18, max 100 |
| `gender` | string | "all" | `all`, `male`, `female`, `nonbinary` |
| `region` | string | "us_national" | `us_national`, `us_northeast`, `us_southeast`, `us_midwest`, `us_west`, `us_southwest`, `uk`, `eu`, `global` |
| `income_bracket` | string | "all" | `all`, `low`, `middle`, `upper_middle`, `high` |
| `education` | string | "all" | `all`, `high_school`, `some_college`, `bachelors`, `graduate` |
| `tech_adoption` | string | "all" | `all`, `innovator`, `early_adopter`, `early_majority`, `late_majority`, `laggard` |

#### Synchronous Response (200) — Panel ≤ 100

```json
{
  "ok": true,
  "data": {
    "study_type": "concept_test",
    "panel_size": 50,
    "mean_score": 5.8,
    "median_score": 6,
    "std_dev": 2.1,
    "score_distribution": { "1": 2, "2": 3, "3": 5, ... },
    "purchase_intent": {
      "definitely": 12,
      "probably": 24,
      "maybe": 30,
      "probably_not": 22,
      "definitely_not": 12
    },
    "top_concerns": ["Price sensitivity", "Feature gaps"],
    "top_appeals": ["Convenience", "Innovation"],
    "responses": [
      {
        "persona": { "id": "...", "age": 34, "gender": "female", ... },
        "score": 7,
        "sentiment": "positive",
        "reasoning": "This would save me so much time...",
        "purchase_intent": "probably"
      }
    ]
  },
  "meta": {
    "request_id": "a1b2c3d4-...",
    "credits_used": 50,
    "credits_remaining": 4950,
    "latency_ms": 3420,
    "engine": "standard",
    "mode": "inline",
    "panel": {
      "requested": 50,
      "delivered": 50
    },
    "inference": {
      "strategy": "round_robin",
      "providers_used": ["gpt-4o-mini", "gemini-flash"],
      "total_cost_usd": 0.003421,
      "total_input_tokens": 12500,
      "total_output_tokens": 8200
    }
  }
}
```

#### Async Response (202) — Panel > 100

```json
{
  "ok": true,
  "data": {
    "job_id": "f8a2e3c4-...",
    "status": "processing",
    "panel_size": 500,
    "total_batches": 100,
    "poll_url": "/v1/jobs/f8a2e3c4-...",
    "estimated_seconds": 10
  },
  "meta": {
    "request_id": "b2c3d4e5-...",
    "credits_used": 500,
    "credits_remaining": 4500,
    "latency_ms": 245,
    "engine": "standard",
    "mode": "async"
  }
}
```

#### Example: Concept Test

```bash
curl -X POST https://api.instantfocus.dev/v1/evaluate \
  -H "Authorization: Bearer if_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "study_type": "concept_test",
    "stimulus": "A meal kit delivery service that uses AI to learn your taste preferences and automatically adjusts recipes each week based on your feedback.",
    "panel_size": 50,
    "audience": {
      "size": 50,
      "gender": "all",
      "region": "us_national",
      "income_bracket": "middle"
    }
  }'
```

#### Example: A/B Test

```bash
curl -X POST https://api.instantfocus.dev/v1/evaluate \
  -H "Authorization: Bearer if_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "study_type": "ab_test",
    "variant_a": "FreshBox — Weekly meal kits with chef-designed recipes. $12.99/serving. Free shipping on first order.",
    "variant_b": "FreshBox — AI-powered meal kits that learn your taste. Recipes adapt each week. $14.99/serving.",
    "panel_size": 100
  }'
```

#### Example: Survey

```bash
curl -X POST https://api.instantfocus.dev/v1/evaluate \
  -H "Authorization: Bearer if_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "study_type": "survey",
    "stimulus": "A new fitness app that uses your phone camera to count reps and correct your form in real-time.",
    "questions": [
      "What is your first impression of this product?",
      "How much would you pay per month for this?",
      "What feature would make you switch from your current fitness app?"
    ],
    "panel_size": 50
  }'
```

---

### GET /v1/jobs/:id

Poll for async job status and results. Use when `/v1/evaluate` returns a 202.

**Authentication required.**

#### Response — Processing (202)

```json
{
  "ok": true,
  "data": {
    "job_id": "f8a2e3c4-...",
    "status": "processing",
    "panel_size": 500,
    "total_batches": 100,
    "batches_completed": 42,
    "batches_failed": 0,
    "progress_pct": 42,
    "created_at": "2026-04-13T04:30:00Z",
    "updated_at": "2026-04-13T04:30:12Z"
  },
  "meta": {
    "credits_used": 500,
    "credits_remaining": 4500,
    "engine": "standard"
  }
}
```

#### Response — Completed (200)

```json
{
  "ok": true,
  "data": {
    "job_id": "f8a2e3c4-...",
    "status": "completed",
    "panel_size": 500,
    "total_batches": 100,
    "batches_completed": 100,
    "batches_failed": 0,
    "progress_pct": 100,
    "completed_at": "2026-04-13T04:30:25Z",
    "result": {
      "study_type": "concept_test",
      "panel_size": 500,
      "mean_score": 5.6,
      "...": "full study results"
    }
  }
}
```

#### Job Status Values

| Status | Meaning |
|--------|---------|
| `pending` | Job created, batches queuing |
| `processing` | Batches being processed by LLM workers |
| `finalizing` | All batches complete, assembling results |
| `completed` | Results ready |
| `failed` | Job failed (check `error` field) |

#### Polling Strategy

```javascript
async function pollJob(jobId, apiKey) {
  while (true) {
    const res = await fetch(`https://api.instantfocus.dev/v1/jobs/${jobId}`, {
      headers: { Authorization: `Bearer ${apiKey}` },
    });
    const data = await res.json();

    if (data.data.status === 'completed') return data.data.result;
    if (data.data.status === 'failed') throw new Error(data.data.error);

    // Poll every 1-2 seconds
    await new Promise(r => setTimeout(r, 1000));
  }
}
```

---

### GET /v1/usage

Check current credit balance and usage statistics.

**Authentication required.**

#### Response

```json
{
  "ok": true,
  "data": {
    "tier": "pro",
    "credits_balance": 4500,
    "credits_used_this_month": 500,
    "request_count": 12,
    "last_request": "2026-04-13T04:30:00Z",
    "billing_period": "2026-04"
  }
}
```

---

### GET /v1/health

Service health check. No authentication required.

#### Response

```json
{
  "status": "ok",
  "service": "instantfocus",
  "version": "1.0.0",
  "timestamp": "2026-04-13T04:30:00Z",
  "providers": {
    "openai": true,
    "google": true,
    "groq": true,
    "anthropic": true
  }
}
```

---

## Study Types

### concept_test

Rate a product or feature concept on a 1–10 scale with purchase intent.

**Response fields:** `mean_score`, `median_score`, `std_dev`, `score_distribution`, `purchase_intent`, `top_concerns`, `top_appeals`, `responses`

**Per-persona fields:** `score` (1–10), `sentiment`, `reasoning`, `purchase_intent`

### sentiment

Analyze sentiment toward a stimulus.

**Response fields:** `positive_pct`, `negative_pct`, `neutral_pct`, `mixed_pct`, `confidence`, `key_themes`, `responses`

**Per-persona fields:** `sentiment` (positive/negative/neutral/mixed), `reasoning`, `key_theme`

### nps

Net Promoter Score prediction.

**Response fields:** `nps_score` (-100 to 100), `promoters_pct`, `passives_pct`, `detractors_pct`, `mean_score`, `reasons_to_promote`, `reasons_to_be_passive`, `reasons_to_detract`, `responses`

**Per-persona fields:** `score` (0–10), `reasoning`, `sentiment`

### survey

Custom questions to the panel.

**Response fields:** `question_results` (per-question summaries with common themes), `responses`

**Per-persona fields:** `answers` (question → answer mapping), `reasoning`

### ab_test

Compare two variants head-to-head.

**Response fields:** `winner` (a/b/tie), `variant_a_score`, `variant_b_score`, `variant_a_pct`, `variant_b_pct`, `no_preference_pct`, `key_differentiators`, `responses`

**Per-persona fields:** `preference` (a/b/none), `score_a` (1–10), `score_b` (1–10), `reasoning`, `sentiment`

---

## Response Envelope

All responses follow this structure:

```json
{
  "ok": true,
  "data": { },
  "meta": {
    "request_id": "uuid",
    "credits_used": 50,
    "credits_remaining": 4950,
    "latency_ms": 3420
  }
}
```

Error responses:

```json
{
  "ok": false,
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "This study requires 500 credits but you have 100 remaining."
  },
  "meta": {
    "request_id": "uuid",
    "credits_used": 0,
    "credits_remaining": 100,
    "latency_ms": 12
  }
}
```

---

## Error Codes

| HTTP Status | Code | Meaning |
|-------------|------|---------|
| 200 | — | Success (synchronous or completed job) |
| 202 | — | Accepted (async job created — poll for results) |
| 400 | `VALIDATION_ERROR` | Invalid request body |
| 401 | `UNAUTHORIZED` | Missing or invalid API key |
| 402 | `INSUFFICIENT_CREDITS` | Not enough credits for this study |
| 403 | `FORBIDDEN` | Access denied (wrong API key for job) |
| 404 | `NOT_FOUND` | Endpoint or job not found |
| 500 | `INTERNAL_ERROR` | Server error — retry with exponential backoff |
| 500 | `EVALUATION_ERROR` | LLM evaluation failed |
| 500 | `JOB_CREATION_ERROR` | Failed to create async job |

---

## Credits

Each persona evaluation costs 1 credit. A study with `panel_size: 50` costs 50 credits.

- Free tier: 100 credits/month
- Pay as you go pricing, with volume discounts
- Pay only for what you use, no minimums, no required subscriptions
- Buy credits at [instantfocus.dev/pricing](https://instantfocus.dev/pricing)

---

## Rate Limits

- Standard tier: 10 requests/minute
- Pro tier: 60 requests/minute
- Async jobs: panel sizes up to 10,000 personas

---

## Engine Modes

### standard (default)

Generates diverse ephemeral personas using demographic distributions and OCEAN personality traits. Each persona is unique per request.

### anchor

Uses pre-calibrated personas with established behavioral profiles. More consistent across runs, ideal for benchmarking and longitudinal studies. Requires anchor personas to be loaded via admin API.
