For people and agents
External Agent API
Let something else read your travels, and add to them.
Dear Travel is a travel record with an agent living inside it. This API lets an agent running somewhere else — on your own machine, in a chat app, anywhere that can make an HTTPS request — read that record, add to it, and talk to the agent that keeps it.
If you are an agent, start with these
/llms.txt— this whole contract as plain text, in about a tenth the bytes./api/agent/external/openapi— the same thing as JSON, and the only place with the full JSON-schema parameters for every tool. The tables further down name the tools and what they need; this gives you their arguments.
Neither needs a key. Both are generated from the constants the gateway enforces, so they cannot drift from it.
Three rules
Read these first. They are not configuration, and no key changes them.
- The agent’s mind is read-only from outside. Dear Travel’s agent keeps a mind — a graph of what it has concluded about the traveller. You can read it. Nothing outside Dear Travel can write it, and there is no endpoint that would. The reason is mechanical: the whole mind is loaded into the agent’s prompt every turn, so anything written from outside would come back to the agent as its own recollection, indistinguishable from what it worked out for itself. If you have something the mind should hold, say so through
/chatand let the agent decide. - Nothing deletes. The tool surface is adds and edits. There is no delete tool, and no permission that could authorize one.
- Nothing reaches settings. No key can change the traveller’s preferences, their account, or issue another key.
Getting a key
Each traveller issues their own keys, in Settings → External agent. A key acts as exactly one account — the account of whoever issued it — so there is no way to issue one on someone else’s behalf, and no way for a key to issue another key.
The key is shown once. Dear Travel stores only a hash of it, so a lost key is re-issued, never recovered. It can be renamed and re-scoped at any time, and turned off immediately — that is why it is an opaque random string looked up in a table rather than a signed token that would stay valid until it expired.
Authorization: Bearer dt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxPermissions
A key carries a set of permissions. They only ever subtract: a key acts as one account and can never reach outside it.
| Permission | Grants |
|---|---|
travel:read | Read trips, plans, packing lists and the wishlist. |
travel:write | Create and update trips, packing lists, items and journal entries. Never delete. |
wallet:read | Read the travel wallet — passports, visas, trusted-traveler and elite status, card names (never numbers). |
mind:read | Read the agent's mind — the rendered index, and one page in full. Read only. |
files:write | Send files along with a write, to be filed onto what it creates. |
agent:chat | Hold a conversation with the in-app agent and act on the traveller’s behalf. |
There is no write counterpart to mind:read, and no delete permission at all. Those absences are the two rules above, expressed in the vocabulary.
Endpoints
Every tool you may call, its JSON-schema parameters, the permission it needs, and whether your key holds it. Tools you cannot call are listed too — an agent that cannot see what it is missing has no way to ask its owner for the right permission.
Body { tool, args, attachments?, correlationId?, ownerConfirmed? }. Returns 200 even when the tool refuses — a refused write is an answered request, and ok: false with a sentence in result is the answer. A 404 means the tool is not in the catalog; a 403 names the permission you are missing.
The rendered index the in-app agent reasons over each turn — root note, then every page grouped by type. Open one page in full with the read_memory tool. There is no POST here, by design.
Body { message, conversationId? }. Pass back the conversationId to continue a thread. Proposed writes come back pending unless the traveller marked the key trusted. This is also the only road by which something you learned outside can reach the mind: you tell the agent, and the agent decides what to remember.
The same contract as this page, as JSON, generated from the same constants the gateway enforces. Point an agent at it before it has a key.
What an agent can reach
6 reads and 5 adds or edits. This table is generated from the allowlist the gateway actually enforces.
| Read | Needs | What it does |
|---|---|---|
list_trips | travel:read | List the traveller's trips with dates, location and plan counts. Use scope to focus: 'upcoming' (active/future), 'past', or 'all'. |
get_trip | travel:read | Get one trip in detail: its plans (flights, lodging, activities, …) and packing lists. Pass the trip id (with or without the trip: prefix). |
list_packing | travel:read | List the traveller's packing lists with packed/total counts. |
list_wishlist | travel:read | List the traveller's saved Wishlist — places AND activities they want to do, including bucket-list items, each with status and any target timing. Use for "what's on my wishlist", planning ideas, or matching a trip to something they wanted to do. |
list_wallet | wallet:read | List the traveller's travel wallet — passports, residency/visa status, Global Entry / trusted-traveler programs, airline & hotel elite status, the credit cards they carry (names only, never numbers), lounge access, insurance. Use for visa/entry, lounge, best-card, alliance-perk, and document-validity questions. |
read_memory | mind:read | Read one page of the agent's mind in full by node_id, plus its directly-connected pages. Ids come from the index at GET /api/agent/external/mind. Read only — nothing outside Dear Travel can write the mind. |
| Add or edit | Needs | What it does |
|---|---|---|
create_tripaccepts files | travel:write | Create a new trip for the traveller. Use when they ask you to start/plan a trip. Dates are optional (they fill in as plans land). For the destination pass a plain city name in `city` (e.g. 'Lisbon'); the server resolves it. This WRITES immediately when called on this endpoint — there is no approval step here, so confirm with the traveller yourself before calling it. |
update_tripaccepts files | travel:write | Edit an existing trip (rename, change dates, destination, or type). Pass `trip_id` and only the fields to change. To clear a date pass it as null. This WRITES immediately when called on this endpoint — there is no approval step here, so confirm with the traveller yourself before calling it. |
create_packing_list | travel:write | Create a new packing list, optionally pre-filled with items and linked to a trip. Use when the traveller asks you to make them a list. (For an AI-generated list from a trip's specifics, the traveller can also use the trip's “Create Packing List” button.) This WRITES immediately when called on this endpoint — there is no approval step here, so confirm with the traveller yourself before calling it. |
add_packing_item | travel:write | Add one item to an existing packing list. Pass `list_id` and the item `label`. Use `list_packing` first to find the list id. This WRITES immediately when called on this endpoint — there is no approval step here, so confirm with the traveller yourself before calling it. |
create_journal_entryaccepts files | travel:write | Write a journal entry on the traveller's behalf — a note, reflection, or record of something that happened. Optionally pin it to a trip. This WRITES immediately when called on this endpoint — there is no approval step here, so confirm with the traveller yourself before calling it. |
Notice what is absent. No delete tool, and none of the mind’s write tools. The catalog is an allowlist, so a tool added inside Dear Travel stays unreachable from outside until someone puts it on the list on purpose.
Sending files
Understanding a document and storing it are different questions, and the answers differ. You almost certainly have your own model — extract the fields where you are, rather than relaying bytes to Dear Travel so it can spend the traveller’s AI budget re-reading what you already read. But a summary is not a receipt, so the bytes still have to land.
So it is one call: send the structured fields and the file. The file is filed onto whatever that call creates — a photo onto the trip, a scan onto the journal entry. Up to 5 files per call, 10 MB each, base64-encoded. Images, video, audio and PDF; SVG is refused. Sending files needs files:write on top of the tool’s own permission — creating a journal entry and putting a scanned visa in the traveller’s media are different grants.
POST /api/agent/external/tools
Authorization: Bearer dt_…
Content-Type: application/json
{
"tool": "create_journal_entry",
"args": {
"body": "Landed in Lisbon. The taxi queue was 40 minutes.",
"trip_id": "…",
"entry_type": "note"
},
"attachments": [
{
"filename": "boarding-pass.pdf",
"mimeType": "application/pdf",
"data": "JVBERi0xLjQK…",
"caption": "TP1234 LIS"
}
]
}A filing miss never fails the write, and is never silent either. If the entry saved but the file did not, result says so — you can retry the file alone.
A worked example
Read the catalog, then read the traveller’s upcoming trips.
curl -s https://dear.travel/api/agent/external/tools \
-H "Authorization: Bearer $DEAR_TRAVEL_KEY"
curl -s https://dear.travel/api/agent/external/tools \
-H "Authorization: Bearer $DEAR_TRAVEL_KEY" \
-H "Content-Type: application/json" \
-d '{"tool":"list_trips","args":{"scope":"upcoming"}}'Add a trip, then ask the agent about it in its own words.
curl -s https://dear.travel/api/agent/external/tools \
-H "Authorization: Bearer $DEAR_TRAVEL_KEY" \
-H "Content-Type: application/json" \
-d '{"tool":"create_trip","args":{"name":"Lisbon in spring","city":"Lisbon","start_date":"2027-04-02","end_date":"2027-04-09"}}'
curl -s https://dear.travel/api/agent/external/chat \
-H "Authorization: Bearer $DEAR_TRAVEL_KEY" \
-H "Content-Type: application/json" \
-d '{"message":"What should I pack for Lisbon in April?"}'Rate limits and errors
- 60 calls per minute per key, and a separate hourly ceiling on
/chatbecause that one spends the account’s AI budget. Over the line is a429withRetry-After. 401— the key is missing, malformed, unknown, turned off or expired. One answer for all of them on purpose: which of your guesses was close is not something an unauthenticated caller has earned.403— authenticated, but the key lacks a permission. The response names which one, so the fix is “ask forfiles:write” rather than a bare refusal.404on a tool call — that tool is not in the catalog. It may exist inside Dear Travel; it is not reachable from outside.200withok: false— the tool ran and declined, or failed. Relayresult. Do not retry: a permission decision will not change on the second attempt.
Want a capability that is not here? The tool surface grows deliberately — each entry is a decision, not an export. Machine-readable spec: /api/agent/external/openapi.