# Inbox Preview API — Agent Skills

You are interacting with an inbox preview service that renders HTML emails as they would appear in real email web clients (Gmail, Outlook.com, and Yahoo Mail) and captures screenshots.

## Base URL

```
https://api.powerline.ai
```

## Workflow

1. **Submit** email HTML (or a base64 `.eml`) via `POST /v1/jobs`
2. **Poll** for results via `GET /v1/jobs/{id}` (status progresses: `queued → rendering → completed` or `failed`) — or stream renders as they finish via Server-Sent Events at `GET /v1/jobs/{id}/events`
3. **Use** the screenshot URLs from the completed job

Typical completion time is 10–60 seconds depending on which and how many clients are requested.

## Endpoints

### Submit a preview job

```
POST /v1/jobs
Content-Type: application/json

{
  "html": "<html>...</html>",
  "subject": "Optional email subject",
  "renders": [
    { "client": "gmail", "viewport_width": 600 },
    { "client": "outlook" },
    { "client": "outlook", "color_scheme": "dark" },
    { "client": "outlook", "color_scheme": "dark", "images_disabled": true }
  ]
}
```

- `html`: The full email HTML to preview. Provide exactly one of `html` or `eml`.
- `eml`: A complete raw RFC 822 message (`.eml`), base64-encoded — inserted verbatim (real headers, multipart, inline images, attachments). Max 10MB decoded.
- `subject` (optional): Subject line, used when synthesizing a message from `html`.
- `renders` (optional): **Exactly the renders you want, one spec each** — a client plus its environment. Up to 30 per job. Each spec takes:
  - `client` (required): Client ID. `GET /v1/clients` lists what is renderable right now.
  - `viewport_width` (optional): Pixel width, 200–2560. Defaults to the client's default (`1280`, a standard desktop view).
  - `color_scheme` (optional): `"light"` (default) or `"dark"`. **Dark requires client support** — `outlook` and `yahoo` today (both actively transform message colors, a common source of broken emails); check `color_schemes` in `GET /v1/clients`. An unsupported scheme is a 400 naming the entry, never a silent skip.
  - `images_disabled` (optional): Render with remote images blocked (default `false`). Composes with `color_scheme` — dark with images blocked is a valid render.

  Duplicate specs (after defaults apply) are dropped. Omit `renders` entirely for the server default: every live client at its default width, light, images on.

Response (202):
```json
{
  "job_id": "j_V1StGXR8Z5jdHi6B",
  "status": "queued",
  "poll_url": "/v1/jobs/j_V1StGXR8Z5jdHi6B",
  "renders_accepted": [
    { "client": "gmail", "viewport_width": 600, "color_scheme": "light", "images_disabled": false }
  ],
  "render_count": 1,
  "estimated_completion_seconds": 5
}
```

`renders_accepted` is the definitive list the job will produce — defaults applied, duplicates dropped.

### Poll job status

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

When `status` is `"completed"`, the response includes:
- `results.renders[]` — array of render results, each with:
  - `client`: client ID (e.g. `"gmail"`)
  - `client_description`: human name (e.g. `"Gmail (Web)"`)
  - `color_scheme`: `"light"` or `"dark"`
  - `images_disabled`: whether remote images were blocked
  - `viewport_width`: pixel width
  - `screenshot_url`: path to the PNG (e.g. `/v1/jobs/j_abc123/screenshots/gmail-600-light.png`)
  - `render_duration_ms`: how long the render took
- `results.failed_renders[]` — any renders that failed, each with an `error` message

### Get a screenshot

```
GET /v1/jobs/{job_id}/screenshots/{filename}
```

Returns the PNG image directly. Filename format: `{client}-{width}-{color_scheme}[-noimg].png` (extra captures use a `-subject` / `-inbox` suffix). Use the `screenshot_url` values from the job rather than constructing filenames.

### List available clients

```
GET /v1/clients
```

Returns the email clients this deployment can render **right now** — use it to decide what to request.

## Available Clients

| ID | Name | Tier | Notes |
|---|---|---|---|
| `gmail` | Gmail (Web) | real | A real logged-in Gmail web session, screenshotted |
| `outlook` | Outlook (Web) | real | A real logged-in Outlook.com web session, screenshotted |
| `yahoo` | Yahoo Mail (Web) | real | A real logged-in Yahoo Mail web session, screenshotted |

