> ## Documentation Index
> Fetch the complete documentation index at: https://docs.biomni.phylo.bio/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> From an API key to running an agent task and downloading its results

This guide takes you from an API key to a full agent run: create a project,
start a task, wait for it to finish, and pull the result files. Every call uses
your [API key](/api/authentication) as a bearer token.

## Set up your key

Create a key in **Settings → API Keys** (see [Authentication](/api/authentication)),
then export it:

```bash theme={null}
export BIOMNI_API_KEY="<your-api-key>"
```

<Info>
  The base URL is `https://api.phylo.bio/api/v1`. All requests act within the
  workspace your key belongs to.
</Info>

Confirm it works with a read-only call:

```bash theme={null}
curl https://api.phylo.bio/api/v1/projects \
  -H "Authorization: Bearer $BIOMNI_API_KEY"
```

A `200` means you're authenticated; a `401` means the key is missing, revoked,
or expired (see [Errors & rate limits](/api/errors)).

## The core loop

<Steps>
  <Step title="Create a project">
    Projects group related tasks and files.

    ```bash theme={null}
    curl -X POST https://api.phylo.bio/api/v1/projects \
      -H "Authorization: Bearer $BIOMNI_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"name": "BRCA1 variant workup"}'
    ```

    Response contains a project `id` (`proj_...`).
  </Step>

  <Step title="Start a task">
    Send a message with a `project_id` — Biomni auto-creates a task and starts
    the agent. The response includes the new `task` (with its `id`, `sess_...`).

    ```bash theme={null}
    curl -X POST https://api.phylo.bio/api/v1/messages \
      -H "Authorization: Bearer $BIOMNI_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "project_id": "proj_a4374b138a",
        "content": "Analyze the 3 BRCA1 variants: current ClinVar status, PARP inhibitor eligibility, and any conflicts in the literature."
      }'
    ```

    To attach files you uploaded to the project, pass their ids as
    `"file_ids": ["file_..."]` (see [Working with files](#working-with-files)).
  </Step>

  <Step title="Poll until it's done">
    Fetch the task and watch `status` move from `idle`/`running` to
    `completed` (or `failed`).

    ```bash theme={null}
    curl https://api.phylo.bio/api/v1/tasks/sess_d9711ec9b477 \
      -H "Authorization: Bearer $BIOMNI_API_KEY"
    ```

    Poll on an interval (e.g. every 5–10s) with backoff. Agent tasks can run for
    minutes; design for long-running work rather than a single blocking request.
  </Step>

  <Step title="Fetch the results">
    List the files the agent produced. Each carries a presigned `download_url`.

    ```bash theme={null}
    curl "https://api.phylo.bio/api/v1/tasks/sess_d9711ec9b477/results?with_urls=true" \
      -H "Authorization: Bearer $BIOMNI_API_KEY"
    ```

    Download each `download_url` to save the outputs.
  </Step>
</Steps>

## Full example (Python)

A complete, copy-pasteable script that runs the loop end-to-end:

```python theme={null}
import os, time, requests

BASE = "https://api.phylo.bio/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['BIOMNI_API_KEY']}"}

def post(path, body):
    r = requests.post(f"{BASE}{path}", headers=HEADERS, json=body)
    r.raise_for_status()
    return r.json()

def get(path, **params):
    r = requests.get(f"{BASE}{path}", headers=HEADERS, params=params)
    r.raise_for_status()
    return r.json()

# 1. Create a project
project = post("/projects", {"name": "BRCA1 variant workup"})
project_id = project["id"]

# 2. Start a task (auto-created from the message)
msg = post("/messages", {
    "project_id": project_id,
    "content": "Analyze the 3 BRCA1 variants: ClinVar status, PARP inhibitor "
               "eligibility, and literature conflicts.",
})
task_id = msg["task"]["id"]

# 3. Poll until the task finishes
while True:
    task = get(f"/tasks/{task_id}")
    if task["status"] in ("completed", "failed"):
        break
    time.sleep(5)

if task["status"] == "failed":
    raise RuntimeError(f"Task {task_id} failed")

# 4. Download result files
results = get(f"/tasks/{task_id}/results", with_urls="true")
for f in results.get("data", []):
    content = requests.get(f["download_url"]).content
    with open(f["name"], "wb") as fh:
        fh.write(content)
    print("saved", f["name"])
```

## Working with files

To give the agent input files, upload them to the project's Drive first, then
reference their ids when you start the task. The bytes go **directly to cloud
storage** — the API only hands you a presigned target and never touches the
file itself:

1. `POST /files/uploads` — begin an upload for a file in a project; returns a
   presigned `upload_url`, the `fields` to send with it, and a `file_id`.
2. POST the bytes to `upload_url` as form-data with those `fields` (returns
   `204`).
3. `POST /files/{file_id}/finalize` — confirm the upload so the agent can read
   it.
4. Pass `"file_ids": ["file_..."]` in your `POST /messages` call.

Files of any size work — the response's `upload_type` is `single` for small
files and `multipart` for large ones (upload the parts, then finalize with their
ETags). We recommend keeping individual files under **25 GB**. See the
[Upload a file](/api-reference/pages/upload-file) reference for both flows.

## Choosing a model tier

The first `POST /messages` that creates a Task can pick the agent's model tier
with `model`: `standard` (default), `fast` (lower latency), or `max` (highest
capability). Omit it to use the account default. The tier is set when the Task
is created, so continuing a Task (`task_id`) ignores it.

## Next steps

* [Recipes](/api/recipes) — batch runs, long-running tasks, cancellation.
* [Errors & rate limits](/api/errors) — status codes, retries, and limits.
