This case study connects an existing AI story SaaS called StoryShelf to AgentChat. A user creates a story through conversation; the agent writes structured story and chapter records through StoryShelf APIs, starts illustration jobs, inspects returned image URLs, and updates the story. StoryShelf then reads its own database and displays the result in its normal library and editor.
The important detail is that AgentChat does not become the story database and StoryShelf does not parse a long assistant answer into records. The agent performs the same controlled operations that the StoryShelf application already understands.
Step 1: expose the StoryShelf capability pack
POST /v1/stories
GET /v1/stories/{story_id}
POST /v1/stories/{story_id}/chapters
PUT /v1/stories/{story_id}/chapters/{chapter_id}
POST /v1/image-jobs
GET /v1/image-jobs/{job_id}
PUT /v1/stories/{story_id}/cover Each create response returns stable IDs. Story and chapter writes are idempotent with a client_request_id so a retry does not create duplicates. Image jobs are asynchronous and return poll_after_seconds. The completed image response contains asset_id and image_url, never base64 data.
Step 2: provision the StoryShelf document
POST /api/api-documents
Authorization: Bearer ac_live_AGENT_CHAT_KEY
Content-Type: application/json
{
"title": "StoryShelf Story and Illustration API",
"description": "Create stories and chapters, generate illustrations, and attach image assets.",
"content": "# StoryShelf API\nBase URL: https://stories.example.com/v1\n\nCreate the story record before chapters and preserve every returned ID. Write chapters one at a time.\n\nPOST /stories body: client_request_id, title, audience, style, premise, outline[]. Response: story_id, status.\n\nPOST /stories/{story_id}/chapters body: client_request_id, position, title, content. Response: chapter_id, revision.\n\nPOST /image-jobs body: client_request_id, story_id, chapter_id optional, prompt, aspect_ratio. Response: job_id, status, poll_after_seconds. Start the job in one turn; sleep and poll in a later turn.\n\nGET /image-jobs/{job_id} returns status and, when succeeded, asset_id and image_url. Call view_image when visual inspection is useful.\n\nPUT /stories/{story_id}/cover body: asset_id.\n\nNever publish a draft unless the user explicitly asks."
} The document contains workflow rules that cannot be inferred reliably from an OpenAPI path list: preserve IDs, separate asynchronous turns, inspect image URLs, and do not publish implicitly.
Step 3: create the user’s story session
POST /api/agent/sessions
Authorization: Bearer ac_live_AGENT_CHAT_KEY
Content-Type: application/json
{
"title": "Story creator for user_88",
"llm_config_id": "creative_model_uuid",
"api_document_ids": ["story_api_document_uuid"],
"system_prompt": "Help the user plan and create stories. Save approved creative work through StoryShelf APIs. Keep the user informed when image jobs are running.",
"max_turns": 25,
"tool_timeout_seconds": 180,
"tool_result_max_chars": 12000,
"host_headers": [
{ "host": "stories.example.com", "header_key": "Authorization", "header_value": "Bearer short_lived_story_user_token" }
]
} StoryShelf creates this session from its backend and stores the returned session UUID beside the user’s conversation. The short-lived header limits all story API reads and writes to user_88. It is injected at request time and is not included in LLM context or AgentChat API responses.
Step 4: let the user create through chat
POST /api/agent/sessions/session_uuid/chat
Authorization: Bearer ac_live_AGENT_CHAT_KEY
Content-Type: application/json
{
"message": "Create a three-chapter story for ages 8-10 about a child who repairs a lighthouse for lost spaceships. Use a gentle watercolor style and make an illustrated cover."
} AgentChat stores the user message and returns processing. The StoryShelf UI begins polling messages immediately instead of holding the chat request open.
The concrete tool-call sequence
- The agent plans the title, premise, audience, style, and three-part outline.
- It calls POST /stories and receives story_id story_123.
- It writes chapter one, two, and three through separate API calls and preserves each chapter_id.
- It creates an image prompt consistent with the saved story style and calls POST /image-jobs.
- Because the job must complete asynchronously, the next model turn calls sleep using poll_after_seconds, then a later request polls GET /image-jobs/job_7.
- When the response contains image_url, the agent calls view_image so the illustration enters context as native multimodal input.
- If the image matches the story, it calls PUT /stories/story_123/cover with asset_id. If not, it submits a revised prompt as a new job.
- It finishes with the saved story ID, chapter summary, and cover status.
Tool results are persisted as they complete, but the next model request receives concurrently executed tool results in original tool-call order. The API document should discourage dependent writes in the same parallel batch when one call needs an ID returned by another.
Step 5: merge AgentChat messages correctly
GET /api/agent/sessions/session_uuid/messages?after_id=msg_uuid&after_revision=6
Authorization: Bearer ac_live_AGENT_CHAT_KEY The StoryShelf client replaces a message only when its revision increases. It can render assistant text, a compact illustration-job activity, and a final image preview. Failed partial assistant messages remain visible but are excluded from future LLM context by AgentChat.
If image generation uses the remaining turn budget, AgentChat returns a state whose next_action is continue. StoryShelf shows a Continue button and calls POST /api/agent/sessions/session_uuid/continue. The new run receives a fresh turn budget and continues from durable messages.
Step 6: render the story from the StoryShelf database
POST /stories, POST /chapters, and PUT /cover already wrote validated records to StoryShelf. The library page queries StoryShelf tables and shows the new title and cover. The editor loads chapters by story_id. The chat transcript is not parsed to reconstruct the story.
This design makes partial completion safe. If the cover job fails, the three chapters still exist. The user can continue the same AgentChat session and ask for a new cover without generating the story again.
What must be tested before launch
- Retry story and chapter creation with the same client_request_id and verify no duplicate records appear.
- Return an image job failure and verify the agent preserves the story and explains the recoverable step.
- Stop during image polling, then continue the session and finish the cover.
- Attempt to read another user’s story ID with the injected token and verify StoryShelf returns 403 or not found.
- Return an invalid image URL and verify the agent reports the tool error instead of embedding it as text.
- Refresh the StoryShelf library from its own database after every completed mutation.