mountable.io

Cookbook: a report after the API worker restarts

Generate a small report in FastAPI, upload the closed file, and return those same bytes from a fresh API process.

A FastAPI worker can build a report for one signed-in user. When that process exits, its disk is gone.

mountable.io is a persistent filesystem for AI agents. This recipe uploads the closed report before it returns an id. A later process reads that same file.

mountable.io maintains the runnable example, separately from FastAPI. The report is synthetic. It holds no customer data. This is not a job queue or a login system.

Prerequisites

Use an existing mountable.io account with a saved payment method, an existing filesystem and an API key that can access it. Storage and file operations follow the usage prices. The example creates no account, filesystem or credits.

You also need Python 3.11 or newer and uv. Set MOUNTABLE_API_KEY in your private environment or secret manager. Never put a key in a command argument.

git clone https://github.com/writeitai/mountable.git
cd mountable/examples/fastapi-reports
uv sync --locked
export MOUNTABLE_FILESYSTEM_ID="your-filesystem-id"
export REPORT_STORE=mountable

The example uses the Python SDK. It does not mount the filesystem. For one filesystem and a hard monthly spend cap per end user, follow the capped workspace guide. This recipe uses one existing filesystem and a path your backend chooses.

The two fixture tokens are fixture-alice and fixture-bob. They exist only in this example. Your own API would use its real session. It would still choose the filesystem and the path, and it would not take either from the request.

Create a report

Start the API:

uv run uvicorn app:create_default_app --factory --host 127.0.0.1 --port 8000

Ask for a report as Alice:

curl -s -H "Authorization: Bearer fixture-alice" \
  -X POST http://127.0.0.1:8000/reports

A successful response is 201 and JSON with one field:

{"id":"a1b2c3d4e5f60789"}

The id is 16 lowercase hex characters. It is not a filesystem id and it is not a path. The server writes reports/alice/<id>.txt only after it has closed the report. The write uses If-None-Match: *, so an existing file is left as it is.

The file looks like this, with that request's id:

report_id: a1b2c3d4e5f60789
user: alice
kind: synthetic
item,quantity
alpha,1
beta,2

If the upload fails, the response is 503 and {"detail":"upload_failed"}. That JSON has no id. A missing or unknown token returns 401.

The request body is ignored. Sending a filesystem id or a path does not change where the file goes.

Read it from a fresh process

Stop the server and start it again with the same environment. Then read the report:

curl -s -D - -H "Authorization: Bearer fixture-alice" \
  http://127.0.0.1:8000/reports/$ID

The body is the same file. Save it and compare the SHA-256 with the copy you took before the restart:

curl -s -H "Authorization: Bearer fixture-alice" \
  http://127.0.0.1:8000/reports/$ID | sha256sum

The two hashes match when every byte matches.

The same id with Bob's token returns 404 and {"detail":"not_found"}. The server looks only under reports/bob/. Alice's file stays where it is.

One command runs that check for you. It creates a report, restarts the server, compares the SHA-256, checks Bob's refusal, and deletes the file:

uv run python app.py live

It prints match, the remote path, the SHA-256 and the size. It does not print the API key.

A 402 with filesystem_spending_cap_reached stops that filesystem's billable mountable.io work. Other filesystems keep working.

Clean up

uv run python app.py cleanup --user alice --report-id "$ID"

This deletes that one file. A file that is already gone is left gone.

Offline checks

From the example directory:

uv run pytest -q

The tests use FastAPI's TestClient and a fake store, and they also start two local server processes over a temporary directory. They make no paid cloud calls. They check the report hash, a refusal for the other fixture user, and that a failed upload returns no id.

Sources: FastAPI testing, FastAPI dependencies, run a FastAPI process, Python SDK.