---
lastModified: 2026-10-07
---

# Use Jinn from an agent

This page is for coding agents and the people who set them up. It says how to read these docs, how to call Jinn without a person watching, and how to tell what went wrong.

## Read the docs

- Every page is Markdown at the same address with `.md` added, for example [/runs.md](/runs.md). A request with `Accept: text/markdown` gets the Markdown too.
- [llms.txt](/llms.txt) lists every page with one line about it. [llms-full.txt](/llms-full.txt) holds all of them in one file.
- [openapi.json](/openapi.json) describes every route, field, error code and limit of the API. Each operation has an `operationId`.

## Authenticate

Give the agent an API key in `JINN_KEY`. An owner makes keys in the console under **Account**. Use a `run` key when the agent only starts runs of named functions. Use an `admin` key when it also publishes functions. Keys never expire; revoke one in the console when the agent no longer needs it.

The agent never needs a model vendor's key. Providers hold those, and people add them in the console or with `jinn provider`.

## Use the CLI

Install it with:

```sh
curl -fsSL https://github.com/usejinn/jinn-cli/releases/latest/download/install.sh | sh
```

Add `--json` to any command. Results are printed as one JSON document. A stream (`jinn run` without `--detach`, `jinn logs`) is printed as JSON lines. The CLI never prompts when stdin is not a terminal.

```sh
$ jinn run support-triage --prompt "Ticket #4812: charged twice" --in ./in --out ./result --json
{"run":{"id":"run_fc8b8312336d7d3d0c6cace0",…,"state":"queued",…},"type":"run"}
{"run":{"id":"run_fc8b8312336d7d3d0c6cace0",…,"state":"running",…},"type":"run"}
{"at":"2026-10-07T13:03:18.206042976Z","kind":"system","text":"You answer billing tickets for Acme. …","type":"log"}
{"arguments":"{\"email\":\"maria.lopez@brightpath.io\"}","at":"2026-10-07T13:03:24.738990358Z","id":"toolu_01AradMuJxWMfkM52RpwN8os","kind":"tool_call","name":"lookup_customer","type":"log"}
{"run":{"id":"run_fc8b8312336d7d3d0c6cace0",…,"state":"succeeded",…,"output":{…},…},"type":"run"}
{"dir":"./result","file_count":2,"files":[{"path":"actions.json","bytes":154},{"path":"reply.md","bytes":859}],"type":"output"}
```

| Line `type` | Holds |
|---|---|
| `run` | The run, as `GET /v1/runs/{id}` returns it. Printed when it is queued, when it starts and when it ends. |
| `log` | One log event: `at`, `kind` and the event's fields. |
| `output` | The folder the output was unpacked into, and its files. |

Errors are one JSON object on stderr: `{"error": "…", "exit": 3, "status": 402, "code": "no_credit"}`.

| Exit status | Meaning |
|---|---|
| 0 | Done. For `jinn run`, the run succeeded. |
| 1 | The run failed. Its last `run` line has `failure` and `detail`. |
| 2 | The command was used wrongly. |
| 3 | The API refused the request. `code` says why; see [API errors](/api#errors). |
| 4 | Anything else: the network, a local file. |

To start a run and come back later, use `--detach`, then `jinn show RUN --json` until `state` is `succeeded` or `failed`.

## Use the API

The API is plain HTTPS and JSON at `https://api.usejinn.com/v1`. A refused request has a stable `code`:

```json
{"code": "no_credit", "error": "this account has no credit left; an owner can add credit in the console"}
```

Switch on `code`, not on the message. Retry `conflict` and `internal`. Do not retry the others without a change. For results, poll `GET /v1/runs/{id}`, or start the run with a `webhook` and wait for the [event](/webhooks).

## Write a function an agent can rely on

- Name every file the run must return in `output_manifest`. The agent inside the run cannot finish until they exist, so the caller never gets a half-done result.
- End the system prompt with what to write where, then "call submit_result".
- Keep `timeout_minutes` close to what the work takes. A run that hangs costs until its timeout.
- Give setup commands exact package versions, so every run starts the same.
