# From llms.txt to a triaged JSON You are an agent, or a developer with one. You have `https://fixragent.com/llms.txt` and a key. Five ways to a triaged JSON; pick one. Every step below is copy-pasteable. The curl path is one command. The JavaScript and Python paths are one downloaded file each, no dependencies. The MCP path is a download, a config edit and a restart. The Postman path answers on its first send, from the mock, before you set anything. ## 0 · The key Ask at [fixragent.com/docs#key](https://fixragent.com/docs#key), or email legal@fixragent.com with one line on what you are building, and a person replies with your key. While the key is on its way, the mock at `https://b1442b0b-4a7a-4135-ab4d-9c4b9c1ab0d2.mock.pstmn.io/api/triage` ([details](https://fixragent.com/docs#mock)) replays captured replies with no key, so your client is wired up and tested on day one. The mock takes a request body of up to 1 MB, which holds a photo of about 700 KB. A phone photo is usually larger, so make a small copy first; either line writes `photo-small.jpg`, at most 1,024 pixels on its longer side: ```sh sips -Z 1024 photo.jpg --out photo-small.jpg ``` That is macOS, built in. On Linux, or on macOS with Pillow (`pip install Pillow`): ```sh python3 -c "from PIL import Image, ImageOps; im = ImageOps.exif_transpose(Image.open('photo.jpg')); im.thumbnail((1024, 1024)); im.convert('RGB').save('photo-small.jpg', quality=85)" ``` Then send the small copy to the mock, with no key (on macOS, `base64 -w0 photo-small.jpg` is `base64 -i photo-small.jpg`): ```sh ( printf '{"mimeType":"image/jpeg","variant":"source:agents","image":"' base64 -w0 photo-small.jpg printf '"}' ) \ | curl -sS -X POST "https://b1442b0b-4a7a-4135-ab4d-9c4b9c1ab0d2.mock.pstmn.io/api/triage?config=fast" \ -H "Content-Type: application/json" \ --data-binary @- ``` The reply is a captured three-read triage with all twenty core fields. The real API takes photos up to 3 MB, so your own photos go to it as they are. Put the key in your environment. Every direct example here and on [/docs](https://fixragent.com/docs) reads it from `TRIAGE_DEMO_KEY`, the same name the Postman collection uses: ```sh export TRIAGE_DEMO_KEY='the key from your reply' test -n "$TRIAGE_DEMO_KEY" && echo 'key is set' ``` API base: https://fixragent.com/api/triage. It travels in the `x-triage-key` header. A demo key carries 60 requests a day; one address gets 20 an hour; the demo spends up to twenty dollars a day on our side. Every reply that stops you names the guard in a sentence and carries a `Retry-After` in seconds. ## 1 · curl — one command ```sh ( printf '{"mimeType":"image/jpeg","problem_text":"Water on the floor under the heater.","variant":"source:agents","image":"' base64 -w0 photo.jpg printf '"}' ) \ | curl -sS -X POST "https://fixragent.com/api/triage?config=fast" \ -H "Content-Type: application/json" \ -H "x-triage-key: $TRIAGE_DEMO_KEY" \ --data-binary @- ``` The body goes in on stdin because a photo of a few megabytes is about four megabytes of base64, longer than Linux accepts as one command-line argument. On macOS, `base64 -w0 photo.jpg` is `base64 -i photo.jpg`. The photo is JPEG, PNG or WebP, 1 KB to 3 MB decoded. What comes back, abridged — a production reply from 14 Sep 2026 (the full shape is at [/docs#core](https://fixragent.com/docs#core)): ```json { "diagnosis_id": "ae0deb3c-98c2-42ee-bad7-d4c0e62dd0a7", "engine_version": "engine-v2-vote-1.0.0", "rubric_version": "v1.1-2026-09-11", "config_id": "fast-a1v11-gemini-3.5-flash-lite-2026-09-12", "callback_url": "/api/outcome?diagnosis_id=ae0deb3c-98c2-42ee-bad7-d4c0e62dd0a7", "agreement": { "mode": "fast", "n": 3, "k": 3, "decided": true }, "core": { "is_building_asset": true, "asset_type": "Toilet", "asset_category": "PLUMBING", "fault_detected": false, "severity": "P4", "tier": "WHENEVER", "trade_required": null, "visible_wiring": false, "water_near_electrical": false }, "warnings": [] } ``` Read three fields first: `core.tier` (one of EMERGENCY, TODAY, THIS WEEK, WHENEVER — assigned in code from fixed rules), `core.trade_required`, and `core.resident_explanation`. The Triage Profile as a page is `https://fixragent.com/c/`. A `config_id` that begins `fallback-` means a hardcoded config answered in place of a measured candidate, and `warnings` says so. `config=fast` reads the photo three times and `config=deep` five times on production; both return `agreement`, how many reads gave the same yes/no answer about a fault, and `agreement.n` is the count that ran. `deep` takes longer and spends more of the day's demo budget. Neither is instant: set your client timeout to at least 60 seconds (the server ceiling). `&extended=1` adds the rest of the contract. ## 1b · JavaScript — one file, no dependencies ```sh curl -fsSL https://fixragent.com/sdk/js/index.mjs -o fixragent-triage.mjs ``` ```js import { readFile } from 'node:fs/promises'; import { triage, reportOutcome, FixragentError } from './fixragent-triage.mjs'; const reply = await triage(await readFile('photo.jpg'), { key: process.env.TRIAGE_DEMO_KEY, // sent as x-triage-key config: 'fast', // 'fast' reads the photo three times, 'deep' five problemText: 'Water on the floor under the heater.', }); console.log(reply.core.tier, reply.core.trade_required, reply.core.resident_explanation); await reportOutcome({ callbackUrl: reply.callback_url, outcome: 'fixed' }); // when you know ``` Node 18 or newer, or a browser with `fetch`. The photo is a `Buffer`, a `Uint8Array`, a base64 string or a data URL. A refused reply throws `FixragentError` with `status`, `code` (the API's own `error` value), `message` and `retryAfter`. `lang: 'es'` answers with the prose fields in Spanish; `hazard_detail` stays English. Every call sends `variant: source:sdk-js`, counted as channel `api`. Types: `https://fixragent.com/sdk/js/types.d.ts`, generated from openapi.json. Read me: https://fixragent.com/sdk/js/README.md. The same file is the `@fixragent/triage` npm package; the `npm install` line arrives with the first publish. ## 1c · Python — one file, standard library only ```sh curl -fsSL https://fixragent.com/sdk/python/fixragent_triage/__init__.py -o fixragent_triage.py ``` ```python import os from fixragent_triage import triage, report_outcome, FixragentError with open('photo.jpg', 'rb') as f: reply = triage(f.read(), key=os.environ['TRIAGE_DEMO_KEY'], config='fast', problem_text='Water on the floor under the heater.') core = reply['core'] print(core['tier'], core['trade_required'], core['resident_explanation']) report_outcome('fixed', callback_url=reply['callback_url']) # when you know ``` Python 3.9 or newer, `urllib` only. The photo is `bytes`, a base64 string or a data URL. A refused reply raises `FixragentError` with `status`, `code`, `message` and `retry_after`. `lang='es'` answers with the prose fields in Spanish; `hazard_detail` stays English. Every call sends `variant: source:sdk-python`, counted as channel `api`. Types: `https://fixragent.com/sdk/python/fixragent_triage/types.py` (TypedDict), generated from openapi.json. Read me: https://fixragent.com/sdk/python/README.md. The same file is the `fixragent-triage` PyPI package; the `pip install` line arrives with the first publish. ## 2 · The MCP tool — one file, no dependencies ```sh mkdir -p ~/fixragent-mcp curl -fsSL https://fixragent.com/mcp/server.js -o ~/fixragent-mcp/server.js FIXRAGENT_API_KEY="$TRIAGE_DEMO_KEY" node ~/fixragent-mcp/server.js --check ``` The tool reads its key from `FIXRAGENT_API_KEY`. Register it with your client. Claude Code: ```sh claude mcp add fixragent -e FIXRAGENT_API_KEY="$TRIAGE_DEMO_KEY" -- node ~/fixragent-mcp/server.js ``` Claude Desktop (`claude_desktop_config.json`) or Cursor (`.cursor/mcp.json`): ```json { "mcpServers": { "fixragent": { "command": "node", "args": ["/absolute/path/to/fixragent-mcp/server.js"], "env": { "FIXRAGENT_API_KEY": "the key from your reply" } } } } ``` Restart the client. The tool is `triage_photo`; ask for a photo to be triaged and give the path. Inputs: `image_path` or `image_base64`, `mime_type` (required), `problem_text`, `role` (`landlord` or `fixer`), `config` (`fast` or `deep`). Output: the API reply plus `share_url` and `outcome_url`. The full README, the install for each client, the error table and the tests: [fixragent.com/mcp/README.md](https://fixragent.com/mcp/README.md). Without a client, the server answers raw JSON-RPC over stdio, one message per line: ```sh printf '%s\n' \ '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"me","version":"0"}}}' \ '{"jsonrpc":"2.0","method":"notifications/initialized"}' \ '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"triage_photo","arguments":{"image_path":"photo.jpg","mime_type":"image/jpeg"}}}' \ | FIXRAGENT_API_KEY="$TRIAGE_DEMO_KEY" node ~/fixragent-mcp/server.js ``` ## 3 · Postman — import and send 1. Import [fixragent.com/docs/fixragent-triage-api.postman_collection.json](https://fixragent.com/docs/fixragent-triage-api.postman_collection.json) (Collection v2.1). 2. Send "Triage a photo — three reads (config=fast)" as it is: `baseUrl` starts on the mock, so the reply arrives with no key. 3. For a real triage, import the [environment file](https://fixragent.com/docs/fixragent-triage-api.postman_environment.json) beside it — `baseUrl` is already `https://fixragent.com` there — put your key in `TRIAGE_DEMO_KEY` and the photo as base64 in `photo_base64`, switch the environment on, and send again. (Or set the same three collection variables by hand.) The captured replies for 200, 400, 401, 413 and 503 sit beside the request as examples, and "Report the outcome (callback_url)" closes the loop. ## 4 · Channel — say which door you came through Every triage row records its door in `variant`. Direct calls send `"variant": "source:agents"` and are counted as channel `api`; the JavaScript and Python clients send `source:sdk-js` and `source:sdk-python` for you (also channel `api`); the MCP tool sends `source:mcp` for you and is counted as channel `mcp`. Send the value as written, and your traffic is counted on its own line. ## 5 · Tell us what happened Every reply carries a `callback_url`. When you know how it turned out, POST it back: ```sh curl -sS -X POST https://fixragent.com/api/outcome \ -H "Content-Type: application/json" \ -d '{"diagnosis_id":"","outcome":"fixed","reported_via":"triage_link"}' ``` `outcome` is `fixed`, `not_fixed` or `partial`. A person does the same in two taps at `https://fixragent.com/o/`. ## The specs - [OpenAPI 3.1, JSON](https://fixragent.com/openapi.json) · [YAML](https://fixragent.com/openapi.yaml) — one path, `POST /api/triage`, twenty core fields under `components.schemas.TriageCore`, every error body - [The reference](https://fixragent.com/docs) — the guards, severity and tier, the request body, every response block, what happens to your photo - [One-page sheet](https://fixragent.com/one-pager) — the Triage Profile and its payload on one printable page; [PDF, Letter](https://fixragent.com/docs/ONE-PAGER.pdf) · [PDF, A4](https://fixragent.com/docs/ONE-PAGER-A4.pdf) - [JavaScript client](https://fixragent.com/sdk/js/index.mjs) · [Python client](https://fixragent.com/sdk/python/fixragent_triage/__init__.py) — one file each, typed from the spec; [Postman environment](https://fixragent.com/docs/fixragent-triage-api.postman_environment.json) - [llms.txt](https://fixragent.com/llms.txt) · [llms-full.txt](https://fixragent.com/llms-full.txt) ## How it fits One fixed JSON shape every time, which your developers map to your own work-order fields. Use it today with a demo key. The Triage Profile at `/c/` is the page a person opens; `/o/` closes the loop. The reply is an educational triage for routing: gas, water near electrics and exposed wiring go straight to a licensed trade. Terms for the API: [fixragent.com/api-terms](https://fixragent.com/api-terms).