API
The Bee developer API serves your Bee data over HTTP under /v1/*. There are three ways to reach it:
- Public endpoint (default, recommended for experiments):
https://api.developer.bee.amazon.dev, with a standard public TLS certificate. - Local proxy:
bee proxyserves the API on127.0.0.1and handles authentication for you. - Raw TLS (advanced): TLS with attestation, straight to the API host. You verify Bee’s own certificate yourself.
Public endpoint (default)
This is the production Bee API, serving your real data.
This is the recommended way to experiment with the API. https://api.developer.bee.amazon.dev uses a certificate from a public CA, so any HTTP client works with its standard system trust store. Authenticate with a Bearer token:
curl -H "Authorization: Bearer $BEE_TOKEN" \
"https://api.developer.bee.amazon.dev/v1/me"TLS for this endpoint ends at Bee’s public TLS front end, which forwards your requests to the API. To have TLS end at the API itself, with attestation, use raw TLS.
Local proxy
The bee proxy command starts a local HTTP API proxy server, allowing you to access your Bee data through standard HTTP requests. This is useful for building local applications, testing integrations, or accessing Bee data from tools that don’t support the Bee protocol directly.
The proxy server is intended for local development only. Do not expose it to the public internet as it provides unauthenticated access to your Bee data.
You must authenticate first with bee login, then start bee proxy.
Usage
# TCP listener (defaults to port 8787, auto-picking the next free port if taken)
bee proxy
bee proxy --port 8787This starts a local HTTP server bound to 127.0.0.1, by default on port 8787.
If 8787 is in use, the proxy auto-picks the next free port (unless you pin one
with --port). Only paths under /v1/* are forwarded; any other path returns
404.
Options
| Flag | Description |
|---|---|
--port N | Listen on TCP port N on 127.0.0.1. Defaults to 8787 (auto-incrementing to the next free port when omitted). Cannot be combined with --socket. |
--socket [path] | Listen on a Unix domain socket instead of TCP. Defaults to ~/.bee/proxy.sock when no path is given. Stale socket files are removed before listening. Cannot be combined with --port. |
--idle-timeout SECONDS | Server idle request timeout. Defaults to 120. Increase it for large exports, or pass 0 to disable the idle timeout entirely. |
# Unix socket listener (default path: ~/.bee/proxy.sock)
bee proxy --socket
bee proxy --socket /tmp/bee-proxy.sock
# Raise the idle timeout for long-running exports (or 0 to disable)
bee proxy --idle-timeout 300
bee proxy --idle-timeout 0Proxy Authentication
When another trusted local process already handles Bee API authentication, point this
CLI at it with bee login --proxy so requests are forwarded through that proxy instead
of using a stored token directly:
# HTTP proxy
bee login --proxy http://127.0.0.1:8787
# Unix socket proxy
bee login --proxy ~/.bee/proxy.sockThis saves the proxy configuration to ~/.bee/proxy-{env}.json. When a proxy config
exists, it takes precedence over stored token auth. Run bee logout to clear it
before switching back to direct token auth.
Raw TLS (advanced)
This is also the production Bee API.
Raw TLS is TLS with attestation, for advanced use. Connect straight to https://app-api-developer.ce.bee.amazon.dev, the host the Bee CLI uses, so TLS ends at the API itself. Use the same /v1/* endpoints and authenticate with a Bearer token.
curl --cacert ./bee-ca.pem \
-H "Authorization: Bearer $BEE_TOKEN" \
"https://app-api-developer.ce.bee.amazon.dev/v1/me"You must verify Bee’s certificate yourself. This host uses Bee’s private CA (not a public CA), so standard system trust stores alone are not enough: configure your client to trust Bee’s root CA, as --cacert does above.
Certificate source used by Bee CLI (PROD_ROOT_CA):
API Reference
These paths work the same with all three options: prefix them with the public
endpoint, the raw TLS host, or your local proxy address. The proxy forwards any
/v1/* path straight through to the Bee API. The list below is a representative
subset, not an exhaustive catalog — additional
developer endpoints (for example insights, locations, photos, todoSuggestions,
conversations/:id/related, search/conversations, journals, and todayBrief)
are forwarded the same way.
User
# Get user profile
GET /v1/meChanges
# Get changed entity ids since cursor (or default window when cursor is omitted)
GET /v1/changesFacts
# List all facts
GET /v1/facts
# Get a specific fact
GET /v1/facts/:id
# Create a new fact
POST /v1/facts
Content-Type: application/json
{"text": "Your fact here"}
# Update a fact
PUT /v1/facts/:id
Content-Type: application/json
{"text": "Updated fact", "confirmed": true}
# Delete a fact
DELETE /v1/facts/:idTodos
# List all todos
GET /v1/todos
# Get a specific todo
GET /v1/todos/:id
# Create a new todo
POST /v1/todos
Content-Type: application/json
{"text": "New todo", "alarm_at": "2026-02-11T09:00:00Z"}
# Update a todo
PUT /v1/todos/:id
Content-Type: application/json
{"text": "Updated todo", "completed": true, "alarm_at": null}
# Delete a todo
DELETE /v1/todos/:idalarm_at is provided as an ISO 8601 timestamp on input (e.g.
"2026-02-11T09:00:00Z"), but is returned as epoch milliseconds in responses.
Journals
# List journals
GET /v1/journals
# Get a specific journal
GET /v1/journals/:idConversations
# List conversations
GET /v1/conversations
# Get a specific conversation
GET /v1/conversations/:idDaily Summaries
# List daily summaries
GET /v1/daily
# Get a specific daily summary
GET /v1/daily/:idSearch
# BM25 keyword search
POST /v1/search/conversations
Content-Type: application/json
{"query": "project roadmap", "limit": 20, "cursor": null}
# Neural search
POST /v1/search/conversations/neural
Content-Type: application/json
{"query": "project roadmap", "limit": 20}Streaming
# SSE stream (optional: ?types=new-utterance,todo-created)
GET /v1/stream
# Common event types:
# new-conversation, update-conversation, update-conversation-summary, delete-conversation
# new-utterance, todo-created, todo-updated, todo-deleted
# journal-created, journal-updated, journal-deleted, journal-textThreads
Preview: test users only. Every /v1/threads endpoint answers 403
{"error": "test_user_required"} for other accounts, and the API may still change.
Threads are your chats with the Bee agent, the same ones you see in the app. Send a
message, then wait until the agent finishes all its work on the thread, not just
the next message. That wait also covers work started elsewhere, such as a message sent
from the app. The bee threads commands wrap these endpoints.
# List threads, most recently active first (no paging)
GET /v1/threads
# Start a new thread with a first message
POST /v1/threads
Content-Type: application/json
{"text": "Plan my trip", "file_ids": [], "wait_ms": 30000, "client_message_id": "trip-1"}
# Send a message to a thread (same body)
POST /v1/threads/:id/messages
# Wait for the agent again, after a message of the thread
GET /v1/threads/:id/wait?after=:message_id&wait_ms=30000
# Get all messages, oldest first (no paging)
GET /v1/threads/:id/messages
# Stop the agent's work on the thread
POST /v1/threads/:id/abort
# Upload an attachment for file_ids (multipart/form-data)
POST /v1/threads/attachmentsSend body
| Field | Description |
|---|---|
text | Message text, up to 100,000 characters. |
file_ids | Up to 20 distinct ids from POST /v1/threads/attachments. Each must be your own upload that isn’t attached to a message yet. |
wait_ms | How long to wait for the agent, 0 to 120000. Defaults to 30000. Also a query parameter of /wait. |
client_message_id | Optional idempotency key, 1 to 128 characters from A-Z a-z 0-9 . _ : -. Retrying with the same key doesn’t send the message again. |
A message needs non-blank text, at least one file id, or both. POST /v1/threads
always picks the thread id. With a client_message_id the id is derived from that key,
so a retried create lands on the same thread and message. Without a key, every create
starts a new thread.
Send and wait responses
# 200: the agent finished
{"status": "completed", "thread_id": "…", "message_id": "…", "text": "…", "messages": […]}
# 202: the agent is still working ("pending") or has gone quiet ("stalled")
{"status": "pending", "thread_id": "…", "message_id": "…"}message_idis your message. Pass it asafterto wait again.messagesholds the agent’s messages written afterafter, oldest first.textis the last text part of the final agent message, which is the answer.pendingmeanswait_msran out.stalledmeans the thread is still marked running, but the agent has sent no update in the 5 minutes since the latest send, or in the last 30 minutes. Both can still finish, so you can keep waiting.
Other responses: the list returns {"threads": [{"thread_id", "name", "state", "created_at", "updated_at"}]}
with state set to idle or running and times in epoch milliseconds. GET …/messages returns
{"thread_id", "messages": […]}, and abort returns {"thread_id", "state": "idle"}.
Resume a send
BASE=http://127.0.0.1:8787 # bee proxy
# Send without blocking. If this request fails, retry it with the same client_message_id.
curl -s -X POST "$BASE/v1/threads" \
-H 'Content-Type: application/json' \
-d '{"text": "Summarize my week", "wait_ms": 0, "client_message_id": "week-summary-1"}'
# 202 {"status":"pending","thread_id":"T","message_id":"M"}
# Wait for the answer. Repeat while the status is "pending".
curl -s "$BASE/v1/threads/T/wait?after=M&wait_ms=60000"
# 200 {"status":"completed","thread_id":"T","message_id":"M","text":"…","messages":[…]}A wait holds the request open. Through bee proxy, keep wait_ms below
--idle-timeout (120 seconds by default), or raise that limit.
Attachments
# Optional fields (width, height, kind, filename, content_type) must come before the file part
curl -s -X POST "$BASE/v1/threads/attachments" \
-F width=640 -F height=480 \
-F file=@photo.jpg
# {"file_id":"F","kind":"image","filename":"photo.jpg","content_type":"image/jpeg","size_bytes":48213,"width":640,"height":480}Files can be up to 25 MB. kind (image, audio or file) comes from the content
type unless you set the kind field. width and height are only read when they come
before the file part. Pass the file_id in file_ids. Each upload can be attached to
one message.
Errors
| Status | error | When |
|---|---|---|
400 | text_or_files_required | Blank text and no file_ids. |
400 | invalid_file_ids | A file id is duplicated, unknown, not yours, or already attached. |
400 | message_not_in_thread | after isn’t a message of this thread. |
400 | No file uploaded, File too large (max 26214400 bytes), Uploaded file is empty | Upload problems. |
403 | test_user_required | The account isn’t a test user. |
403 | agent_vm_consent_required | Upload only: you haven’t granted the agent consent. |
404 | thread_not_found | The thread doesn’t exist or belongs to someone else. |
503 | agent_unavailable | The agent can’t be reached (send, abort). Nothing was sent. |
Requests that fail schema validation, for example a wait_ms above 120000, also return 400.