## Example: Full workflow

```bash
# 1. Submit
JOB=$(curl -s -X POST https://api.powerline.ai/v1/jobs \
  -H "Content-Type: application/json" \
  -d '{"html":"<html><body><h1>Hello</h1></body></html>","renders":[{"client":"gmail"}]}')
JOB_ID=$(echo $JOB | jq -r .job_id)

# 2. Poll until completed
curl -s https://api.powerline.ai/v1/jobs/$JOB_ID | jq .status

# 3. Get the render results (screenshot URLs)
curl -s https://api.powerline.ai/v1/jobs/$JOB_ID | jq .results.renders
```

## Tips for agents

- **Start with Gmail** — it's the strictest client. If your email renders well in Gmail, it will likely work everywhere.
- **Inspect the screenshots** — fetch each `screenshot_url` to see how the email actually rendered per client and environment.
- **Always check dark mode** — Outlook and Yahoo actively recolor message content in dark mode, which breaks emails that look fine in light. Request a dark spec alongside light for the clients that support it.
- **Use table layouts** — Gmail strips flexbox and grid. Table-based layouts are the safest for cross-client compatibility.
- **Check `failed_renders`** — any client that errored is listed there with an `error` message.

## Error format

All errors return:
```json
{
  "error": {
    "code": "not_found",
    "message": "The requested resource was not found.",
    "requestId": "req_a1b2c3d4e5f6"
  }
}
```

Some errors also carry a `suggestion` string (and validation errors a `details` object).
```

## MCP Server

The service includes a [Model Context Protocol](https://modelcontextprotocol.io) server for direct tool-calling integration with Claude Desktop, Claude Code, Cursor, and other MCP-compatible clients.

### Configuration

The server is hosted at `https://api.powerline.ai/v1/mcp` (Streamable HTTP) — nothing to install. Add to your MCP client config (e.g. `claude_desktop_config.json` or `.mcp.json`):

```json
{
  "mcpServers": {
    "inbox-preview": {
      "type": "http",
      "url": "https://api.powerline.ai/v1/mcp"
    }
  }
}
```

Or with Claude Code: `claude mcp add --transport http inbox-preview https://api.powerline.ai/v1/mcp`

### Available Tools

| Tool | Description |
|---|---|
| `list_clients` | List all supported email clients with their rendering tier, engine, and capabilities |
| `submit_preview` | Submit email HTML for rendering across email clients. Returns a job ID to poll. |
| `get_job` | Get the status and screenshots for a preview job. Poll until status is `"completed"`. |
| `get_screenshot` | Get the screenshot URL for a specific job, client, viewport width, and environment |

### Tool Parameters

**`submit_preview`**
- `html` (string): The full email HTML to preview. Provide exactly one of `html` or `eml`.
- `eml` (string): A complete raw `.eml`, base64-encoded (max 10MB decoded). Provide exactly one of `html` or `eml`.
- `subject` (string): Email subject line
- `renders` (array): Exactly the renders you want, one spec each — e.g. Outlook light and Outlook dark are two entries. Each spec: `client` (required — `gmail`, `outlook`, `yahoo`), `viewport_width` (default `1280`), `color_scheme` (`"light"` default | `"dark"` where supported — `outlook` and `yahoo` today), `images_disabled` (default `false`). Omit for the server default: every live client at its default width, light.

**`get_job`**
- `job_id` (string, required): The job ID returned by `submit_preview`

**`get_screenshot`**
- `job_id` (string, required): The job ID
- `client` (string, required): Client ID
- `viewport_width` (number, required): Viewport width of the render — must match a width the job rendered (`1280` when the job used client defaults). Prefer the `screenshot_url` values `get_job` returns.
- `color_scheme` (`"light"` | `"dark"`): The render's color scheme (default: `"light"`)
- `images_disabled` (boolean): Whether the render had images blocked (default: `false`)

### MCP Workflow Example

```
1. Call submit_preview with your email HTML
2. Call get_job with the returned job_id (repeat until status is "completed")
3. Call get_screenshot for specific renders you want to inspect
```

## Machine-readable spec

- This document: `GET /v1/skills.md`
- OpenAPI: `GET /v1/openapi.yaml`
- MCP server: `POST https://api.powerline.ai/v1/mcp` (Streamable HTTP)
