# Emable API

Developer access to Emable data and asynchronous AI jobs.

The emable platform API: company intelligence, async AI jobs, and workspaces — plus Validator over MCP — the layer that powers Talent and Validator.

## Choose the right path

Start with the outcome you want, not the endpoint list:

| Goal | How | Scope |
|---|---|---|
| Prove a key works and see whose identity it acts as | `GET /v1/me` | `profile:read` |
| Analyze a company website with AI | `POST /v1/company-analyses`, then poll `GET /v1/jobs/{job_id}` | `company:analyze`, `jobs:read` |
| See what a workspace-bounded key can access | `GET /v1/workspaces` | `workspaces:read` |
| Do any of the above from a terminal or CI | The `emable` CLI — see below | same scopes |
| Run Validator from Claude (or any AI assistant) | Connect over MCP — see below | `validator:mcp:access` |

Prefer clicking to reading? The [developer platform](https://emable.ai/developers) shows every capability as a working request, and its [test bench](https://emable.ai/developers/bench) fires them live with your key.

## From the terminal

The `emable` CLI wraps this same API for shell and CI use — one key model, one error vocabulary, no client code:

```bash
emable login                                  # stores a verified key, 0600
emable me                                     # GET /v1/me
emable analyze https://example.com --wait     # submit, then poll to a result
emable jobs get <job_id> --json               # raw payload, for jq
emable mcp install claude-code                # register the MCP server below
```

`--json` prints the untouched API payload. Errors carry the same codes as the REST surface, mapped onto exit codes so scripts can branch: `2` usage, `3` auth (`401`/`403`), `4` quota (`402`).

## Authentication

Every request sends your API key as a bearer token:

```
Authorization: Bearer em_test_your_key_here
```

Keys are created at [emable.ai/developers/keys](https://emable.ai/developers/keys), carry explicit scopes, are bounded to one workspace and can be revoked instantly. Secrets are shown once. Browser sign-in, OAuth, account management and billing webhooks are **not** part of this API — they belong to the emable app and to Stripe.

## Validator over MCP

MCP (Model Context Protocol) lets an AI assistant — Claude, or any MCP client — **run Validator in plain language**: define an audience, pose a hypothesis, run the simulation, and read the GO / NO-GO verdict, all from the chat, no client code. Validator's simulations are grounded in emable's proprietary market data (the advisor and market intelligence behind Talent and Validator), so the personas behave like the real Central-European financial-distribution market, not a generic model guess.

**Connect in three steps:**

1. In the emable app, open **Developers → MCP** (or Validator → Connect to    Claude) and generate a personal connection URL.
2. In claude.ai, go to **Settings → Connectors → Add custom connector** and    paste the URL. For a local client instead, run    `emable mcp install claude-desktop --url "<the URL>"` (also `cursor`,    `claude-code`).
3. Ask Claude, e.g. *"List my validator audiences"* or *"Run a validation:    should we launch a premium advisory tier?"*

**Tools the assistant gets** (each maps to a Validator operation, scoped to your account):

| Tool | What it does |
|---|---|
| `list_audiences` | List your reusable audiences / digital twins |
| `get_audience_details` | Inspect one audience's definition |
| `create_audience` | Build a new audience from a plain-language description |
| `run_validation` | Simulate a hypothesis against an audience |
| `get_result` | Read a run's verdict, sentiment and question breakdown |
| `list_validations` | List past validation runs |

**How the connection authenticates** (you do not call this yourself): the connection URL carries a personal token; the MCP server exchanges it for a short-lived session, scoped `validator:mcp:access`, bound to your account. Revoke a connection any time from the same screen.

## Formats

This reference is also available as clean Markdown at [/llms.txt](/llms.txt) (alias `/docs.md`) — one fetch gives an AI assistant the whole surface — and as machine schema at [/openapi-public.json](/openapi-public.json).


## Quickstart

- **Base URL:** `https://api.emable.ai`
- **First request:**

```bash
curl https://api.emable.ai/v1/me \
  -H "Authorization: Bearer em_test_your_key_here"
```

## AI jobs

### `POST /v1/company-analyses`

**Submit a company analysis**

Queues one tenant-owned `company.analyze_url` job. Requires `company:analyze` and an `Idempotency-Key` header. Repeat submissions with the same canonical input return the original job (and do not consume quota). Each new analysis counts against the key's monthly quota; when it is exhausted the request is rejected with `402` before any work starts. Poll the returned URL for completion.

**Parameters:**
- `Idempotency-Key` (header, string, required)

**Request body:**
- `company_url` (string, required): Absolute HTTP or HTTPS URL for the company website.

**Responses:**
- `202`: Successful Response
- `401`: Missing or invalid API key.
- `402`: The key's monthly company-analysis quota is exhausted.
- `403`: The API key lacks the required scope.
- `404`: The requested resource does not exist.
- `409`: The idempotency key was already used with different input.
- `422`: The request could not be validated.
- `429`: The caller exceeded a future rate limit.
- `500`: An unexpected server error occurred.
- `503`: The database is temporarily unavailable.

### `GET /v1/jobs/{job_id}`

**Get an AI job**

Returns a safe polling view of one job owned by the API-key user. Requires `jobs:read`. Unknown and other-tenant identifiers share the same `404`.

**Parameters:**
- `job_id` (path, string, required)

**Responses:**
- `200`: Successful Response
- `401`: Missing or invalid API key.
- `403`: The API key lacks the required scope.
- `404`: The requested resource does not exist.
- `422`: The request could not be validated.
- `429`: The caller exceeded a future rate limit.
- `500`: An unexpected server error occurred.
- `503`: The database is temporarily unavailable.

## Capabilities

### `GET /v1/capabilities`

**List Capabilities**

**Parameters:**
- `q` (query, string, optional)
- `outcome` (query, string, optional)
- `country` (query, string, optional)
- `provider` (query, string, optional)
- `status` (query, string, optional)
- `access` (query, string, optional)
- `pricing_model` (query, string, optional)

**Responses:**
- `200`: Successful Response
- `422`: Validation Error

### `GET /v1/capabilities/{slug}`

**Capability Detail**

**Parameters:**
- `slug` (path, string, required)

**Responses:**
- `200`: Successful Response
- `422`: Validation Error

## Insurance assistant

### `GET /v1/insurance/catalog`

**What the assistant reads**

Every market the assistant can answer for, with the carriers and document classes behind it. Public and unauthenticated on purpose: a reader has to be able to judge the corpus BEFORE trusting an answer out of it, and that judgement cannot be gated behind the sign-up it is supposed to inform.

Counts only documents that are actually retrievable — loaded AND embedded. A document the assistant cannot reach is not coverage.

`document_types` distinguishes what the documents ARE. `terms` is the binding policy wording, `ipid` the EU-standardised two-page summary, `methodology` a distributor-facing underwriting manual. `provenance` separates documents published by the carrier from documents supplied to us under a distribution relationship — different claims, and only the first is one a reader can go and verify.

**Responses:**
- `200`: Successful Response

## Profile

### `GET /v1/me`

**Get the caller profile**

Returns the six public profile fields for the user resolved from the API key. Requires `profile:read`. The caller cannot provide or override a user ID. Example: `curl -H 'Authorization: Bearer em_test_FAKE...' https://api.example.test/v1/me`.

**Responses:**
- `200`: Successful Response
- `401`: Missing or invalid API key.
- `403`: The API key lacks the required scope.
- `404`: The caller profile does not exist.
- `422`: The request could not be validated.
- `429`: The caller exceeded a future rate limit.
- `500`: An unexpected server error occurred.
- `503`: The database is temporarily unavailable.

## Service status

### `GET /health`

**Check service health**

Returns static process health. This endpoint is public and never queries PostgreSQL.

**Responses:**
- `200`: Successful Response

## Use cases

### `GET /v1/use-cases`

**List Use Cases**

**Parameters:**
- `q` (query, string, optional)
- `outcome` (query, string, optional)
- `country` (query, string, optional)
- `provider` (query, string, optional)
- `status` (query, string, optional)
- `access` (query, string, optional)
- `category` (query, string, optional)
- `max_price` (query, string, optional)

**Responses:**
- `200`: Successful Response
- `422`: Validation Error

### `GET /v1/use-cases/{slug}`

**Use Case Detail**

**Parameters:**
- `slug` (path, string, required)

**Responses:**
- `200`: Successful Response
- `422`: Validation Error

### `POST /v1/use-cases/{slug}/quote`

**Quote Use Case**

**Parameters:**
- `slug` (path, string, required)

**Request body:**
- `input` (object, required)

**Responses:**
- `200`: Successful Response
- `422`: Validation Error

### `POST /v1/use-cases/{slug}/runs`

**Start Use Case Run**

**Parameters:**
- `slug` (path, string, required)

**Request body:**
- `input` (object, required)

**Responses:**
- `202`: Successful Response
- `422`: Validation Error

## Workspaces

### `GET /v1/workspaces`

**List the caller workspaces**

Returns active workspaces where the API-key user is a member. Requires `workspaces:read`. A workspace-bound key returns only its bound workspace. This first list is intentionally unpaginated; future list endpoints use opaque cursor pagination as documented in the README.

**Responses:**
- `200`: Successful Response
- `401`: Missing or invalid API key.
- `403`: The API key lacks the required scope.
- `404`: The requested resource does not exist.
- `422`: The request could not be validated.
- `429`: The caller exceeded a future rate limit.
- `500`: An unexpected server error occurred.
- `503`: The database is temporarily unavailable.
