Skip to content

HTTP API

The Pillbox HTTP server exposes a REST API for the web UI and custom integrations. Start the server with pillbox serve start (default port: 4242).

Base URL: http://pillbox.local:4242/api

All responses use a uniform JSON envelope:

{ "ok": true, "data": { ... } }
{ "ok": false, "error": "not_found", "message": "..." }

Returns all registered bottles from the global registry.

Response: array of Bottle objects.

Registers a new bottle and adds it to the global registry.

Request body:

{
"name": "my-project",
"display_name": "My Project",
"directory": "/home/user/projects/my-project",
"scope": "local"
}

Response: 201 Created with the created Bottle object.

Returns a single bottle by UUID v7.

Response: Bottle object or 404.


Prescriptions are nested under their bottle.

Returns the prescription history for a bottle.

Query paramTypeDescription
limitintegerMax results (default: 50)

Response: array of Prescription objects.

Opens a new prescription for a bottle.

Request body:

{
"title": "Implement OAuth login"
}

Response: 201 Created with the Prescription object.
Error: 409 Conflict with the currently open prescription if one already exists.

GET /bottles/:bottle_id/prescriptions/:rx_id

Section titled “GET /bottles/:bottle_id/prescriptions/:rx_id”

Returns a prescription by UUID v7.

Response: Prescription object or 404.

PATCH /bottles/:bottle_id/prescriptions/:rx_id

Section titled “PATCH /bottles/:bottle_id/prescriptions/:rx_id”

Closes an open prescription (sets ended_at to now).

Response: updated Prescription object or 404.

DELETE /bottles/:bottle_id/prescriptions/:rx_id

Section titled “DELETE /bottles/:bottle_id/prescriptions/:rx_id”

Discards a prescription (soft delete). The prescription and its pills are archived with deleted_at set.

Response: { "discarded": true } or 404.

DELETE /bottles/:bottle_id/prescriptions/:rx_id/purge

Section titled “DELETE /bottles/:bottle_id/prescriptions/:rx_id/purge”

Permanently deletes a prescription and all related data: dispense_log entries and all its pills.

Response: { "purged": true } or 404.


Pills are nested under their prescription, which is nested under their bottle.

GET /bottles/:bottle_id/prescriptions/:rx_id/pills

Section titled “GET /bottles/:bottle_id/prescriptions/:rx_id/pills”

Returns all pills attached to a prescription.

Response: array of Pill objects or 404 if the prescription does not exist.

POST /bottles/:bottle_id/prescriptions/:rx_id/pills

Section titled “POST /bottles/:bottle_id/prescriptions/:rx_id/pills”

Creates a new pill.

Request body:

{
"title": "Switched to JWT auth",
"content": "Replaced express-session with jsonwebtoken...",
"compound": "decision"
}

Response: 201 Created with the Pill object.

GET /bottles/:bottle_id/prescriptions/:rx_id/pills/:pill_id

Section titled “GET /bottles/:bottle_id/prescriptions/:rx_id/pills/:pill_id”

Returns a pill by integer ID.

Response: Pill object or 404.

PATCH /bottles/:bottle_id/prescriptions/:rx_id/pills/:pill_id

Section titled “PATCH /bottles/:bottle_id/prescriptions/:rx_id/pills/:pill_id”

Updates a pill’s title, content, or compound. All fields are optional.

Request body:

{
"title": "Updated title",
"content": "Updated content",
"compound": "architecture"
}

Response: updated Pill object or 404.

DELETE /bottles/:bottle_id/prescriptions/:rx_id/pills/:pill_id

Section titled “DELETE /bottles/:bottle_id/prescriptions/:rx_id/pills/:pill_id”

Discards a pill (soft delete). The record is archived with deleted_at set.

Response: archived Pill object or 404.

DELETE /bottles/:bottle_id/prescriptions/:rx_id/pills/:pill_id/purge

Section titled “DELETE /bottles/:bottle_id/prescriptions/:rx_id/pills/:pill_id/purge”

Permanently deletes a pill.

Response: { "purged": true } or 404.

Full-text search over pills across all bottles.

Query paramTypeDescription
querystringSearch query (required)
bottle_idstring (UUID v7)Restrict to a specific bottle
compoundstringFilter by compound type
limitintegerMax results (default: 20)

Response: array of SearchResult objects with id, title, snippet, rank, compound, prescription_id, and bottle_id.


Returns a navigable prescription index for a bottle — useful for populating an agent’s system prompt.

Query paramTypeDescription
limitintegerMax prescriptions to return (default: 30)

Response:

{
"ok": true,
"data": {
"context": "...",
"prescription_count": 12
}
}

Capsules use the global database (~/.pillbox/pillbox.db) regardless of which bottle is active.

Creates a new capsule.

Request body:

{
"title": "Git branch naming convention",
"content": "Always prefix feature branches with feat/...",
"compound": "convention"
}

Response: 201 Created with the Capsule object.

Lists capsules, including archived ones (with deleted_at set). Active capsules appear first.

Query paramTypeDescription
compoundstringFilter by compound type
limitintegerMax results

Response: array of Capsule objects.

Returns a capsule by integer ID, including if it is archived.

Response: Capsule object or 404.

Updates a capsule’s title or content.

Request body:

{
"title": "Updated title",
"content": "Updated content"
}

Response: updated Capsule object or 404.

Discards a capsule (soft delete). The record is archived with deleted_at set.

Response: archived Capsule object or 404.

Permanently deletes a capsule.

Response: { "purged": true } or 404.

Full-text search over capsules.

Query paramTypeDescription
querystringSearch query (required)
compoundstringFilter by compound type
limitintegerMax results (default: 20)

Response: array of SearchResult objects.


Returns the running server version.

Response:

{ "ok": true, "data": { "version": "0.15.2" } }

CodeMeaning
200 OKSuccessful read or update
201 CreatedResource created
404 Not FoundResource does not exist
409 ConflictPrescription already open for this bottle
422 Unprocessable EntityValidation failed on the request body
500 Internal Server ErrorUnexpected server error