# ID.PHOTOS MCP service — instructions for agents

You are reading the reference for an MCP server that turns an ordinary photograph into a
**compliant ID photo** for more than a hundred national standards — passports, visas, residence
permits, driving licences — and returns the compliance verdict with it.

Everything on this page is stable and machine-readable. Human overview: <https://id.photos/mcp>

---

## 1. What this service is for

Give it a photograph of a person and a standard id. It returns a photo cropped, sized, levelled
and measured to that standard, plus a per-rule pass or fail so you can tell the person what is
wrong before anything is printed or paid for.

It does **not** take photographs, judge whether someone is eligible for a document, or submit
anything to any government.

---

## 2. Connect

```
Endpoint   POST https://id.photos/mcp/server      (JSON-RPC 2.0, MCP streamable HTTP)
Stream     GET  https://id.photos/mcp/server      (SSE; optional, nothing is pushed)
```

MCP client configuration:

```json
{
  "mcpServers": {
    "id-photos": {
      "url": "https://id.photos/mcp/server",
      "headers": {
        "X-Client-Id": "your-client-id",
        "X-Client-Secret": "your-client-secret"
      }
    }
  }
}
```

`initialize` and `tools/list` answer without credentials, so you can look at the tool list before
you have an account. Every other call needs them.

---

## 3. Get an account and credentials

**An account first.** Sign up at <https://id.photos/business/signup>. A person has to do this
once: it asks for the business, and accounts are reviewed before they can spend anything. There
is no way for an agent to create an account on its own, deliberately.

**Then credentials**, either way round:

*Register yourself* — one request, no portal, works immediately:

```http
POST https://id.photos/mcp/register
Content-Type: application/json

{ "email": "you@example.com", "password": "…", "client_name": "My agent" }
```

```json
{
  "client_id": "my-agent-4f1c9a2b7e",
  "client_secret": "…",
  "mcp_url": "https://id.photos/mcp/server",
  "headers": { "X-Client-Id": "…", "X-Client-Secret": "…" },
  "instructions_url": "https://id.photos/mcp/instructions.md"
}
```

*Or from the portal* — the account's **API access** page issues, rotates and revokes the same
credentials.

Either way: **the secret is shown once.** Store it. If it is lost, rotate — it cannot be read back.
Credentials do not expire, which is what makes them usable from a client configuration that holds
static headers.

An account token (`POST /api/v1/auth/login`, `Authorization: Bearer …`) is also accepted, but it
expires within hours; prefer the credentials.

---

## 4. What things cost

| | Credits |
|---|---|
| A photo | **1** |
| A photo with the background removed (`soft_v4`) | **1.5** |
| Processing, previewing, adjusting, re-rendering | **0** |

**You are charged when a photo is delivered, not when it is made.** Delivery means
`download_photo` or `send_photo_email`. Everything before that is free, so processing a photo
three times while you get the framing right costs nothing.

A photo is charged **once**. Downloading it again — tomorrow, next year — is free. If you deliver
a photo and then re-render it *with* background removal, only the 0.5 difference is charged.

Re-sending the same image without `session_id` starts a **new** photo that pays in full. To change
anything about a photo you already made, call `regenerate_photo` with its `session_id`.

Credits are bought in advance, by a person, in the business portal: a **credit pack**, or a monthly
plan — **Growth** ($49.99, 40 credits a month) or **Partner** ($249, 1,000 credits a month). Call
`get_account` to read the balance before a long run.

---

## 5. Tools

Arguments are JSON. Images are base64 strings (a `data:` URL is also accepted), up to **20 MB
decoded**.

### `list_standards` — free
`{ "country": "Canada" }` (optional filter). Returns standards grouped by country; use
`standard_id` from here.

### `get_standard_requirements` — free
`{ "standard_id": "US_PP" }`. Printed size, head height and what the standard demands. Aliases are
accepted and resolved (`us_passport` → `US_PP`).

### `process_photo` — free
```json
{
  "image_base64": "…",
  "standard_id": "US_PP",
  "background_type": "original",
  "bw_output": false,
  "background_fill": "white",
  "figure_scale": 0,
  "figure_pan": 0
}
```
Returns `session_id`, the validation results per rule, and the applied measurements. **Nothing is
charged.** Keep the `session_id`.

- `background_type` — `original` keeps the background; `soft_v1`…`soft_v3`, `soft_v5` soften or
  replace it; `soft_v4` is Remove Background and adds 0.5 credit at delivery.
- `background_fill` — `white` or `arabic_blue`, only with a `soft_*` background.
- `figure_scale` — one of `-10, -5, 0, 5, 10` (percent). Presentation only; it does not change any
  measurement.
- `figure_pan` — one of `-6, -3, 0, 3, 6` (percent; negative moves the subject up).

### `regenerate_photo` — free
Same arguments plus `session_id`. Re-renders **that** photo in place: same session, same single
fee. This is how you try a variation without paying twice.

### `get_session` · `list_sessions` — free
`{ "session_id": "…" }` / `{ "limit": 20, "offset": 0 }`.

### `download_photo` — **charges**
`{ "session_id": "…", "format": "digital" }` — `"printable"` only exists for standards with two
outputs. Returns `image_base64` (JPEG) and the remaining balance.

### `send_photo_email` — **charges**
```json
{ "session_id": "…", "to": ["customer@example.com"], "subject": "…",
  "message": "…", "include_back": false }
```

### `get_account` — free
Balance, plan, and the prices above.

---

## 6. A worked run

```
1. list_standards { "country": "United States" }        -> standard_id "US_PP"
2. get_standard_requirements { "standard_id": "US_PP" } -> 51x51 mm, head 25-35 mm
3. process_photo { image_base64, standard_id: "US_PP" } -> session_id, validation results
   ├─ a rule failed?   tell the person what to change, take a new photograph, process again
   └─ all passed?      continue
4. (optional) regenerate_photo { …, session_id, background_type: "soft_v4" }
5. download_photo { session_id }                        -> the image, 1 credit taken
```

Steps 1 to 4 cost nothing however many times they run. Step 5 is the only one that spends.

---

## 7. Errors

A failed tool answers as a **result** with `isError: true` and readable text — not a transport
error — so you can act on it. The text starts with the status where there is one.

| | Meaning | What to do |
|---|---|---|
| `401` | Credentials missing, wrong, revoked, or an app key rather than an account credential | Check the two headers; rotate if the secret was lost |
| `402` | Not enough credits, or the account has lapsed | A person must top up; `get_account` shows the balance |
| `403` | The account is not allowed to do that | Ask the account owner |
| `404` | No such session on this account | Check `session_id`; sessions are per account |
| `409` | The output was replaced by hand and will not be re-generated | Start a new photo |
| `429` | Rate limited, or self-serve registration is at its daily limit | Wait; `Retry-After` says how long |
| `503` | A processing service is briefly unavailable | Retry |

An unknown standard, a malformed image or an oversized image are refused the same readable way.

---

## 8. Rules to follow

- **Do not re-upload to change something.** `regenerate_photo` with the `session_id` costs
  nothing extra; a fresh upload is a new photo and a new charge.
- **Check the validation results before delivering.** A photo that fails a rule will be rejected
  at the counter, and the credit is spent either way.
- **Do not treat `download_photo` as idempotent-free the first time.** It is the charge point.
  Call it when the person actually wants the photo.
- **Never send a photograph you were not asked to process.** Every image is a picture of a real
  person, held under the account that sent it.
- **One session per person per standard.** Two standards for the same person are two photos and
  two charges — that is correct, not a bug to work around.
