Plug your campaign into anything.
Every part of Tavern is an API and an MCP server: the session journal, the wiki, the map, character sheets and the combat tracker. Connect an AI assistant in one step, or build your own tools on the same calls the app makes.
106 operations across 12 areas of the app, over REST and MCP.
Connect an AI assistant
The MCP server lives at https://gettavern.app/mcp. Add it to your assistant and it opens a Tavern page asking you to approve it: who is asking, what it may do, and where you'll be sent back to.
Claude
In Settings, under Connectors, add a custom connector and paste the server's address. Claude opens a Tavern page asking you to approve it.
ChatGPT
Add a connector with the server's address where your plan offers custom connectors, and approve it on the Tavern page that opens.
Claude Code
Add it from a terminal, then run /mcp inside a session to sign in.
claude mcp add --transport http tavern https://gettavern.app/mcp
Cursor
Add it to ~/.cursor/mcp.json, and Cursor asks you to sign in the first time it connects.
{
"mcpServers": {
"tavern": {
"url": "https://gettavern.app/mcp"
}
}
}
VS Code
Add it to .vscode/mcp.json in a workspace, or to your user settings.
{
"servers": {
"tavern": {
"type": "http",
"url": "https://gettavern.app/mcp"
}
}
}
Anything else
Any client that speaks Streamable HTTP. It finds the sign-in on its own; one that only takes a header can send an API key instead.
{
"url": "https://gettavern.app/mcp",
"headers": {
"Authorization": "Bearer tvn_your_key"
}
}
What people ask it
- Recap Thursday's session from everybody's notes. Reads the play date and every note on it, and writes a recap in the table's own names.
- Start a wiki page for the ferryman at Hollowmere and link him in tonight's notes. Makes the page, writes what the notes say about him, and adds the link.
- Ilsa takes 9 and she's prone. Takes it off her temporary hit points first, then her own, and puts the condition on her sheet.
- Which threads from the last three sessions have we dropped? Reads the sessions and the pages they mention, and lists what was left open.
- Set up a fight: the party against the three toll-bridge bandits. Starts a battle, adds the party's seats and the bandits, and waits for initiative.
- What has my character got left? Reads your sheet as it stands: hit points, spell slots, limited uses and what's equipped.
Everything it can do
You
Who the credential is, your public profile, your invitations and feedback.
whoami(GET /api/v1/me). Who am I: The account this credential acts as, which credential it is, and what it may do.list_campaigns(GET /api/v1/campaigns). List my campaigns: Every campaign you own or hold a seat at, and the invitations waiting for an answer.respond_to_invitation(POST /api/v1/invitations/{seat}/{answer}). Answer an invitation: Accept or decline a seat somebody offered you. The seat id is onlist_campaigns' invitations.get_profile_settings(GET /api/v1/me/profile). Get my profile settings: Your public profile's handle and which parts of it are shown.update_profile_settings(PATCH /api/v1/me/profile). Change my profile settings: Rename your handle, or show and hide parts of your public profile. Fields left out stay as they are.get_subscription(GET /api/v1/me/subscription). Get my subscription: Whether you subscribe to Lorekeeper, from when and until when.get_public_profile(GET /api/v1/profiles/{handle}). Read a public profile: Somebody's public page: the tables they sit at and run, and the shape of what they have written. Names and counts only.send_feedback(POST /api/v1/feedback). Send feedback: Tell the people who make Tavern about a bug, an idea or anything else. The answer comes back onlist_my_feedback.list_my_feedback(GET /api/v1/feedback). List my feedback: Everything you have sent, each with its status and any reply.
Campaigns and seats
Make a table, fill its seats, change its look, leave one.
create_campaign(POST /api/v1/campaigns). Create a campaign: 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_campaign(GET /api/v1/campaigns/{campaign}). Get a campaign: One campaign: its name, look, sort of game, whether Lorekeeper sits at it, and its seats.update_campaign_look(PATCH /api/v1/campaigns/{campaign}). Change a campaign's look: Set the table's accent colour and its mark. Null puts the default back. GM only. GM only.list_seats(GET /api/v1/campaigns/{campaign}/seats). List the seats: 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.add_seat(POST /api/v1/campaigns/{campaign}/seats). Add a seat: Offer somebody a seat by name and address. Nobody is emailed: the answer carries the seat's own join link. Owner only. Campaign owner only.remove_seat(DELETE /api/v1/campaigns/{campaign}/seats/{seat}). Remove a seat: Take a seat away from the table. Owner only. Campaign owner only.get_my_seat(GET /api/v1/campaigns/{campaign}/me). Get my seat: Your own seat at this table: your name there, your colour, your face, and the character you play.update_my_seat(PATCH /api/v1/campaigns/{campaign}/me). Change my seat: Your name, colour and picture at this table. The GM's colour is gold and stays gold.leave_campaign(POST /api/v1/campaigns/{campaign}/leave). Leave a campaign: Give up your seat at a table you don't own. The owner can offer it to somebody else.
Session journal
Play dates and the notes everybody writes on them.
list_sessions(GET /api/v1/campaigns/{campaign}/sessions). List sessions: Every play date, newest first, with its title, its number, how many notes it has and its recap.get_session(GET /api/v1/campaigns/{campaign}/sessions/{date}). Read a session: 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]].create_session(POST /api/v1/campaigns/{campaign}/sessions). Log a session: 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. GM only.update_session(PATCH /api/v1/campaigns/{campaign}/sessions/{date}). Rename or move a session: Change a session's title, or the day it was played. Moving the day takes its notes with it. GM only. GM only.add_session_note(POST /api/v1/campaigns/{campaign}/sessions/{date}/notes). Add a note to a session: 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.update_session_note(PUT /api/v1/campaigns/{campaign}/notes/{note}). Edit a session note: Rewrite a shared note. Your own, or any if you are the GM. Every version is kept.delete_session_note(DELETE /api/v1/campaigns/{campaign}/notes/{note}). Delete a session note: Delete a shared note and its history. Your own, or any if you are the GM.
Campaign wiki
Pages for every person, place and thing, their history, and search across the lot.
list_wiki_pages(GET /api/v1/campaigns/{campaign}/wiki). List wiki pages: Every page in the campaign's wiki, by name, with its type, a short excerpt and how often it is mentioned.get_wiki_page(GET /api/v1/campaigns/{campaign}/wiki/{page}). Read a wiki page: One page: its name, type, body, pictures, the seat whose character it is, and everywhere it is mentioned.get_wiki_page_history(GET /api/v1/campaigns/{campaign}/wiki/{page}/history). Read a page's history: Every saved version of a page, newest first, with who saved it.create_wiki_page(POST /api/v1/campaigns/{campaign}/wiki). Create a wiki page: Start a page for a person, place, thing, faction or thread. A page with the same name already there is returned rather than doubled.update_wiki_page(PATCH /api/v1/campaigns/{campaign}/wiki/{page}). Edit a wiki page: 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_wiki_page(DELETE /api/v1/campaigns/{campaign}/wiki/{page}). Delete a wiki page: Delete a page. A page somebody's seat is linked to as their character is refused until the link comes off. GM only. GM only.link_wiki_page_to_seat(PUT /api/v1/campaigns/{campaign}/wiki/{page}/seat). Mark a page as somebody's character: 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.search_campaign(GET /api/v1/campaigns/{campaign}/search). Search a campaign: Find words across the shared notes, the wiki and the shared map pins. Private notes are never searched here.
My Notes
Your own sealed notes, and everything you have written at a table.
list_my_notes(GET /api/v1/campaigns/{campaign}/my-notes). List my private notes: 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.create_my_note(POST /api/v1/campaigns/{campaign}/my-notes). Write a private note: A note sealed to you. File it on a play date or a wiki page, or leave it as scratch.update_my_note(PUT /api/v1/campaigns/{campaign}/my-notes/{note}). Edit a private note: Rewrite one of your sealed notes, or refile it. Every version is kept.delete_my_note(DELETE /api/v1/campaigns/{campaign}/my-notes/{note}). Delete a private note: Delete one of your sealed notes and every version of it.get_my_note_history(GET /api/v1/campaigns/{campaign}/my-notes/{note}/history). Read a private note's history: Every version of one of your sealed notes.list_everything_i_wrote(GET /api/v1/campaigns/{campaign}/my-writing). Everything I wrote here: The shared half of My Notes: the session notes you wrote, the pins you dropped for everyone, and every wiki version you saved.
Map
The table's maps and the pins on them, shared and private.
list_maps(GET /api/v1/campaigns/{campaign}/maps). List maps: 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.list_pins(GET /api/v1/campaigns/{campaign}/maps/{map}/pins). List a map's pins: The shared pins on one map, plus your own private ones. Places carry the wiki page they are.list_my_private_pins(GET /api/v1/campaigns/{campaign}/my-pins). List my private pins: Your private pins on every map at this table.create_pin(POST /api/v1/campaigns/{campaign}/maps/{map}/pins). Drop a pin: Put a pin on a map. Coordinates are fractions of the map's width and height, from the top left. A pin carryingentitySlugis that page's place on the map (GM only).update_pin(PATCH /api/v1/campaigns/{campaign}/pins/{pin}). Move or edit a pin: Move a pin or change what it says. Fields left out stay as they are.delete_pin(DELETE /api/v1/campaigns/{campaign}/pins/{pin}). Delete a pin: Take a pin off the map. Your own, or any shared one if you are the GM.update_map(PATCH /api/v1/campaigns/{campaign}/maps/{map}). Rename or reorder a map: Rename a map the GM added, or move it one place along the tabs (move: -1 or 1). GM only. GM only.delete_map(DELETE /api/v1/campaigns/{campaign}/maps/{map}). Take a map down: Remove a map the GM added, with every pin on it (private ones included). Gone for good. GM only. GM only.
Characters
Sheets, hit points, conditions, spell slots, items, homebrew and companions.
list_characters(GET /api/v1/campaigns/{campaign}/characters). List characters: Every character at the table by name, with who holds each, their level line and whether they have fallen.get_character(GET /api/v1/campaigns/{campaign}/characters/{character}). Read a character: 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_party(GET /api/v1/campaigns/{campaign}/party). The party at a glance: Every standing character's live numbers in one call, the Master HUD's view. GM only. GM only.update_character_state(PATCH /api/v1/campaigns/{campaign}/characters/{character}/state). Change hit points, conditions or slots: 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, spell slots spent or restored, and death saves. Everything else on the sheet is left alone.set_character_overlay(PUT /api/v1/campaigns/{campaign}/characters/{character}/overlay). Replace what has been spent: 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. Preferupdate_character_statefor a single change.add_character_from_beyond(POST /api/v1/campaigns/{campaign}/characters). Add a character from Beyond: Mirror a public sheet from Beyond into this table, by its link or its number. One way: nothing is written back.sync_character(POST /api/v1/campaigns/{campaign}/characters/{character}/sync). Sync a character from Beyond: Re-read a Beyond sheet. Resets what has been spent to what Beyond reports, except the parts named inkeep. Homebrew, custom items, companions and layout are never touched.play_character(POST /api/v1/campaigns/{campaign}/characters/{character}/play). Make a character my primary: Switch which of your characters is the one the table sees. Yours only. The character's own player.mark_character_fallen(POST /api/v1/campaigns/{campaign}/characters/{character}/death). Mark a character as fallen: 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.revive_character(DELETE /api/v1/campaigns/{campaign}/characters/{character}/death). Bring a character back: Take the fallen stamp off. Nothing that moved to a successor moves back.delete_character(DELETE /api/v1/campaigns/{campaign}/characters/{character}). Delete a character: Delete a character and everything made for them here. Gone for good.assign_character(POST /api/v1/campaigns/{campaign}/characters/{character}/assign). Hand a character to a seat: Give a character made here to a player's seat. From then on it is theirs to edit and play. GM only. GM only.set_character_portrait(PUT /api/v1/campaigns/{campaign}/characters/{character}/portrait). Set a character's portrait: Wear a picture of your own, or switch back to the one Beyond has. Both are kept whichever is worn.add_custom_item(POST /api/v1/campaigns/{campaign}/characters/{character}/items). Add a custom item: Put something in the character's Inventory that the sheet doesn't have. A sync never touches it.update_custom_item(PUT /api/v1/campaigns/{campaign}/characters/{character}/items/{item}). Edit a custom item: Replace a custom item's details.delete_custom_item(DELETE /api/v1/campaigns/{campaign}/characters/{character}/items/{item}). Delete a custom item: Take a custom item out of the Inventory.add_homebrew_box(POST /api/v1/campaigns/{campaign}/characters/{character}/homebrew). Add a homebrew card: Add a card to the character's Actions: an ability, a trick, a boon. A sync never touches it.update_homebrew_box(PUT /api/v1/campaigns/{campaign}/characters/{character}/homebrew/{box}). Edit a homebrew card: Replace a homebrew card's details.delete_homebrew_box(DELETE /api/v1/campaigns/{campaign}/characters/{character}/homebrew/{box}). Delete a homebrew card: Take a homebrew card off the Actions tab.add_companion(POST /api/v1/campaigns/{campaign}/characters/{character}/companions). Add a companion: Give the character a companion with a stat block of its own. One animal companion at a time.update_companion(PUT /api/v1/campaigns/{campaign}/characters/{character}/companions/{companion}). Edit a companion: Replace a companion's stat block.set_companion_state(PUT /api/v1/campaigns/{campaign}/characters/{character}/companions/{companion}/state). Change a companion's hit points: Set a companion's hit points and the uses spent on its cards.delete_companion(DELETE /api/v1/campaigns/{campaign}/characters/{character}/companions/{companion}). Delete a companion: Part ways with a companion.
Character builder
Make a character from scratch, or design the sheet itself.
create_custom_character(POST /api/v1/campaigns/{campaign}/characters/custom). Make a character here: 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.update_custom_sheet(PUT /api/v1/campaigns/{campaign}/characters/{character}/sheet). Edit a character made here: Rewrite a custom character's sheet from the creator's answers. Synced sheets are refused: change those on Beyond and sync.list_character_drafts(GET /api/v1/campaigns/{campaign}/drafts). List my half-made characters: Characters you started in the creator and haven't finished.start_character_draft(POST /api/v1/campaigns/{campaign}/drafts). Start a character draft: Open the creator:guidedfor the usual questions,blankto design the sheet itself, optionally from a frame the table keeps.get_character_draft(GET /api/v1/campaigns/{campaign}/drafts/{draft}). Read a character draft: One draft and the question it is on.update_character_draft(PUT /api/v1/campaigns/{campaign}/drafts/{draft}). Save a character draft: Save the whole draft. Every save carries every answer.finish_character_draft(POST /api/v1/campaigns/{campaign}/drafts/{draft}/finish). Finish a character draft: Turn a draft into a character. It must have a name.delete_character_draft(DELETE /api/v1/campaigns/{campaign}/drafts/{draft}). Throw a draft away: Delete a half-made character.list_sheet_frames(GET /api/v1/campaigns/{campaign}/frames). List the table's sheet designs: Sheet shapes designed with Blank Slate and kept at this table for the next player.save_sheet_frame(POST /api/v1/campaigns/{campaign}/frames). Keep a sheet design at the table: Save a sheet shape under a name. Saving over the same name corrects it.delete_sheet_frame(DELETE /api/v1/campaigns/{campaign}/frames/{frame}). Take a sheet design off the table: Remove a kept frame. Characters built on it keep their own copy.
Combat Tracker
Run a battle: the roster, initiative, turns, health and the summary.
get_combat_roster(GET /api/v1/campaigns/{campaign}/combat/roster). Who can join a battle: Every seat at the table (with their character's numbers where they have one) and every sheet nobody holds. GM only. GM only.list_battles(GET /api/v1/campaigns/{campaign}/combat/battles). List battles: Every battle at the table, newest first: drafts, the one under way, and the ended. GM only. GM only.create_battle(POST /api/v1/campaigns/{campaign}/combat/battles). Set up a battle: Start a battle's roster. Nothing happens until it is started. GM only. GM only.get_battle(GET /api/v1/campaigns/{campaign}/combat/battles/{battle}). Read a battle: The order, whose turn it is, the round, everybody's live health, and the log. GM only. GM only.update_battle(PATCH /api/v1/campaigns/{campaign}/combat/battles/{battle}). Rename a battle or set the turn: Change the battle's name, its round, or whose turn it is. GM only. GM only.add_participant(POST /api/v1/campaigns/{campaign}/combat/battles/{battle}/participants). Put somebody in a battle: Add a character at this table (playerorcompanion, by slug, with live health), a seat with no sheet (seat), or anybody you name on the spot (custom, orcreaturewith a stat block you supply). GM only. GM only.update_participant(PATCH /api/v1/campaigns/{campaign}/combat/battles/{battle}/participants/{participant}). Change somebody in a battle: Their name, side, initiative, stat block or state. Health for a character at this table lands on their own sheet. GM only. GM only.change_participant_health(POST /api/v1/campaigns/{campaign}/combat/battles/{battle}/participants/{participant}/health). Damage or heal somebody in a battle: Take damage (temporary hit points first) or heal (never past the maximum), and it goes in the log. GM only. GM only.remove_participant(DELETE /api/v1/campaigns/{campaign}/combat/battles/{battle}/participants/{participant}). Take somebody out of a battle: Remove them from the order altogether. GM only. GM only.start_battle(POST /api/v1/campaigns/{campaign}/combat/battles/{battle}/start). Start a battle: The roster becomes the order: round one, first in initiative goes. GM only. GM only.next_turn(POST /api/v1/campaigns/{campaign}/combat/battles/{battle}/next-turn). Next turn: 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. GM only.end_battle(POST /api/v1/campaigns/{campaign}/combat/battles/{battle}/end). End a battle: Call it. The battle becomes its summary and never moves again. GM only. GM only.delete_battle(DELETE /api/v1/campaigns/{campaign}/combat/battles/{battle}). Delete a battle: Delete a battle and its log. Characters' health stays on their sheets. GM only. GM only.
Lorekeeper
Hand a session's notes to Lorekeeper and watch the write-up land.
list_lorekeeper_jobs(GET /api/v1/campaigns/{campaign}/lorekeeper/jobs). Lorekeeper's queue: Session notes handed to Lorekeeper at this table, and how far each write-up has got. GM only. GM only.queue_session_for_lorekeeper(POST /api/v1/campaigns/{campaign}/lorekeeper/jobs). Hand session notes to Lorekeeper: 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. GM only.cancel_lorekeeper_job(DELETE /api/v1/campaigns/{campaign}/lorekeeper/jobs/{job}). Take notes back from Lorekeeper: Withdraw a job that hasn't started. GM only. GM only.
Memorial Pages
Close the book on a campaign and read its memorial.
get_memorial(GET /api/v1/campaigns/{campaign}/memorial). Read a campaign's memorial: 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.complete_campaign(POST /api/v1/campaigns/{campaign}/complete). Complete a campaign: Close the book. Asks for the campaign's own name typed back asconfirm, exactly as the app does. Undoable withreopen_campaign. GM only. GM only.reopen_campaign(DELETE /api/v1/campaigns/{campaign}/complete). Reopen a campaign: Take the completion off. Completing again puts it back. GM only. GM only.set_memorial_public(PATCH /api/v1/campaigns/{campaign}/memorial). Share or hide the memorial: Make the memorial page public (anybody with the link, and search engines) or the table's alone again. GM only. GM only.
Notifications and progress
What's new at your tables, and how far you've come.
list_notifications(GET /api/v1/me/notifications). List my notifications: 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.count_unread_notifications(GET /api/v1/me/notifications/count). Count unread notifications: How many notifications are waiting. One number, cheap to ask.mark_notifications_read(POST /api/v1/me/notifications/read). Mark notifications read: Mark some (ids), a kind's worth (kinds) or all (all: true) of your notifications read. Withunread: trueandids, put them back to unread.delete_notification(DELETE /api/v1/me/notifications/{notification}). Clear a notification: Take one notification off your list.get_my_progress(GET /api/v1/me/progress). My Tavern Level and achievements: 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.
Safe to hand over
- It is you, and no more. A key or an app reaches the tables you sit at, with the seat you have at each. A player's key can't do the GM's job, and nobody's key opens a table they don't sit at.
- You choose how far it goes. Read, write and delete are separate. Give a dashboard read only, and keep delete for the scripts you trust.
- Other people's private notes stay private. Your own private notes come back to you. Nobody else's are served by any call, to anybody.
- Revoke it in one tap. Every key and every connected app is listed in your settings with when it was last used, and goes the moment you say so.
- Shown once, stored as a hash. A key is on screen once, when you make it. After that Tavern keeps only a fingerprint of it, so not even the database can hand it back.
- No surprises. The API sends no email and never reaches the back office. Calls are limited per credential, so a runaway script slows down instead of running wild.
Build your own
Make an API key in your settings (https://gettavern.app/settings/api) and send it as a bearer header. The REST API is at https://gettavern.app/api/v1, described in full at https://gettavern.app/openapi.json and in prose at https://gettavern.app/api-docs.md. Apps that act for other people use OAuth: https://gettavern.app/auth.md.
Your campaigns
curl https://gettavern.app/api/v1/campaigns \
-H "Authorization: Bearer tvn_your_key"
Add a note to a session
curl -X POST https://gettavern.app/api/v1/campaigns/hollowmere/sessions/2026-11-05/notes \
-H "Authorization: Bearer tvn_your_key" \
-H "Content-Type: application/json" \
-d '{"body": "We crossed at dusk. [[The Ferryman]] wanted silver, not coin."}'