API Reference
Complete REST API documentation for Archivist. All endpoints require the x-api-key header unless noted.
New to Archivist? Start with Core Concepts and the Data Models. See also code examples and the API changelog on GitHub.
Supported Methods
Quick reference for HTTP methods supported by each resource.
| Resource | GET | POST | PUT | PATCH | DELETE |
|---|---|---|---|---|---|
| Campaigns | ✓ | ✓ | — | ✓ | ✓ |
| Characters | ✓ | ✓ | — | ✓ | ✓ |
| Sessions | ✓ | — | — | ✓ | — |
| Beats | ✓ | ✓ | — | ✓ | ✓ |
| Moments | ✓ | ✓ | — | ✓ | ✓ |
| Factions | ✓ | ✓ | — | ✓ | ✓ |
| Locations | ✓ | ✓ | — | ✓ | ✓ |
| Items | ✓ | ✓ | — | ✓ | ✓ |
| Journal Entries | ✓ | ✓ | ✓ | — | ✓ |
| Journal Folders | ✓ | ✓ | ✓ | — | ✓ |
| Links | ✓ | ✓ | — | ✓ | ✓ |
| Entities | ✓ | — | — | — | — |
| Ask (RAG) | — | ✓ | — | — | — |
Health
/healthHealth check for the API service (no auth required).
Request Example
curl https://api.myarchivist.ai/healthResponses
{
"status": "healthy",
"service": "archivist-api",
"version": "1.0.0",
"environment": "production",
"port": 8000,
"timestamp": "2025-01-15T10:30:00.123456Z"
}Ask (RAG)
/v1/askRAG chat endpoint for campaign questions. Supports chat history and streaming.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
campaign_id*— ID of your campaignmessages*— Chat messages: [{ role, content }]stream— Return a streaming responsegm_permissions— When true, grant full journal access for the request (overrides asker_id). Defaults to false.asker_id— Optional user ID to scope journal access; must be a player/admin/owner of the campaign. Defaults to null.Request Example
curl -X POST \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"campaign_id": "your-campaign-id",
"messages": [ {"role": "user", "content": "Who is Cassius Traven?"} ],
"gm_permissions": false,
"asker_id": null
}' \
https://api.myarchivist.ai/v1/askResponses
{
"answer": "Cassius Traven is a shadowy information broker who operates in the city's underground...",
"citations": [
{
"citation_id": "S1",
"source_type": "timeline",
"source_id": "beat-abc123",
"title": "Session 4 — The Sunken Manor",
"session_number": 4,
"excerpt": "Cassius revealed his true identity as..."
}
],
"monthlyTokensRemaining": 12345,
"hourlyTokensRemaining": 6789
}{
"detail": "messages is required"
}{
"detail": "Invalid API key"
}{
"detail": "asker_id user does not have access to this world"
}{
"detail": "asker_id user not found"
}{
"detail": "Monthly token limit exceeded"
}Campaigns
/v1/campaignsList your campaigns.
Headers
x-api-key*— Your API keyQuery Params
page— Page numbersize— Page sizeRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/campaigns?page=1&size=10"Responses
{
"data": [
{
"id": "camp_abc123",
"title": "Shadows of Elyndor",
"description": "A dark fantasy campaign",
"system": "D&D 5e",
"public": false,
"created_at": "2024-01-15T10:30:00Z"
}
],
"total": 1,
"page": 1,
"size": 20,
"pages": 1
}{
"detail": "Invalid API key"
}/v1/campaigns/{campaign_id}Get a specific campaign by ID.
Headers
x-api-key*— Your API keyRequest Example
curl -H "x-api-key: YOUR_API_KEY" https://api.myarchivist.ai/v1/campaigns/abc123Responses
{
"id": "camp_abc123",
"title": "Shadows of Elyndor",
"description": "A dark fantasy campaign",
"system": "D&D 5e",
"public": false,
"created_at": "2024-01-15T10:30:00Z"
}{
"detail": "Campaign not found"
}{
"detail": "Invalid API key"
}/v1/campaignsCreate a new campaign. Creation bootstraps the same baseline records as the Archivist web app: the owner is added as a player/admin, a default GM speaker is created, Character Arc defaults are seeded, and default compendium field definitions are created.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
title*— Campaign titledescription— Campaign descriptionsystem— Game system (e.g. D&D 5e)Request Example
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "My Epic Campaign",
"description": "A thrilling adventure",
"system": "D&D 5e"
}' \
https://api.myarchivist.ai/v1/campaignsResponses
{
"id": "camp_new123",
"title": "My Epic Campaign",
"description": "A thrilling adventure",
"system": "D&D 5e",
"public": false,
"created_at": "2024-01-25T16:45:00Z"
}{
"detail": "title is required"
}{
"detail": "Invalid API key"
}/v1/campaigns/{campaign_id}Update a campaign.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
title— Campaign titledescription— Campaign descriptionRequest Example
curl -X PATCH \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Updated Campaign Name"}' \
https://api.myarchivist.ai/v1/campaigns/abc123Responses
{
"id": "camp_abc123",
"title": "Updated Campaign Name",
"description": "A dark fantasy campaign",
"system": "D&D 5e",
"public": false,
"created_at": "2024-01-15T10:30:00Z"
}{
"detail": "Campaign not found"
}{
"detail": "Invalid API key"
}/v1/campaigns/{campaign_id}Permanently delete a campaign. This action cannot be undone.
Headers
x-api-key*— Your API keyRequest Example
curl -X DELETE -H "x-api-key: YOUR_API_KEY" https://api.myarchivist.ai/v1/campaigns/abc123Responses
(no content){
"detail": "Campaign not found"
}{
"detail": "Invalid API key"
}/v1/campaigns/{campaign_id}/statsGet campaign statistics (character count, session count, etc).
Headers
x-api-key*— Your API keyRequest Example
curl -H "x-api-key: YOUR_API_KEY" https://api.myarchivist.ai/v1/campaigns/abc123/statsResponses
{
"campaign_id": "camp_abc123",
"characters": 12,
"sessions": 24,
"moments": 156,
"beats": 48,
"factions": 5,
"locations": 18,
"items": 32
}{
"detail": "Campaign not found"
}/v1/campaigns/{campaign_id}/linksList links between entities in a campaign. Supports filtering by source/target entities and alias.
Headers
x-api-key*— Your API keyQuery Params
page— Page numbersize— Page sizefrom_id— Filter by source entity IDfrom_type— Filter by source entity type: Character, Faction, Location, Item, Moment, GameSession, Beat, Backstory, Journal, or Worldto_id— Filter by target entity IDto_type— Filter by target entity type: Character, Faction, Location, Item, or Momentalias— Filter by relationship label/aliasRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/campaigns/abc123/links?from_id=char_456&from_type=Character"Responses
{
"data": [
{
"id": "link_123",
"campaign_id": "camp_abc123",
"from_id": "char_456",
"from_type": "Character",
"to_id": "char_789",
"to_type": "Character",
"alias": "friends",
"created_at": "2024-01-15T10:30:00Z"
}
],
"total": 25,
"page": 1,
"size": 20,
"pages": 2
}/v1/campaigns/{campaign_id}/linksCreate or update a link between two entities. Source and target records must both exist in the same campaign, and aliases are normalized/deduped server-side.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
from_id*— Source entity IDfrom_type*— Source entity type: Character, Faction, Location, Item, Moment, GameSession, Beat, Backstory, Journal, or Worldto_id*— Target entity IDto_type*— Target entity type: Character, Faction, Location, Item, or Momentalias— Relationship labelResponses
{
"id": "link_new123",
"campaign_id": "camp_abc123",
"from_id": "char_456",
"from_type": "Character",
"to_id": "loc_789",
"to_type": "Location",
"alias": "home",
"created_at": "2024-01-25T16:45:00Z"
}{
"detail": "Target Character not found"
}{
"detail": "Link source and target must belong to the same world"
}/v1/campaigns/{campaign_id}/links/{link_id}Update a link alias. Aliases are normalized with the same rules used during create.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
alias— Updated relationship labelResponses
{
"id": "link_123",
"campaign_id": "camp_abc123",
"from_id": "char_456",
"from_type": "Character",
"to_id": "loc_789",
"to_type": "Location",
"alias": "updated label",
"created_at": "2024-01-15T10:30:00Z"
}{
"detail": "Link not found"
}{
"detail": "Link alias cannot be empty"
}/v1/campaigns/{campaign_id}/links/{link_id}Delete a link.
Headers
x-api-key*— Your API keyResponses
(no content){
"detail": "Link not found"
}Characters
/v1/charactersList characters filtered by campaign. The legacy `player_name` field is deprecated; responses may include a nullable read-only `player` object sourced from Speaker.
Headers
x-api-key*— Your API keyQuery Params
campaign_id*— Campaign IDsearch— Full-text search (name, aliases, player, type) with ILIKE fallbackcharacter_type— PC or NPCapproved_only— Filter approved (default true)with_links— Preserve wikilink markuppage— Page numbersize— Page size (max 100)fields— Set to card for lightweight list items (id, character_name, type, image)Request Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/characters?campaign_id=abc123"Responses
{
"data": [
{
"id": "char_123",
"campaign_id": "camp_abc123",
"character_name": "Thorin Ironforge",
"player_name": null,
"player": {
"id": "speaker_123",
"name": "John Smith",
"handle": "johnsmith",
"roles": ["Player"],
"campaign_id": "camp_abc123",
"created_at": "2024-01-10T08:00:00Z"
},
"description": "A brave dwarf warrior",
"type": "PC",
"merge": false,
"created_at": "2024-01-15T10:30:00Z"
}
],
"total": 1,
"page": 1,
"size": 20,
"pages": 1
}{
"detail": "campaign_id is required"
}{
"detail": "Invalid API key"
}/v1/characters/{character_id}Get a specific character by ID. The response may include aliases, backstory, speaker linkage, and a nullable read-only `player` object; `player_name` remains legacy.
Headers
x-api-key*— Your API keyQuery Params
with_links— Preserve wikilink markupRequest Example
curl -H "x-api-key: YOUR_API_KEY" https://api.myarchivist.ai/v1/characters/char-123Responses
{
"id": "char_123",
"campaign_id": "camp_abc123",
"character_name": "Thorin Ironforge",
"character_aliases": ["Thorin", "Ironforge"],
"player_name": null,
"player": {
"id": "speaker_123",
"name": "John Smith",
"handle": "johnsmith",
"roles": ["Player"],
"campaign_id": "camp_abc123",
"created_at": "2024-01-10T08:00:00Z"
},
"description": "A brave dwarf warrior",
"backstory": "Raised in the halls beneath Emberpeak...",
"type": "PC",
"merge": false,
"created_at": "2024-01-15T10:30:00Z"
}{
"detail": "Character not found"
}/v1/charactersCreate a new character. `player` is response-only and is not accepted in the request body. Additive parity fields include `character_aliases`/`characterAliases`, `speaker_id`/`speakerId`, and `backstory`; legacy `character_alias` and `player_name` are still accepted.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
character_name*— Character namecampaign_id*— Campaign IDcharacter_alias— Deprecated single alias field; merged into character_aliases after sanitizationcharacter_aliases— Array of aliases (camelCase `characterAliases` also accepted)player_name— Deprecated legacy player name fieldspeaker_id— Linked Speaker ID in the same campaign (camelCase `speakerId` also accepted)description— Character descriptionbackstory— Character backstory; wikilinks are resolved and stored separately from description linkstype— PC or NPCimage— HTTPS image URL (max 2048 chars)Request Example
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"character_name": "Thorin Ironforge",
"character_aliases": ["Thorin", "Ironforge"],
"description": "A brave dwarf warrior",
"backstory": "Raised in the halls beneath Emberpeak...",
"speaker_id": "speaker_123",
"type": "PC",
"campaign_id": "abc123"
}' \
https://api.myarchivist.ai/v1/charactersResponses
{
"id": "char_new456",
"campaign_id": "camp_abc123",
"character_name": "Thorin Ironforge",
"character_aliases": ["Thorin", "Ironforge"],
"player_name": null,
"player": null,
"description": "A brave dwarf warrior",
"backstory": "Raised in the halls beneath Emberpeak...",
"type": "PC",
"merge": false,
"created_at": "2024-01-25T16:45:00Z"
}{
"detail": "speaker_id must belong to the same world"
}{
"detail": "image must be an HTTPS URL"
}{
"detail": "Campaign not found"
}/v1/characters/{character_id}Update a character. `player` is response-only and is not accepted in the request body. PATCH supports the same additive parity fields as POST.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
character_name— Character namecharacter_alias— Deprecated single alias fieldcharacter_aliases— Alias list (camelCase `characterAliases` also accepted)player_name— Deprecated legacy player name fieldspeaker_id— Linked Speaker ID (camelCase `speakerId` also accepted)description— Character descriptionbackstory— Character backstorytype— PC or NPCimage— HTTPS image URL (max 2048 chars)Responses
{
"id": "char_123",
"campaign_id": "camp_abc123",
"character_name": "Thorin Ironforge the Bold",
"character_aliases": ["Thorin", "The Bold"],
"player_name": null,
"player": {
"id": "speaker_123",
"name": "John Smith",
"handle": "johnsmith",
"roles": ["Player"],
"campaign_id": "camp_abc123",
"created_at": "2024-01-10T08:00:00Z"
},
"description": "A brave dwarf warrior with a legendary magic axe",
"backstory": "Raised in the halls beneath Emberpeak and sworn to the old kings...",
"type": "PC",
"merge": false,
"created_at": "2024-01-15T10:30:00Z"
}{
"detail": "Character name cannot match the linked speaker name"
}{
"detail": "Character not found"
}/v1/characters/{character_id}Delete a character.
Headers
x-api-key*— Your API keyResponses
(no content){
"detail": "Character not found"
}Sessions
/v1/sessionsList sessions in a campaign. Each session includes a notes field which is null for most session types. For rawNotes sessions, notes contains the user-supplied session notes (these sessions have no transcript content).
Headers
x-api-key*— Your API keyQuery Params
campaign_id*— Campaign IDpage— Page number (default 1)size— Page size (default 20, max 100)session_type— discordVoice, rawNotes, txtUpload, or playByPostpublic_only— Show only public sessionswith_links— Preserve wikilink markup in summary fieldsRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/sessions?campaign_id=abc123&page=1&size=20"Responses
{
"data": [
{
"id": "session_123",
"campaign_id": "camp_abc123",
"type": "audioUpload",
"title": "The Lost Mines of Phandelver",
"summary": "The party explored the ancient mines...",
"notes": null,
"session_date": "2024-01-20T19:00:00Z",
"image": null,
"index": 5,
"public": false,
"pbp_start_msg_url": null,
"pbp_end_msg_url": null,
"created_at": "2024-01-20T22:30:00Z",
"updated_at": null
}
],
"total": 24,
"page": 1,
"size": 20,
"pages": 2
}{
"detail": "campaign_id is required"
}/v1/sessions/{session_id}Get a specific session by ID. The notes field is null for most session types. For rawNotes sessions, notes contains the user-supplied session notes; these sessions do not have transcript content. When include_beats or include_moments is true, nested beat descriptions and moment content follow the same with_links behavior as the dedicated beats and moments endpoints.
Headers
x-api-key*— Your API keyQuery Params
include_beats— Include related beatsinclude_moments— Include related momentswith_links— Preserve wikilink markup in summary and, when included, nested beat descriptions and moment contentRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/sessions/session-123?include_beats=true"Responses
{
"id": "session_123",
"campaign_id": "camp_abc123",
"type": "audioUpload",
"title": "The Lost Mines of Phandelver",
"summary": "The party explored the ancient mines...",
"notes": null,
"session_date": "2024-01-20T19:00:00Z",
"image": null,
"index": 5,
"public": false,
"pbp_start_msg_url": null,
"pbp_end_msg_url": null,
"created_at": "2024-01-20T22:30:00Z",
"updated_at": null,
"beats": null,
"moments": null
}{
"detail": "Session not found"
}/v1/sessions/{session_id}/cast-analysisGet stored cast analysis data for a session.
Headers
x-api-key*— Your API keyRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/sessions/session-123/cast-analysis"Responses
{
"id": "cast_analysis_123",
"session_id": "session_123",
"analysis": {
"coreSessionMetrics": {
"totalLines": 1284,
"totalWords": 18201,
"avgWordsPerLine": 14.18,
"totalTurns": 1284,
"avgTurnWords": 14.18
},
"talkShare": {
"GM": 0.42,
"Player 1": 0.31,
"Player 2": 0.27
}
},
"created_at": "2025-01-12T20:30:00Z",
"updated_at": "2025-01-12T20:30:00Z"
}{
"detail": "Cast analysis not found"
}/v1/sessions/{session_id}/handoutGet the session handout for a game session. Returns the structured handout JSON if one has been generated. The handout includes a narrative summary, session outline, encounters, character and entity spotlights, notable items, valuable information, party status, and moments.
Headers
x-api-key*— Your API keyRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/sessions/session-123/handout"Responses
{
"summary": "The party ventured into the **Thornwood** seeking the missing courier...",
"sessionOutline": "### Entering the Thornwood\n- The party tracked muddy footprints north from Briarton\n- They discovered an abandoned camp with signs of a struggle\n\n### The Ambush\n- Bandits attacked from the tree line\n- The party captured the bandit leader, **Harsk**",
"encounters": [
{
"title": "Ambush at the Thornwood Camp",
"bullets": [
"Three bandits attacked from concealed positions in the tree line",
"The ranger flanked the enemies using the dense underbrush",
"The bandit leader Harsk was subdued and captured alive",
"One bandit escaped north toward the river crossing"
]
}
],
"characterSpotlight": [
{
"name": "Elara Windsong",
"description": "Led the interrogation of the captured bandit leader. Negotiated safe passage through the Thornwood in exchange for Harsk's release.",
"bullets": [
"Identified the bandit camp through tracking",
"Secured critical intelligence about the courier's destination"
]
}
],
"otherEntitySpotlight": [
{
"name": "Harsk",
"description": "Bandit leader captured during the Thornwood ambush; revealed the courier was taken to Ashenmere."
}
],
"items": [
{
"name": "Courier's Sealed Letter",
"description": "A wax-sealed letter bearing the sigil of House Valdris, found discarded at the abandoned camp."
}
],
"valuableInformation": [
{
"info": "The courier was taken alive to Ashenmere by a group working for an unknown patron."
},
{
"info": "Harsk mentioned a 'tall woman in grey' who paid for the abduction."
}
],
"partyStatusAndNextSteps": {
"partyStatus": {
"summary": "The party is camped at the edge of the Thornwood, lightly wounded after the bandit ambush.",
"bullets": [
"Harsk has been released per the negotiated agreement",
"The sealed letter may contain information about the courier's mission"
]
},
"nextSteps": {
"summary": "The party intends to travel north to Ashenmere to locate and rescue the missing courier."
}
},
"moments": [
{
"label": "A Tense Negotiation",
"content": "Elara convinced Harsk to reveal the courier's location in exchange for his freedom."
}
]
}{
"detail": "Handout not found for this session"
}/v1/sessions/{session_id}/transcriptGet the cleaned transcript for a game session. Returns the processed transcript with utterances, full text, and aggregate stats. Raw/uncleaned utterances are excluded. Use format=markdown for an agent-friendly plain-text markdown version (returned with Content-Type: text/markdown).
Headers
x-api-key*— Your API keyQuery Params
format— Response format: json (default) or markdown. The markdown format returns a plain-text document with metadata, statistics, and speaker-labeled utterances optimised for LLM agent consumption.Request Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/sessions/session-123/transcript"Responses
{
"version": 1,
"created_at": "2025-03-10T06:01:11.195Z",
"metadata": {
"session_id": "session-123",
"session_type": "discordVoice",
"campaign_id": "camp-abc",
"world_title": "Adventures in the Realm",
"world_language": "en",
"session_date": "2025-03-10T03:28:30.266Z",
"speakers": [
"Joshua Perez (GM)",
"Hannah Gray (Zekaria [PC])"
],
"speaker_count": 2,
"source_type": "discordVoice",
"created_at": "2025-03-10T06:01:11.195Z"
},
"utterances": [
{
"speaker_label": "Joshua Perez (GM)",
"transcript": "Alright, let's pick up where we left off last time.",
"start": 0,
"end": 3.2,
"said_at": "2025-03-10T03:28:35.170Z"
},
{
"speaker_label": "Hannah Gray (Zekaria [PC])",
"transcript": "Sounds good. I think we were heading toward the ruins.",
"start": 3.5,
"end": 6.8,
"said_at": "2025-03-10T03:28:38.670Z"
}
],
"text": "Joshua Perez (GM): Alright, let's pick up where we left off...",
"stats": {
"char_count": 143672,
"tokens": 40104,
"utterance_count": 1797
}
}# Transcript
## Metadata
- **Session ID**: session-123
- **Campaign**: Adventures in the Realm
- **Session Date**: 2025-03-10T03:28:30.266Z
- **Session Type**: discordVoice
- **Speakers**: Joshua Perez (GM), Hannah Gray (Zekaria [PC])
- **Speaker Count**: 2
## Statistics
- **Utterances**: 1,797
- **Characters**: 143,672
- **Tokens**: 40,104
## Transcript
**Joshua Perez (GM)** [0:00]: Alright, let's pick up where we left off last time.
**Hannah Gray (Zekaria [PC])** [0:03]: Sounds good. I think we were heading toward the ruins.
...{
"detail": "Transcript not found for this session"
}/v1/sessions/{session_id}Update a session (title, summary, or session_date only).
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
title— Session titlesummary— Session summarysession_date— ISO datetimeResponses
{
"id": "session_123",
"campaign_id": "camp_abc123",
"type": "audioUpload",
"title": "Updated Session Title",
"summary": "Updated summary...",
"notes": null,
"session_date": "2024-01-20T19:00:00Z",
"image": null,
"index": 5,
"public": false,
"pbp_start_msg_url": null,
"pbp_end_msg_url": null,
"created_at": "2024-01-20T22:30:00Z",
"updated_at": "2024-01-20T23:00:00Z"
}{
"detail": "Session not found"
}Beats
/v1/beatsList beats in a campaign (paginated, ordered by index), list beats linked to a session (paginated flat list), or fetch a session beat tree with include_hierarchy=true.
Headers
x-api-key*— Your API keyQuery Params
campaign_id— Campaign ID (required for campaign-wide list)session_id— Session ID — returns a flat paginated list, or a nested tree when include_hierarchy=trueinclude_hierarchy— With session_id, return nested beat tree instead of paginated flat listpage— Page number (flat lists only)size— Page size (flat lists only, max 100)with_links— Preserve wikilink markupRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/beats?campaign_id=abc123"
# Flat session-scoped beat list (paginated)
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/beats?session_id=session_456&page=1&size=20"
# Session beat hierarchy (nested array, not paginated)
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/beats?session_id=session_456&include_hierarchy=true"Responses
{
"data": [
{
"id": "beat_123",
"campaign_id": "camp_abc123",
"game_session_ids": ["session_456"],
"game_session_id": "session_456",
"label": "The Final Battle",
"type": "major",
"description": "The party confronts the dark lord",
"index": 1,
"parent_id": null,
"metadata": {},
"created_at": "2024-01-20T22:30:00Z",
"updated_at": null
}
],
"total": 48,
"page": 1,
"size": 20,
"pages": 3
}[
{
"id": "beat_123",
"campaign_id": "camp_abc123",
"label": "Act I",
"type": "major",
"index": 0,
"children": [
{
"id": "beat_124",
"label": "The ambush",
"type": "minor",
"index": 0,
"children": []
}
]
}
]{
"detail": "campaign_id or session_id is required"
}/v1/beats/{beat_id}Get a specific beat by ID.
Headers
x-api-key*— Your API keyQuery Params
with_links— Preserve wikilink markupRequest Example
curl -H "x-api-key: YOUR_API_KEY" https://api.myarchivist.ai/v1/beats/beat-123Responses
{
"id": "beat_123",
"campaign_id": "camp_abc123",
"game_session_ids": ["session_456"],
"game_session_id": "session_456",
"label": "The Final Battle",
"type": "major",
"description": "The party confronts the dark lord",
"index": 1,
"parent_id": null,
"metadata": {},
"created_at": "2024-01-20T22:30:00Z",
"updated_at": null
}{
"detail": "Beat not found"
}/v1/beatsCreate a new beat.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
label*— Beat labeltype*— major, minor, or stepcampaign_id*— Campaign IDgame_session_id— Session IDdescription— Beat descriptionindex— Position within siblingsparent_id— Parent beat ID (for minor/step)Request Example
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"label": "The Final Battle",
"type": "major",
"campaign_id": "abc123",
"game_session_id": "session-123"
}' \
https://api.myarchivist.ai/v1/beatsResponses
{
"id": "beat_new789",
"campaign_id": "camp_abc123",
"game_session_id": "session_456",
"label": "The Final Battle",
"type": "major",
"description": null,
"index": 1,
"parent_id": null,
"created_at": "2024-01-25T16:45:00Z"
}{
"detail": "label, type, and campaign_id are required"
}/v1/beats/{beat_id}Update a beat (uses JSON Merge Patch).
Headers
x-api-key*— Your API keyContent-Type*— application/merge-patch+jsonRequest Body
label— Beat labeldescription— Beat descriptionindex— Position within siblingsparent_id— Parent beat IDResponses
{
"id": "beat_123",
"campaign_id": "camp_abc123",
"game_session_id": "session_456",
"label": "Updated Beat Label",
"type": "major",
"description": "Updated description",
"index": 2,
"parent_id": null,
"created_at": "2024-01-20T22:30:00Z"
}{
"detail": "Beat not found"
}/v1/beats/{beat_id}Delete a beat (with hierarchy rules).
Headers
x-api-key*— Your API keyResponses
(no content){
"detail": "Beat not found"
}Moments
/v1/momentsList moments in a campaign or session. Supports entity and session filters via comma-separated ID lists.
Headers
x-api-key*— Your API keyQuery Params
campaign_id— Campaign IDsession_id— Session IDsearch— Search by labelpage— Page numbersize— Page size (max 100)fields— Set to card for lightweight list items (id, label, image, session_id, created_at, categories)character_ids— Comma-separated character IDs to filter by linked entitieslocation_ids— Comma-separated location IDs to filter by linked entitiesfaction_ids— Comma-separated faction IDs to filter by linked entitiesitem_ids— Comma-separated item IDs to filter by linked entitiessession_ids— Comma-separated session IDs to filter by sessionwith_links— Preserve wikilink markupRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/moments?campaign_id=abc123&character_ids=char_123,char_456&session_ids=session_456"Responses
{
"data": [
{
"id": "moment_123",
"campaign_id": "camp_abc123",
"session_id": "session_456",
"label": "Epic Quote",
"content": "I am Thorin, son of Thrain...",
"image": null,
"index": 0,
"categories": ["quotes"],
"created_at": "2024-01-20T22:30:00Z",
"updated_at": null
}
],
"total": 156,
"page": 1,
"size": 20,
"pages": 8
}{
"data": [
{
"id": "moment_123",
"label": "Epic Quote",
"image": null,
"session_id": "session_456",
"created_at": "2024-01-20T22:30:00Z",
"categories": ["quotes"]
}
],
"total": 156,
"page": 1,
"size": 20,
"pages": 8
}/v1/moments/{moment_id}Get a specific moment by ID.
Headers
x-api-key*— Your API keyQuery Params
with_links— Preserve wikilink markupResponses
{
"id": "moment_123",
"campaign_id": "camp_abc123",
"session_id": "session_456",
"label": "Epic Quote",
"content": "I am Thorin, son of Thrain...",
"image": null,
"index": 0,
"categories": ["quotes"],
"created_at": "2024-01-20T22:30:00Z",
"updated_at": "2024-01-21T10:00:00Z"
}{
"detail": "Moment not found"
}/v1/momentsCreate a new moment.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
label*— Moment labelcampaign_id*— Campaign IDsession_id— Session IDcontent— Moment contentResponses
{
"id": "moment_new999",
"campaign_id": "camp_abc123",
"session_id": "session_456",
"label": "Epic Quote",
"content": "I am Thorin...",
"created_at": "2024-01-25T16:45:00Z"
}{
"detail": "label and campaign_id are required"
}/v1/moments/{moment_id}Update a moment.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonResponses
{
"id": "moment_123",
"campaign_id": "camp_abc123",
"session_id": "session_456",
"label": "Updated Label",
"content": "Updated content...",
"created_at": "2024-01-20T22:30:00Z"
}{
"detail": "Moment not found"
}/v1/moments/{moment_id}Delete a moment.
Headers
x-api-key*— Your API keyResponses
(no content){
"detail": "Moment not found"
}Factions
/v1/factionsList factions in a campaign.
Headers
x-api-key*— Your API keyQuery Params
campaign_id*— Campaign IDsearch— Full-text search (name, aliases, type) with ILIKE fallbackwith_links— Preserve wikilink markupRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/factions?campaign_id=abc123&search=thieves"Responses
{
"data": [
{
"id": "faction_123",
"campaign_id": "camp_abc123",
"name": "The Shadow Thieves",
"description": "A guild of thieves operating in the shadows",
"type": "guild",
"merge": false,
"created_at": "2024-01-15T10:30:00Z"
}
],
"total": 5,
"page": 1,
"size": 20,
"pages": 1
}{
"detail": "campaign_id is required"
}/v1/factions/{faction_id}Get a specific faction by ID.
Headers
x-api-key*— Your API keyQuery Params
with_links— Preserve wikilink markupResponses
{
"id": "faction_123",
"campaign_id": "camp_abc123",
"name": "The Shadow Thieves",
"description": "A guild of thieves operating in the shadows",
"type": "guild",
"merge": false,
"created_at": "2024-01-15T10:30:00Z"
}{
"detail": "Faction not found"
}/v1/factionsCreate a new faction.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
name*— Faction namecampaign_id*— Campaign IDdescription— Faction descriptiontype— Faction typeimage— HTTPS image URL (max 2048 chars)Responses
{
"id": "faction_new456",
"campaign_id": "camp_abc123",
"name": "The Iron Brotherhood",
"description": "A mercenary company",
"type": "guild",
"merge": false,
"created_at": "2024-01-25T16:45:00Z"
}{
"detail": "name and campaign_id are required"
}/v1/factions/{faction_id}Update a faction.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
name— Faction namedescription— Faction descriptiontype— Faction typeimage— HTTPS image URL (max 2048 chars)Responses
{
"id": "faction_123",
"campaign_id": "camp_abc123",
"name": "Updated Faction Name",
"description": "Updated description",
"type": "guild",
"merge": false,
"created_at": "2024-01-15T10:30:00Z"
}{
"detail": "Faction not found"
}/v1/factions/{faction_id}Delete a faction.
Headers
x-api-key*— Your API keyResponses
(no content){
"detail": "Faction not found"
}Locations
/v1/locationsList locations in a campaign.
Headers
x-api-key*— Your API keyQuery Params
campaign_id*— Campaign IDsearch— Full-text search (name, aliases, type) with ILIKE fallbackwith_links— Preserve wikilink markupRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/locations?campaign_id=abc123&search=tavern"Responses
{
"data": [
{
"id": "loc_123",
"campaign_id": "camp_abc123",
"name": "The Prancing Pony",
"description": "A cozy inn in Bree",
"type": "tavern",
"parent_id": "loc_456",
"merge": false,
"created_at": "2024-01-15T10:30:00Z"
}
],
"total": 18,
"page": 1,
"size": 20,
"pages": 1
}{
"detail": "campaign_id is required"
}/v1/locations/{location_id}Get a specific location by ID.
Headers
x-api-key*— Your API keyQuery Params
with_links— Preserve wikilink markupResponses
{
"id": "loc_123",
"campaign_id": "camp_abc123",
"name": "The Prancing Pony",
"description": "A cozy inn in Bree",
"type": "tavern",
"parent_id": "loc_456",
"merge": false,
"created_at": "2024-01-15T10:30:00Z"
}{
"detail": "Location not found"
}/v1/locationsCreate a new location.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
name*— Location namecampaign_id*— Campaign IDdescription— Location descriptiontype— Location typeparent_id— Parent location IDimage— HTTPS image URL (max 2048 chars)Responses
{
"id": "loc_new789",
"campaign_id": "camp_abc123",
"name": "Rivendell",
"description": "The Last Homely House",
"type": "city",
"parent_id": null,
"merge": false,
"created_at": "2024-01-25T16:45:00Z"
}{
"detail": "name and campaign_id are required"
}/v1/locations/{location_id}Update a location.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
name— Location namedescription— Location descriptiontype— Location typeparent_id— Parent location IDimage— HTTPS image URL (max 2048 chars)Responses
{
"id": "loc_123",
"campaign_id": "camp_abc123",
"name": "Updated Location Name",
"description": "Updated description",
"type": "tavern",
"parent_id": "loc_456",
"merge": false,
"created_at": "2024-01-15T10:30:00Z"
}{
"detail": "Location not found"
}/v1/locations/{location_id}Delete a location.
Headers
x-api-key*— Your API keyResponses
(no content){
"detail": "Location not found"
}Items
/v1/itemsList items in a campaign.
Headers
x-api-key*— Your API keyQuery Params
campaign_id*— Campaign IDsearch— Full-text search (name, aliases, type) with ILIKE fallbackwith_links— Preserve wikilink markupRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/items?campaign_id=abc123&search=sword"Responses
{
"data": [
{
"id": "item_123",
"campaign_id": "camp_abc123",
"name": "Sting",
"description": "An elven blade that glows blue near orcs",
"type": "weapon",
"merge": false,
"created_at": "2024-01-15T10:30:00Z"
}
],
"total": 32,
"page": 1,
"size": 20,
"pages": 2
}{
"detail": "campaign_id is required"
}/v1/items/{item_id}Get a specific item by ID.
Headers
x-api-key*— Your API keyQuery Params
with_links— Preserve wikilink markupResponses
{
"id": "item_123",
"campaign_id": "camp_abc123",
"name": "Sting",
"description": "An elven blade that glows blue near orcs",
"type": "weapon",
"merge": false,
"created_at": "2024-01-15T10:30:00Z"
}{
"detail": "Item not found"
}/v1/itemsCreate a new item.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
name*— Item namecampaign_id*— Campaign IDdescription— Item descriptiontype— Item type (weapon, armor, etc)image— HTTPS image URL (max 2048 chars)Responses
{
"id": "item_new456",
"campaign_id": "camp_abc123",
"name": "Anduril",
"description": "The Flame of the West",
"type": "weapon",
"merge": false,
"created_at": "2024-01-25T16:45:00Z"
}{
"detail": "name and campaign_id are required"
}/v1/items/{item_id}Update an item.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
name— Item namedescription— Item descriptiontype— Item type (weapon, armor, etc)image— HTTPS image URL (max 2048 chars)Responses
{
"id": "item_123",
"campaign_id": "camp_abc123",
"name": "Updated Item Name",
"description": "Updated description",
"type": "weapon",
"merge": false,
"created_at": "2024-01-15T10:30:00Z"
}{
"detail": "Item not found"
}/v1/items/{item_id}Delete an item.
Headers
x-api-key*— Your API keyResponses
(no content){
"detail": "Item not found"
}Quests
/v1/questsList canonical quests in a campaign. Results are paginated and include summary counts, quest status/category, and first/last session provenance when known.
Headers
x-api-key*— Your API keyQuery Params
campaign_id*— Campaign IDpage— Page numbersize— Page sizesearch— Search by quest name, giver, or narrative fieldsstatus— Filter by planned | in-progress | blocked | failed | done | n/aquest_category— Filter by main | side | faction | personal | n/aRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/quests?campaign_id=abc123&status=in-progress"Responses
{
"data": [
{
"id": "quest_123",
"campaign_id": "camp_abc123",
"order_index": 2,
"quest_name": "Recover the Azure Sigil",
"quest_giver": "Aria Voss",
"quest_giver_id": "char_123",
"quest_category": "main",
"status": "in-progress",
"next_action": "Question the ferryman at Blackwake.",
"resolution": null,
"objective_count": 3,
"completed_objective_count": 1,
"progress_entry_count": 2,
"related_entity_count": 3,
"first_session": {
"id": "sess_001",
"number": 4,
"title": "Ashes on the Water",
"session_date": "2025-02-10T02:30:00Z"
},
"last_session": {
"id": "sess_003",
"number": 6,
"title": "The Ferryman's Price",
"session_date": "2025-02-24T02:30:00Z"
},
"created_at": "2025-02-10T03:00:00Z",
"updated_at": "2025-02-24T04:15:00Z"
}
],
"total": 1,
"page": 1,
"size": 20,
"pages": 1
}{
"detail": "Invalid API key"
}{
"detail": "Access denied to this campaign"
}/v1/quests/{quest_id}Get a fully expanded quest by ID, including objectives, progress log entries, related entity refs, and session provenance.
Headers
x-api-key*— Your API keyRequest Example
curl -H "x-api-key: YOUR_API_KEY" https://api.myarchivist.ai/v1/quests/quest_123Responses
{
"id": "quest_123",
"campaign_id": "camp_abc123",
"order_index": 2,
"quest_name": "Recover the Azure Sigil",
"quest_giver": "Aria Voss",
"quest_giver_id": "char_123",
"quest_category": "main",
"status": "in-progress",
"success_definition": "Return the sigil to Aria Voss.",
"failure_conditions": "The sigil is destroyed or delivered to House Merrow.",
"next_action": "Question the ferryman at Blackwake.",
"resolution": null,
"objective_count": 3,
"completed_objective_count": 1,
"progress_entry_count": 2,
"objectives": [
{ "id": "obj_1", "text": "Reach Blackwake alive", "status": "completed", "order": 1 },
{ "id": "obj_2", "text": "Find who bought the sigil", "status": "in-progress", "order": 2 },
{ "id": "obj_3", "text": "Recover the sigil", "status": "pending", "order": 3 }
],
"progress_log": [
"The party tracked the smugglers to Blackwake.",
"A ferryman claimed House Merrow paid for a sealed relic case."
],
"progress_log_entries": [
{
"id": "prog_1",
"text": "The party tracked the smugglers to Blackwake.",
"order": 1,
"session_id": "sess_002",
"session_number": 5,
"session_title": "Salt and Smoke",
"session_date": "2025-02-17T02:30:00Z"
}
],
"related_characters": ["Captain Nera", "Dovan Pike — suspect buyer"],
"related_factions": ["House Merrow"],
"related_locations": ["Blackwake Docks"],
"related_items": ["Azure Sigil"],
"related_entity_refs": [
{
"id": "qe_1",
"entity_type": "character",
"entity_id": "char_456",
"entity_name_snapshot": "Captain Nera",
"label": "Captain Nera",
"order": 1
},
{
"id": "qe_2",
"entity_type": "item",
"entity_id": "item_123",
"entity_name_snapshot": "Azure Sigil",
"label": "Azure Sigil",
"order": 4
}
],
"first_session": {
"id": "sess_001",
"number": 4,
"title": "Ashes on the Water",
"session_date": "2025-02-10T02:30:00Z"
},
"last_session": {
"id": "sess_003",
"number": 6,
"title": "The Ferryman's Price",
"session_date": "2025-02-24T02:30:00Z"
},
"created_at": "2025-02-10T03:00:00Z",
"updated_at": "2025-02-24T04:15:00Z"
}{
"detail": "Quest not found"
}/v1/questsCreate a manual quest. `quest_giver` must match an approved character or faction in the same campaign. If `objectives` are supplied, quest status is recomputed from objective statuses. `related_entity_refs` accepts explicit entity IDs; the convenience arrays (`related_characters`, `related_factions`, `related_locations`, `related_items`) are also accepted.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
campaign_id*— Campaign IDquest_name*— Quest titlequest_giver— Must resolve to an approved character or faction in this campaignquest_category— main | side | faction | personal | n/astatus— planned | in-progress | blocked | failed | done | n/a; ignored when objectives are suppliedsuccess_definition— How the quest succeedsfailure_conditions— How the quest failsnext_action— Immediate next stepresolution— Outcome or conclusion textobjectives— Array of strings or { text, status } objectsprogress_log— Array of progress stringsrelated_characters— Convenience array of related character labelsrelated_factions— Convenience array of related faction labelsrelated_locations— Convenience array of related location labelsrelated_items— Convenience array of related item labelsrelated_entity_refs— Array of { entity_type, entity_id?, entity_name_snapshot?, label? } refsRequest Example
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"campaign_id": "camp_abc123",
"quest_name": "Recover the Azure Sigil",
"quest_giver": "Aria Voss",
"quest_category": "main",
"objectives": [
{ "text": "Find who bought the sigil", "status": "in-progress" },
{ "text": "Recover the sigil", "status": "pending" }
],
"progress_log": [
"The party tracked the smugglers to Blackwake."
],
"related_entity_refs": [
{ "entity_type": "item", "entity_id": "item_123", "label": "Azure Sigil" },
{ "entity_type": "location", "entity_name_snapshot": "Blackwake Docks", "label": "Blackwake Docks" }
]
}' \
https://api.myarchivist.ai/v1/questsResponses
{
"id": "quest_new456",
"campaign_id": "camp_abc123",
"order_index": 7,
"quest_name": "Recover the Azure Sigil",
"quest_giver": "Aria Voss",
"quest_giver_id": "char_123",
"quest_category": "main",
"status": "in-progress",
"success_definition": null,
"failure_conditions": null,
"next_action": null,
"resolution": null,
"objectives": [
{ "id": "obj_new1", "text": "Find who bought the sigil", "status": "in-progress", "order": 1 },
{ "id": "obj_new2", "text": "Recover the sigil", "status": "pending", "order": 2 }
],
"progress_log": ["The party tracked the smugglers to Blackwake."],
"progress_log_entries": [
{ "id": "prog_new1", "text": "The party tracked the smugglers to Blackwake.", "order": 1, "session_id": null, "session_number": null, "session_title": null, "session_date": null }
],
"related_characters": [],
"related_factions": [],
"related_locations": ["Blackwake Docks"],
"related_items": ["Azure Sigil"],
"related_entity_refs": [
{ "id": "qe_new1", "entity_type": "item", "entity_id": "item_123", "entity_name_snapshot": "Azure Sigil", "label": "Azure Sigil", "order": 1 },
{ "id": "qe_new2", "entity_type": "location", "entity_id": null, "entity_name_snapshot": "Blackwake Docks", "label": "Blackwake Docks", "order": 2 }
],
"first_session": null,
"last_session": null,
"created_at": "2025-03-01T05:00:00Z",
"updated_at": "2025-03-01T05:00:00Z"
}{
"detail": "Quest giver must match an approved character or faction in this campaign"
}{
"detail": "Write access denied to this campaign"
}/v1/quests/{quest_id}Partially update a quest. Omitted fields are left untouched. Supplying `objectives` replaces the full objective list and recomputes quest status from those objective statuses. Supplying `related_entity_refs` replaces the full related-entity set; the type-specific arrays replace only their matching type bucket.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
quest_name— Updated quest titlequest_giver— Updated quest giver; must still resolve to an approved character or factionquest_category— Updated categorystatus— Updated status when objectives are omittedsuccess_definition— Updated success definitionfailure_conditions— Updated failure conditionsnext_action— Updated next actionresolution— Updated resolutionobjectives— Replacement objective listprogress_log— Replacement progress-log listrelated_characters— Replacement related character listrelated_factions— Replacement related faction listrelated_locations— Replacement related location listrelated_items— Replacement related item listrelated_entity_refs— Replacement full related-entity ref listRequest Example
curl -X PATCH \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"next_action": "Break into House Merrow's archive vault.",
"status": "blocked",
"related_factions": ["House Merrow", "Blackwake Dock Union"]
}' \
https://api.myarchivist.ai/v1/quests/quest_123Responses
{
"id": "quest_123",
"campaign_id": "camp_abc123",
"order_index": 2,
"quest_name": "Recover the Azure Sigil",
"quest_giver": "Aria Voss",
"quest_giver_id": "char_123",
"quest_category": "main",
"status": "blocked",
"success_definition": "Return the sigil to Aria Voss.",
"failure_conditions": "The sigil is destroyed or delivered to House Merrow.",
"next_action": "Break into House Merrow's archive vault.",
"resolution": null,
"objectives": [
{ "id": "obj_1", "text": "Reach Blackwake alive", "status": "completed", "order": 1 },
{ "id": "obj_2", "text": "Find who bought the sigil", "status": "in-progress", "order": 2 },
{ "id": "obj_3", "text": "Recover the sigil", "status": "pending", "order": 3 }
],
"progress_log": [
"The party tracked the smugglers to Blackwake.",
"A ferryman claimed House Merrow paid for a sealed relic case."
],
"progress_log_entries": [
{ "id": "prog_1", "text": "The party tracked the smugglers to Blackwake.", "order": 1, "session_id": "sess_002", "session_number": 5, "session_title": "Salt and Smoke", "session_date": "2025-02-17T02:30:00Z" }
],
"related_characters": ["Captain Nera"],
"related_factions": ["House Merrow", "Blackwake Dock Union"],
"related_locations": ["Blackwake Docks"],
"related_items": ["Azure Sigil"],
"related_entity_refs": [
{ "id": "qe_1", "entity_type": "character", "entity_id": "char_456", "entity_name_snapshot": "Captain Nera", "label": "Captain Nera", "order": 1 },
{ "id": "qe_2", "entity_type": "faction", "entity_id": null, "entity_name_snapshot": "House Merrow", "label": "House Merrow", "order": 2 },
{ "id": "qe_3", "entity_type": "faction", "entity_id": null, "entity_name_snapshot": "Blackwake Dock Union", "label": "Blackwake Dock Union", "order": 3 },
{ "id": "qe_4", "entity_type": "location", "entity_id": null, "entity_name_snapshot": "Blackwake Docks", "label": "Blackwake Docks", "order": 4 },
{ "id": "qe_5", "entity_type": "item", "entity_id": "item_123", "entity_name_snapshot": "Azure Sigil", "label": "Azure Sigil", "order": 5 }
],
"first_session": {
"id": "sess_001",
"number": 4,
"title": "Ashes on the Water",
"session_date": "2025-02-10T02:30:00Z"
},
"last_session": {
"id": "sess_003",
"number": 6,
"title": "The Ferryman's Price",
"session_date": "2025-02-24T02:30:00Z"
},
"created_at": "2025-02-10T03:00:00Z",
"updated_at": "2025-03-02T01:15:00Z"
}{
"detail": "quest_name cannot be empty"
}{
"detail": "Quest not found"
}/v1/quests/{quest_id}Delete a quest.
Headers
x-api-key*— Your API keyRequest Example
curl -X DELETE -H "x-api-key: YOUR_API_KEY" https://api.myarchivist.ai/v1/quests/quest_123Responses
(no content){
"detail": "Quest not found"
}Entities
/v1/entitiesLightweight compendium picker across characters, factions, locations, or items. Returns card-shaped results with pagination via limit (not size).
Headers
x-api-key*— Your API keyQuery Params
campaign_id*— Campaign IDtype*— characters, factions, locations, or itemssearch— Full-text search (name, aliases, type) with ILIKE fallbackpage— Page number (default 1)limit— Page size (default 20, max 100)Request Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/entities?campaign_id=abc123&type=characters&search=thor&limit=20"Responses
{
"results": [
{
"id": "char_123",
"name": "Thorin Ironforge",
"type": "PC",
"image": null
}
],
"hasMore": false,
"page": 1,
"pages": 1,
"total": 1
}Journal Entries
/v1/journalsList journal entries in a campaign. Results are filtered to entries the caller can see: public entries, entries authored by the caller, or entries explicitly shared with the caller. Use fields=card for lightweight items (id, title, summary, updated_at, folder_id).
Headers
x-api-key*— Your API keyQuery Params
campaign_id*— Campaign ID (world_id)page— Page numbersize— Page sizefields— Set to card for lightweight list items (id, title, summary, updated_at, folder_id)with_links— Preserve wikilink markup in contentRequest Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/journals?campaign_id=abc123"Responses
{
"data": [
{
"id": "journal_123",
"campaign_id": "camp_abc123",
"title": "World History",
"summary": "A high-level overview of the first age...",
"token_count": 3245,
"is_public": false,
"status": "draft",
"tags": ["history"],
"folder_id": "folder_789",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-16T10:30:00Z"
}
],
"total": 8,
"page": 1,
"size": 20,
"pages": 1
}{
"detail": "campaign_id is required"
}{
"detail": "Invalid API key"
}/v1/journals/{entry_id}Get a specific journal entry by ID including full content. Responses include the caller’s effective permission level for the entry.
Headers
x-api-key*— Your API keyRequest Example
curl -H "x-api-key: YOUR_API_KEY" https://api.myarchivist.ai/v1/journals/journal_123Responses
{
"id": "journal_123",
"campaign_id": "camp_abc123",
"title": "World History",
"summary": "A high-level overview of the first age...",
"content": "In the beginning, the gods created the world of Aetheria...",
"content_rich": { "root": { "type": "root", "version": 1, "children": [] } },
"tags": ["history"],
"token_count": 3245,
"is_public": false,
"status": "draft",
"folder_id": "folder_789",
"permission_level": "manage",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-16T10:30:00Z"
}{
"detail": "Journal entry not found"
}{
"detail": "Insufficient permissions"
}/v1/journalsCreate a new journal entry. `content_rich` is canonical when both rich and plain content are supplied, folder IDs must belong to the same world, creator-level manage permission is granted automatically, and wikilinks are extracted/synced from the saved content. Maximum 2,000,000 characters per request. Worlds are capped at 1,000,000 journal tokens; individual entries are capped at 50,000 tokens.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
world_id*— Campaign IDtitle*— Entry titlecontent— Plain text content (max 2M chars)content_rich— Lexical editor state JSON; takes precedence over content when both are presentcontent_metadata— Structured content metadatasummary— Short summarytags— Array of tag stringscover_image— Optional journal cover image URLis_pinned— Pin this entry in the UIis_public— Publicly visible entrystatus— draft | published | archivedpublished_at— Explicit published timestamp; otherwise set automatically when status becomes publishedarchived_at— Explicit archived timestamp; otherwise set automatically when status becomes archivedfolder_id— Folder IDRequest Example
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"world_id": "camp_abc123",
"title": "World History",
"content": "In the beginning, the gods created the world of Aetheria...",
"tags": ["history"],
"status": "draft"
}' \
https://api.myarchivist.ai/v1/journalsResponses
{
"success": true,
"id": "journal_new456"
}{
"detail": "This journal entry exceeds the per-entry token limit"
}{
"detail": "Write access denied to this world"
}{
"detail": "Journal folder not found"
}{
"detail": "Content too large; max 2000000 characters"
}/v1/journalsUpdate an existing journal entry. `content_rich` remains canonical, embeddings are regenerated when saved content changes, permissions can be added or removed, ownership can be reassigned with `author_id`, and journal wikilinks are re-synced from the saved content.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
id*— Journal entry IDtitle— Updated titlesummary— Updated summarycontent— Updated content (triggers re-embedding)content_rich— Updated Lexical JSON (takes precedence over content)content_metadata— Updated metadata objecttags— Updated tagscover_image— Updated cover image URLis_pinned— Updated pinned stateis_public— Updated visibilitystatus— Updated statuspublished_at— Updated published timestamparchived_at— Updated archived timestampfolder_id— Updated folderauthor_id— Transfer authorship to another user with world accesspermissions— Permission mutation object: { add: [{ user_id, level }], remove: [{ user_id }] }Request Example
curl -X PUT \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "journal_123",
"content": "Updated journal content with [[Cassius Traven]].",
"status": "published",
"permissions": {
"add": [{ "user_id": "user_456", "level": "comment" }],
"remove": []
}
}' \
https://api.myarchivist.ai/v1/journalsResponses
{
"success": true,
"id": "journal_123"
}{
"detail": "Insufficient permissions"
}{
"detail": "Journal entry not found"
}{
"detail": "Cannot remove the current owner from this journal entry."
}/v1/journalsDelete a journal entry and its associated embeddings. Requires manage permission on the entry.
Headers
x-api-key*— Your API keyQuery Params
id*— Journal entry IDRequest Example
curl -X DELETE -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/journals?id=journal_123"Responses
{
"success": true
}{
"detail": "Insufficient permissions"
}{
"detail": "Journal entry not found"
}Journal Folders
/v1/journal-foldersList journal folders for a campaign. Ordered by path and position for tree rendering.
Headers
x-api-key*— Your API keyQuery Params
campaign_id*— Campaign ID (world_id)Request Example
curl -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/journal-folders?campaign_id=abc123"Responses
{
"data": [
{
"id": "folder_123",
"campaign_id": "camp_abc123",
"parent_id": null,
"name": "Lore",
"path": "lore",
"description": null,
"position": 0,
"metadata": null,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-16T10:30:00Z"
}
]
}{
"detail": "Access denied to this world"
}{
"detail": "World not found"
}/v1/journal-folders/{folder_id}Get a specific journal folder by ID.
Headers
x-api-key*— Your API keyRequest Example
curl -H "x-api-key: YOUR_API_KEY" https://api.myarchivist.ai/v1/journal-folders/folder_123Responses
{
"id": "folder_123",
"campaign_id": "camp_abc123",
"parent_id": null,
"name": "Lore",
"path": "lore",
"description": null,
"position": 0,
"metadata": null,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-16T10:30:00Z"
}{
"detail": "Access denied to this world"
}{
"detail": "World not found"
}{
"detail": "Journal folder not found"
}/v1/journal-foldersCreate a new journal folder.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
world_id*— Campaign IDname*— Folder namepath*— Folder path (unique per campaign; normalized, no leading/trailing slashes)parent_id— Parent folder ID (must be in same campaign)description— Folder descriptionposition— Sort position within parentmetadata— Optional metadata JSONRequest Example
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"world_id": "camp_abc123",
"name": "Lore",
"path": "lore",
"position": 0
}' \
https://api.myarchivist.ai/v1/journal-foldersResponses
{
"success": true,
"id": "folder_456"
}{
"detail": "Invalid parent folder"
}{
"detail": "Folder path already exists for this campaign"
}{
"detail": "Write access denied to this world"
}{
"detail": "World not found"
}/v1/journal-foldersUpdate an existing journal folder.
Headers
x-api-key*— Your API keyContent-Type*— application/jsonRequest Body
id*— Journal folder IDname— Updated namepath— Updated path (unique per campaign; normalized, no leading/trailing slashes)parent_id— Updated parent folder ID (must be in same campaign)description— Updated descriptionposition— Updated positionmetadata— Updated metadata JSONRequest Example
curl -X PUT \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "folder_456",
"name": "Lore Docs",
"path": "lore/docs"
}' \
https://api.myarchivist.ai/v1/journal-foldersResponses
{
"success": true,
"id": "folder_456"
}{
"detail": "Folder cannot be its own parent"
}{
"detail": "Invalid parent folder"
}{
"detail": "Folder path already exists for this campaign"
}{
"detail": "Write access denied to this world"
}{
"detail": "World not found"
}{
"detail": "Journal folder not found"
}/v1/journal-foldersDelete a journal folder.
Headers
x-api-key*— Your API keyQuery Params
id*— Journal folder IDRequest Example
curl -X DELETE -H "x-api-key: YOUR_API_KEY" "https://api.myarchivist.ai/v1/journal-folders?id=folder_456"Responses
{
"success": true
}{
"detail": "Write access denied to this world"
}{
"detail": "World not found"
}{
"detail": "Journal folder not found"
}