Pufy

Developer docs

Ask Pufy from your own code with a personal API key, or connect it to any app that speaks MCP.

Base URL

export PUFY_API=https://api.pufy.app

The examples on this page read the address from PUFY_API.

API keys

Keys are personal and part of Pufy+. A key asks Pufy as you, with your memory, notes, to-dos, web search and connected apps.

  1. In the Pufy app, open Settings, then API and MCP.
  2. Tap New key and name it after where you will use it.
  3. Copy the key. It starts with bear_ and is shown only once. Pufy keeps only a hash of it.

You can have up to 5 keys. Delete one in the same place to stop it at once. Send the key in the Authorization header:

Authorization: Bearer bear_...

POST /v1/ask

Asks Pufy one question and returns the whole answer as JSON when Pufy has finished. A full turn can take a while if Pufy searches or reads pages, so allow a long timeout (a minute or two).

Request body

FieldTypeMeaning
textstring, requiredWhat to ask or ask for. Up to 4,000 characters; longer text is cut.
conversationstring, optionalThe id of one of your agents' chats. Leave it out to use your default agent, Pufy. Group chats are not accepted.

Example

curl $PUFY_API/v1/ask \
  -H "Authorization: Bearer $PUFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": "What is on my to-do list today?"}'

Response

{
  "text": "You have 3 open to-dos today: ...",
  "cards": [],
  "conversationId": "..."
}
FieldMeaning
textPufy's reply.
cardsActions waiting for approval in the app, each {id, summary}. Empty when there are none.
conversationIdThe chat the question went to. Pass it back as conversation to keep using it.
errorOnly when the turn failed part way: {code, message}.

When Pufy wants to act, the reply says so and the card is listed:

{
  "text": "I've drafted the event. Approve it in the app.",
  "cards": [{ "id": "...", "summary": "Create event: Lunch with Ana, Fri 13:00" }],
  "conversationId": "..."
}

The question appears in your chat in the app, marked "(Asked through the API key "name")". Times are read in UTC, so give a time zone when it matters. Files and temporary chats are not available through the API.

GET /v1/cards/{id}

Follow up on a card from an answer: same key, the card's id. You get its summary and what became of it, never what the action returned. Cards from temporary chats and groups are not shown. If you poll, wait a minute or more between checks.

curl $PUFY_API/v1/cards/CARD_ID \
  -H "Authorization: Bearer $PUFY_API_KEY"

{ "id": "...", "summary": "Create event: Lunch with Ana, Fri 13:00", "status": "done" }

status is pending (waiting in the app), running, done, failed, declined or expired. An unknown id answers 404 with bear.api_card_not_found.

Errors and limits

Errors are JSON with a message and a code:

{ "message": "That API key is not valid.", "code": "bear.api_key_invalid" }
StatusCodeMeaning
401bear.api_key_invalidThe key is missing, wrong or deleted.
400bear.api_bodyThe body is not JSON with a non-empty text.
404bear.conversation_not_foundconversation is not one of your agents' chats.
403bear.agent_plus_requiredThe key is valid but its account does not have Pufy+. On /mcp it comes back as a JSON-RPC error (-32001).
429common.rate_limitedToo many requests. About 10 a minute for each key (a wrong key counts against its network address instead). Connecting an MCP client (initialize, notifications, tools/list) does not count. Wait the seconds in the Retry-After header, also in the body as retryAfter.
429bear.daily_limitYour daily turns are used up. API questions count like questions in the app; a refused question does not count. The body has resetAt (Unix seconds, the next midnight UTC) and limit.

MCP server

Pufy is an MCP server, so AI apps can ask it things. The address is the base URL followed by /mcp:

https://api.pufy.app/mcp

Clients that take a remote MCP address with headers

Add Pufy to your client's mcpServers settings, with the base URL in place of PUFY_API. The app's Copy the MCP settings button gives you this with the address and your key filled in.

{
  "mcpServers": {
    "pufy": {
      "url": "https://api.pufy.app/mcp",
      "headers": { "Authorization": "Bearer YOUR_PUFY_KEY" }
    }
  }
}

Claude Code

claude mcp add --transport http pufy $PUFY_API/mcp --header "Authorization: Bearer YOUR_PUFY_KEY"

Calling it by hand

curl $PUFY_API/mcp \
  -H "Authorization: Bearer $PUFY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/call",
       "params": {"name": "ask_bear", "arguments": {"text": "Summarise my notes tagged #trip"}}}'

Approvals

Through the API and MCP, Pufy reads, searches and answers on its own. Anything that sends, books or changes something becomes an approval card that waits in the Pufy app. Only you can approve it there. A key can never approve, decline or change a card, and it cannot create automations.

More for people using Pufy: Use Pufy from other apps.