# Tavern API

A session journal, a campaign wiki and a map that fill themselves in from the notes your table already takes. Free for your whole table, any game, any system.

117 operations, the same ones over REST at `https://gettavern.app/api/v1` and as
tools on the MCP server at `https://gettavern.app/mcp`. The machine-readable description is at
[`/openapi.json`](https://gettavern.app/openapi.json); the catalog that lists both is at
[`/.well-known/api-catalog`](https://gettavern.app/.well-known/api-catalog). The page for people is
[/developers](https://gettavern.app/developers).

## The one idea

Everything authorised in this app is a **seat** at a **campaign**.

- An **account** is a person, across every table they sit at.
- A **campaign** is a table. Its `slug` is what a url says, and every call about one names it.
- A **seat** is one account at one campaign: its name there, its colour, its character, whether it is the GM's.

A credential is an account. It reaches the campaigns that account has a seat at, with that seat,
and nothing more: a player's key can't do what only the GM may, and nobody's key opens a table
they don't sit at.

## Getting a credential

See [auth.md](https://gettavern.app/auth.md). Two ways:

- **An API key**, for your own scripts. Make one in Settings, under API and assistants. It is shown once.
- **OAuth**, for an app acting as you (an assistant, an MCP client). The app sends you to a Tavern
  page that says who it is and what it wants, and you say yes or no.

Send either as `Authorization: Bearer <secret>`. Scopes stack: `read`, then `write`, then `delete`.

## Calling it

- Bodies are JSON, and so are answers.
- A session is addressed by its play date (`YYYY-MM-DD`); wiki pages and characters by their slug.
- In notes and wiki pages, `[[Name]]` links the wiki page of that name and files the mention on it.
- Every picture field takes a web address. Uploading files happens in the app.

## Failures

Every failure answers `{ "error": "…", "code": "…" }`: one sentence in the app's own voice, fit to
show a person, and a stable word for the kind of failure. The statuses are the ordinary ones: `400`
malformed, `401` no credential or a dead one, `403` a scope or a seat that may not, `404` no such
thing (or none this seat may see), `409` already there, `429` slow down.

## Limits

A credential may make about 120 calls a minute, 30 of them changes. Past that it gets `429` with
`Retry-After`. The OAuth endpoints are limited per caller address.

## What is never here

- **Somebody else's private notes.** A note marked Private is sealed to whoever wrote it. Your own
  come back to you; nobody else's are served by any route.
- **Game content.** Tavern ships no rules, no spells, no monsters, no items and no lore, and this
  API supplies none. The one outside reference the app can read from stays a GM's act in the app.
- **Anybody else's table.** A credential reaches the campaigns its account sits at, and nothing else.
- **The back office, and mail.** No key ever carries the operator's powers, and the API sends no
  email: a new seat comes back with its own link to pass on however you like.

<a id="rest"></a>

## The REST API

Every operation, by group. Each one's `Tool` is its name on the MCP server.

<a id="account"></a>

### You

Who the credential is, your public profile, your invitations and feedback.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `GET /api/v1/me` | `whoami` | read | The account this credential acts as, which credential it is, and what it may do. |
| `GET /api/v1/campaigns` | `list_campaigns` | read | Every campaign you own or hold a seat at, and the invitations waiting for an answer. |
| `POST /api/v1/invitations/{seat}/{answer}` | `respond_to_invitation` | write | Accept or decline a seat somebody offered you. The seat id is on `list_campaigns`' invitations. |
| `GET /api/v1/me/profile` | `get_profile_settings` | read | Your public profile's handle and which parts of it are shown. |
| `PATCH /api/v1/me/profile` | `update_profile_settings` | write | Rename your handle, or show and hide parts of your public profile. Fields left out stay as they are. |
| `GET /api/v1/me/subscription` | `get_subscription` | read | Whether you subscribe to Lorekeeper, from when and until when. |
| `GET /api/v1/profiles/{handle}` | `get_public_profile` | read | Somebody's public page: the tables they sit at and run, and the shape of what they have written. Names and counts only. |
| `POST /api/v1/feedback` | `send_feedback` | write | Tell the people who make Tavern about a bug, an idea or anything else. The answer comes back on `list_my_feedback`. |
| `GET /api/v1/feedback` | `list_my_feedback` | read | Everything you have sent, each with its status and any reply. |

<a id="campaigns"></a>

### Campaigns and seats

Make a table, fill its seats, change its look, leave one.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `POST /api/v1/campaigns` | `create_campaign` | write | Make a new table you own. You are its GM unless you name somebody else. Seats for players are made unclaimed, and nobody is emailed: each seat comes back with its own join link to send however you like. |
| `GET /api/v1/campaigns/{campaign}` | `get_campaign` | read | One campaign: its name, look, sort of game, whether Lorekeeper sits at it, and its seats. |
| `PATCH /api/v1/campaigns/{campaign}` | `update_campaign_look` | write | Set the table's accent colour and its mark. Null puts the default back. GM only. |
| `GET /api/v1/campaigns/{campaign}/seats` | `list_seats` | read | Everybody at the table: name, colour, whether they are the GM, whether they have taken their seat, and which wiki page is their character. Addresses only for your own seat, or every seat if you own the campaign. |
| `POST /api/v1/campaigns/{campaign}/seats` | `add_seat` | write | Offer somebody a seat by name and address. Nobody is emailed: the answer carries the seat's own join link. Campaign owner only. |
| `DELETE /api/v1/campaigns/{campaign}/seats/{seat}` | `remove_seat` | delete | Take a seat away from the table. Campaign owner only. |
| `GET /api/v1/campaigns/{campaign}/me` | `get_my_seat` | read | Your own seat at this table: your name there, your colour, your face, and the character you play. |
| `PATCH /api/v1/campaigns/{campaign}/me` | `update_my_seat` | write | Your name, colour and picture at this table. The GM's colour is gold and stays gold. |
| `POST /api/v1/campaigns/{campaign}/leave` | `leave_campaign` | delete | Give up your seat at a table you don't own. The owner can offer it to somebody else. |

<a id="sessions"></a>

### Session journal

Play dates and the notes everybody writes on them.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `GET /api/v1/campaigns/{campaign}/sessions` | `list_sessions` | read | Every play date, newest first, with its title, its number, how many notes it has and its recap. |
| `GET /api/v1/campaigns/{campaign}/sessions/{date}` | `get_session` | read | One play date and every shared note on it, in the order they were written. Notes are in the journal's markdown, with [[wiki links]]. |
| `POST /api/v1/campaigns/{campaign}/sessions` | `create_session` | write | Put a play date in the journal, with a title if it has one. Logging the same date twice is the same session. GM only. |
| `PATCH /api/v1/campaigns/{campaign}/sessions/{date}` | `update_session` | write | Change a session's title, or the day it was played. Moving the day takes its notes with it. GM only. |
| `POST /api/v1/campaigns/{campaign}/sessions/{date}/notes` | `add_session_note` | write | Write a shared note on a play date. Wrap a name in [[double brackets]] to link it to its wiki page (a page is made if none exists). The GM's note on a new date logs the session too; a player can only write on a date the GM has logged. |
| `PUT /api/v1/campaigns/{campaign}/notes/{note}` | `update_session_note` | write | Rewrite a shared note. Your own, or any if you are the GM. Every version is kept. |
| `DELETE /api/v1/campaigns/{campaign}/notes/{note}` | `delete_session_note` | delete | Delete a shared note and its history. Your own, or any if you are the GM. |

<a id="wiki"></a>

### Campaign wiki

Pages for every person, place and thing, their history, and search across the lot.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `GET /api/v1/campaigns/{campaign}/wiki` | `list_wiki_pages` | read | Every page in the campaign's wiki, by name, with its type, a short excerpt and how often it is mentioned. |
| `GET /api/v1/campaigns/{campaign}/wiki/{page}` | `get_wiki_page` | read | One page: its name, type, body, pictures, the seat whose character it is, and everywhere it is mentioned. |
| `GET /api/v1/campaigns/{campaign}/wiki/{page}/history` | `get_wiki_page_history` | read | Every saved version of a page, newest first, with who saved it. |
| `POST /api/v1/campaigns/{campaign}/wiki` | `create_wiki_page` | write | Start a page for a person, place, thing, faction or thread. A page with the same name already there is returned rather than doubled. |
| `PATCH /api/v1/campaigns/{campaign}/wiki/{page}` | `update_wiki_page` | write | Change a page's name, type, text or pictures. Fields left out stay as they are; the slug never moves on a rename. Every version is kept. |
| `DELETE /api/v1/campaigns/{campaign}/wiki/{page}` | `delete_wiki_page` | delete | Delete a page. A page somebody's seat is linked to as their character is refused until the link comes off. GM only. |
| `PUT /api/v1/campaigns/{campaign}/wiki/{page}/seat` | `link_wiki_page_to_seat` | write | Tie a wiki page to the seat whose character it is, so every mention wears their face. Null unties it. A player links their own; the GM may link anybody's. |
| `GET /api/v1/campaigns/{campaign}/search` | `search_campaign` | read | Find words across the shared notes, the wiki and the shared map pins. Private notes are never searched here. |

<a id="notes"></a>

### My Notes

Your own sealed notes, and everything you have written at a table.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `GET /api/v1/campaigns/{campaign}/my-notes` | `list_my_notes` | read | Your own sealed notes at this table: scratch notes, and the ones filed on a play date or a wiki page. Nobody else can read these, the GM included. |
| `POST /api/v1/campaigns/{campaign}/my-notes` | `create_my_note` | write | A note sealed to you. File it on a play date or a wiki page, or leave it as scratch. |
| `PUT /api/v1/campaigns/{campaign}/my-notes/{note}` | `update_my_note` | write | Rewrite one of your sealed notes, or refile it. Every version is kept. |
| `DELETE /api/v1/campaigns/{campaign}/my-notes/{note}` | `delete_my_note` | delete | Delete one of your sealed notes and every version of it. |
| `GET /api/v1/campaigns/{campaign}/my-notes/{note}/history` | `get_my_note_history` | read | Every version of one of your sealed notes. |
| `GET /api/v1/campaigns/{campaign}/my-writing` | `list_everything_i_wrote` | read | The shared half of My Notes: the session notes you wrote, the pins you dropped for everyone, and every wiki version you saved. |

<a id="messages"></a>

### Messages

Direct conversations between the people at one table, and the GM's thread to all of them.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `GET /api/v1/campaigns/{campaign}/conversations` | `list_conversations` | read | Every conversation you are in at this table: the Game Master thread first, which every seat has, then your direct conversations, most recent first, each with who is in it, its last line and how many messages you haven't read. Nothing here reaches another table. |
| `POST /api/v1/campaigns/{campaign}/conversations` | `start_conversation` | write | Open a direct conversation with one or more people seated at this table (their seat ids, from the campaign's seats). Writing to the same people again finds the conversation you already share. Only the people in it can read it, the GM included. |
| `GET /api/v1/campaigns/{campaign}/conversations/{conversation}` | `read_conversation` | read | A conversation's messages, oldest first, written in the journal's dialect (`[[Name]]` links a wiki page). `table` names the Game Master thread. Page back with `before`; ask for only what is new with `after`. |
| `POST /api/v1/campaigns/{campaign}/conversations/{conversation}/messages` | `send_message` | write | Write in a conversation. Anybody in a direct conversation may; only the GM posts in the Game Master thread (`table`). `[[Name]]` links a wiki page. Everybody else in it sees it in their Messages; nothing is emailed and no notification is written. |
| `POST /api/v1/campaigns/{campaign}/conversations/{conversation}/read` | `mark_conversation_read` | write | Caught up on one conversation: its unread count goes to nothing. |
| `DELETE /api/v1/campaigns/{campaign}/messages/{message}` | `delete_message` | delete | Take back a message you wrote. It's gone for good, for everybody in the conversation. |

<a id="map"></a>

### Map

The table's maps and the pins on them, shared and private.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `GET /api/v1/campaigns/{campaign}/maps` | `list_maps` | read | The campaign's maps in tab order: the id each pin is placed against, its name, its size in pixels and how many pins it has. |
| `GET /api/v1/campaigns/{campaign}/maps/{map}/pins` | `list_pins` | read | The shared pins on one map, plus your own private ones. Places carry the wiki page they are. |
| `GET /api/v1/campaigns/{campaign}/my-pins` | `list_my_private_pins` | read | Your private pins on every map at this table. |
| `POST /api/v1/campaigns/{campaign}/maps/{map}/pins` | `create_pin` | write | Put a pin on a map. Coordinates are fractions of the map's width and height, from the top left. A pin carrying `entitySlug` is that page's place on the map (GM only). |
| `PATCH /api/v1/campaigns/{campaign}/pins/{pin}` | `update_pin` | write | Move a pin or change what it says. Fields left out stay as they are. |
| `DELETE /api/v1/campaigns/{campaign}/pins/{pin}` | `delete_pin` | delete | Take a pin off the map. Your own, or any shared one if you are the GM. |
| `PATCH /api/v1/campaigns/{campaign}/maps/{map}` | `update_map` | write | Rename a map the GM added, or move it one place along the tabs (`move`: -1 or 1). GM only. |
| `DELETE /api/v1/campaigns/{campaign}/maps/{map}` | `delete_map` | delete | Remove a map the GM added, with every pin on it (private ones included). Gone for good. GM only. |

<a id="characters"></a>

### Characters

Sheets, hit points, conditions, spell slots, items, homebrew and companions.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `GET /api/v1/campaigns/{campaign}/characters` | `list_characters` | read | Every character at the table by name, with who holds each, their level line and whether they have fallen. |
| `GET /api/v1/campaigns/{campaign}/characters/{character}` | `get_character` | read | A whole character: the sheet, what has been spent and taken (hit points, slots, conditions), homebrew, custom items and companions. Yours, or anybody's if you are the GM. |
| `GET /api/v1/campaigns/{campaign}/party` | `get_party` | read | Every standing character on the Master HUD, live numbers in one call. Characters the GM took off it (`set_character_on_hud`) are left out. GM only. |
| `PATCH /api/v1/campaigns/{campaign}/characters/{character}/state` | `update_character_state` | write | The changes a fight makes, in one call: damage (temporary hit points go first), healing (never past the maximum), temporary hit points, conditions on and off, an exhaustion level, spell slots spent or restored, and death saves. Everything else on the sheet is left alone. |
| `PUT /api/v1/campaigns/{campaign}/characters/{character}/overlay` | `set_character_overlay` | write | Replace the whole record of what this character has spent and taken: hit points, slots, uses, hit dice, conditions, death saves, equip and attune toggles, counts. A full replace, checked against the sheet. Prefer `update_character_state` for a single change. |
| `POST /api/v1/campaigns/{campaign}/characters` | `add_character_from_beyond` | write | Import a public character sheet from an online character builder into this table, by its link or its number. One way: nothing is written back to the sheet's site. |
| `POST /api/v1/campaigns/{campaign}/characters/{character}/sync` | `sync_character` | write | Re-read an imported character's sheet from its source. Resets what has been spent to what the sheet reports, except the parts named in `keep`. Homebrew, custom items, companions and layout are never touched. |
| `PUT /api/v1/campaigns/{campaign}/characters/{character}/hud` | `set_character_on_hud` | write | Take a character off the GM's Master HUD (a one-shot's pregen, somebody away for a while) or put them back. Nothing else changes: the sheet, who holds it and the journal stay as they are. `list_characters` says who is off as `offHud`. GM only. |
| `POST /api/v1/campaigns/{campaign}/characters/{character}/play` | `play_character` | write | Switch which of your characters is the one the table sees. The character's own player. |
| `POST /api/v1/campaigns/{campaign}/characters/{character}/death` | `mark_character_fallen` | write | A death is a stamp, not a deletion: the sheet stays readable. Name a successor (another of the same seat's living characters) and what goes with them. |
| `DELETE /api/v1/campaigns/{campaign}/characters/{character}/death` | `revive_character` | write | Take the fallen stamp off. Nothing that moved to a successor moves back. |
| `DELETE /api/v1/campaigns/{campaign}/characters/{character}` | `delete_character` | delete | Delete a character and everything made for them here. Gone for good. |
| `POST /api/v1/campaigns/{campaign}/characters/{character}/assign` | `assign_character` | write | Give a character made here to a player's seat. From then on it is theirs to edit and play. GM only. |
| `PUT /api/v1/campaigns/{campaign}/characters/{character}/portrait` | `set_character_portrait` | write | Wear a picture of your own, or switch back to the one from the imported sheet. Both are kept whichever is worn. |
| `POST /api/v1/campaigns/{campaign}/characters/{character}/items` | `add_custom_item` | write | Put something in the character's Inventory that the sheet doesn't have. A sync never touches it. |
| `PUT /api/v1/campaigns/{campaign}/characters/{character}/items/{item}` | `update_custom_item` | write | Replace a custom item's details. |
| `DELETE /api/v1/campaigns/{campaign}/characters/{character}/items/{item}` | `delete_custom_item` | delete | Take a custom item out of the Inventory. |
| `POST /api/v1/campaigns/{campaign}/characters/{character}/homebrew` | `add_homebrew_box` | write | Add a card to the character's Actions: an ability, a trick, a boon. A sync never touches it. |
| `PUT /api/v1/campaigns/{campaign}/characters/{character}/homebrew/{box}` | `update_homebrew_box` | write | Replace a homebrew card's details. |
| `DELETE /api/v1/campaigns/{campaign}/characters/{character}/homebrew/{box}` | `delete_homebrew_box` | delete | Take a homebrew card off the Actions tab. |
| `POST /api/v1/campaigns/{campaign}/characters/{character}/companions` | `add_companion` | write | Give the character a companion with a stat block of its own. One animal companion at a time. |
| `PUT /api/v1/campaigns/{campaign}/characters/{character}/companions/{companion}` | `update_companion` | write | Replace a companion's stat block. |
| `PUT /api/v1/campaigns/{campaign}/characters/{character}/companions/{companion}/state` | `set_companion_state` | write | Set a companion's hit points and the uses spent on its cards. |
| `DELETE /api/v1/campaigns/{campaign}/characters/{character}/companions/{companion}` | `delete_companion` | delete | Part ways with a companion. |

<a id="builder"></a>

### Character builder

Make a character from scratch, or design the sheet itself.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `POST /api/v1/campaigns/{campaign}/characters/custom` | `create_custom_character` | write | Build a character from your own answers in one call: the guided creator's questions, sent at once. Labels are yours (species, class and the rest are free text); the app works out the arithmetic. A player's new character becomes theirs and their primary. |
| `PUT /api/v1/campaigns/{campaign}/characters/{character}/sheet` | `update_custom_sheet` | write | Rewrite a custom character's sheet from the creator's answers. Imported sheets are refused: change those where they came from and sync. |
| `GET /api/v1/campaigns/{campaign}/drafts` | `list_character_drafts` | read | Characters you started in the creator and haven't finished. |
| `POST /api/v1/campaigns/{campaign}/drafts` | `start_character_draft` | write | Open the creator: `guided` for the usual questions, `blank` to design the sheet itself, optionally from a frame the table keeps. |
| `GET /api/v1/campaigns/{campaign}/drafts/{draft}` | `get_character_draft` | read | One draft and the question it is on. |
| `PUT /api/v1/campaigns/{campaign}/drafts/{draft}` | `update_character_draft` | write | Save the whole draft. Every save carries every answer. |
| `POST /api/v1/campaigns/{campaign}/drafts/{draft}/finish` | `finish_character_draft` | write | Turn a draft into a character. It must have a name. |
| `DELETE /api/v1/campaigns/{campaign}/drafts/{draft}` | `delete_character_draft` | delete | Delete a half-made character. |
| `GET /api/v1/campaigns/{campaign}/frames` | `list_sheet_frames` | read | Sheet shapes designed with Blank Slate and kept at this table for the next player. |
| `POST /api/v1/campaigns/{campaign}/frames` | `save_sheet_frame` | write | Save a sheet shape under a name. Saving over the same name corrects it. |
| `DELETE /api/v1/campaigns/{campaign}/frames/{frame}` | `delete_sheet_frame` | delete | Remove a kept frame. Characters built on it keep their own copy. |

<a id="combat"></a>

### Combat Tracker

Run a battle: the roster, initiative, turns, health and the summary.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `GET /api/v1/campaigns/{campaign}/combat/roster` | `get_combat_roster` | read | Every seat at the table (with their character's numbers where they have one) and every sheet nobody holds. GM only. |
| `GET /api/v1/campaigns/{campaign}/combat/battles` | `list_battles` | read | Every battle at the table, newest first: drafts, the one under way, and the ended. GM only. |
| `POST /api/v1/campaigns/{campaign}/combat/battles` | `create_battle` | write | Start a battle's roster. Nothing happens until it is started. GM only. |
| `GET /api/v1/campaigns/{campaign}/combat/battles/{battle}` | `get_battle` | read | The order, whose turn it is, the round, everybody's live health, and the log. GM only. |
| `PATCH /api/v1/campaigns/{campaign}/combat/battles/{battle}` | `update_battle` | write | Change the battle's name, its round, or whose turn it is. GM only. |
| `POST /api/v1/campaigns/{campaign}/combat/battles/{battle}/participants` | `add_participant` | write | Add a character at this table (`player` or `companion`, by slug, with live health), a seat with no sheet (`seat`), or anybody you name on the spot (`custom`, or `creature` with a stat block you supply). GM only. |
| `PATCH /api/v1/campaigns/{campaign}/combat/battles/{battle}/participants/{participant}` | `update_participant` | write | Their name, side, initiative, stat block or state. Health for a character at this table lands on their own sheet. GM only. |
| `POST /api/v1/campaigns/{campaign}/combat/battles/{battle}/participants/{participant}/health` | `change_participant_health` | write | Take damage (temporary hit points first) or heal (never past the maximum), and it goes in the log. GM only. |
| `DELETE /api/v1/campaigns/{campaign}/combat/battles/{battle}/participants/{participant}` | `remove_participant` | delete | Remove them from the order altogether. GM only. |
| `POST /api/v1/campaigns/{campaign}/combat/battles/{battle}/start` | `start_battle` | write | The roster becomes the order: round one, first in initiative goes. GM only. |
| `POST /api/v1/campaigns/{campaign}/combat/battles/{battle}/next-turn` | `next_turn` | write | Move to the next one in the order, stepping over anybody out of the fight, into a new round when it comes round. GM only. |
| `POST /api/v1/campaigns/{campaign}/combat/battles/{battle}/end` | `end_battle` | write | Call it. The battle becomes its summary and never moves again. GM only. |
| `DELETE /api/v1/campaigns/{campaign}/combat/battles/{battle}` | `delete_battle` | delete | Delete a battle and its log. Characters' health stays on their sheets. GM only. |

<a id="lorekeeper"></a>

### Lorekeeper

Hand a session's notes to Lorekeeper and watch the write-up land.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `GET /api/v1/campaigns/{campaign}/lorekeeper/jobs` | `list_lorekeeper_jobs` | read | Session notes handed to Lorekeeper at this table, and how far each write-up has got. GM only. |
| `POST /api/v1/campaigns/{campaign}/lorekeeper/jobs` | `queue_session_for_lorekeeper` | write | Give Lorekeeper a session's notes as you wrote them. It logs the session, links every name, writes new wiki pages, updates the ones the session touched, pins the map and writes the recap. Needs Lorekeeper at the table. GM only. |
| `DELETE /api/v1/campaigns/{campaign}/lorekeeper/jobs/{job}` | `cancel_lorekeeper_job` | delete | Withdraw a job that hasn't started. GM only. |

<a id="memorial"></a>

### Memorial Pages

Close the book on a campaign and read its memorial.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `GET /api/v1/campaigns/{campaign}/memorial` | `get_memorial` | read | A completed campaign's memorial: the numbers as they stood the night the book closed, the party, the album, and Lorekeeper's write-up where there is one. |
| `POST /api/v1/campaigns/{campaign}/complete` | `complete_campaign` | write | Close the book. Asks for the campaign's own name typed back as `confirm`, exactly as the app does. Undoable with `reopen_campaign`. GM only. |
| `DELETE /api/v1/campaigns/{campaign}/complete` | `reopen_campaign` | write | Take the completion off. Completing again puts it back. GM only. |
| `PATCH /api/v1/campaigns/{campaign}/memorial` | `set_memorial_public` | write | Make the memorial page public (anybody with the link, and search engines) or the table's alone again. GM only. |

<a id="dice"></a>

### Dice

Roll any dice on the server, and choose how your dice look in the app.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `GET /api/v1/dice/roll` | `roll_dice` | read | Roll any dice in ordinary notation, thrown on the server with crypto-strength randomness: `2d6+3`, `d20`, `d%`, `4d6kh3` (keep the highest three), `2d20kl1` (keep the lowest). Up to 200 dice and 12 terms. Nothing is saved and nobody else sees it. |
| `GET /api/v1/me/dice-style` | `get_dice_style` | read | How your dice look in the app's dice bubble and roll results, on every device you sign in on. |
| `PUT /api/v1/me/dice-style` | `set_dice_style` | write | Choose how your dice look: `tavern` (polished resin in the table's colour), `bone` (old ivory, the numbers cut in by hand), `obsidian` (black glass, flecked with gold), `frost` (clear ice with cold light inside, Lorekeeper), `ember` (coals still burning under the crust, Lorekeeper), `verdigris` (copper gone green, bright at the edges, Lorekeeper), `grove` (living wood, moss in the grain), `amethyst` (a cut violet stone with a light in it, Lorekeeper). The Lorekeeper styles need a Lorekeeper subscription and are refused without one. |

<a id="updates"></a>

### Notifications and progress

What's new at your tables, and how far you've come.

| Call | Tool | Scope | What it does |
| --- | --- | --- | --- |
| `GET /api/v1/me/notifications` | `list_notifications` | read | What has happened at your tables and what's new in Tavern, newest first, read and unread, with how many are unread of each kind. |
| `GET /api/v1/me/notifications/count` | `count_unread_notifications` | read | How many notifications are waiting. One number, cheap to ask. |
| `POST /api/v1/me/notifications/read` | `mark_notifications_read` | write | Mark some (`ids`), a kind's worth (`kinds`) or all (`all: true`) of your notifications read. With `unread: true` and `ids`, put them back to unread. |
| `DELETE /api/v1/me/notifications/{notification}` | `delete_notification` | delete | Take one notification off your list. |
| `GET /api/v1/me/progress` | `get_my_progress` | read | Your Tavern Level and title, how far it is to the next, where your experience came from, every achievement you have unlocked and each table's own set. Counted fresh when asked. |
| `GET /api/v1/achievements` | `list_achievements` | read | The whole catalog: every track, what its number counts, whether it is earned once or once per table, and each of its levels with the goal, the rarity and the experience it is worth. Read it beside your progress to say how far you are from the next one. |


<a id="mcp"></a>

## The MCP server

`https://gettavern.app/mcp`, Streamable HTTP, stateless, JSON responses. Point a client at it and it
starts OAuth on its own (the server answers `401` with a `WWW-Authenticate` header naming its
protected resource metadata); a client that takes a header can send an API key instead.

- Every operation above is a tool of the same name, and a credential only sees the tools its scopes allow.
- `?toolsets=wiki,sessions` narrows the list for a client that wants fewer. `core` is the journal,
  the wiki, the map and your notes; the groups' names are the ones above.
- Prompts: `recap_session`, `loose_ends`, `character_status`.
- What it is, readable without signing in: `/.well-known/mcp/server-card.json`.

## The app's own routes

The routes under `/api/accounts`, `/api/journal`, `/api/characters` and the rest are what the
app's screens call. They change whenever the screens do and are not a supported surface; build on
`/api/v1`.

## Status

[`/api/health`](https://gettavern.app/api/health) answers whether the app is up and whether it can
reach its database. No credential needed.

## Terms

Use of this API is use of the Service: the [Terms of Service](https://gettavern.app/terms) and the
[Privacy Policy](https://gettavern.app/privacy) apply. Questions to hello@gettavern.app.
