Document Generation API
The Document Generation API powers Ema's long-form document workflows: proposals, RFP responses, and other structured documents an AI Employee writes and a person edits. It stores documents and their full version history, generates content by triggering a workflow run, supports an inline chat assistant for editing and Q&A, and manages a reusable template library. The docgen service is mounted at /api/v1/docgen, so every path below is that prefix plus the path shown.
All requests require a JWT or workspace API key. Examples use https://your-tenant.ema.ai as the host.
Documents are scoped to the caller. Each document belongs to the user and workspace that created it. A document that belongs to another workspace returns 404. There is no existence leak across workspaces.
Documents
A document holds a title, HTML content, and JSON metadata, and tracks a current version number.
- List:
GET /api/v1/docgen/documents— ordered byupdated_atdescending, paginated withlimitandoffset. Filter by metadata withtypeandparent_document_id. Passinclude_content=trueto includecontent_htmlin each item; omit it for lightweight poll responses. - Create:
POST /api/v1/docgen/documents— requirestitle; optionally seedcontent_html, bind aworkflow_id, attach asession_id, and setmetadata. - Get / update / delete:
GET,PATCH,DELETE /api/v1/docgen/documents/{id}.PATCHupdates thetitleand/or shallow-merges ametadatapatch;DELETEis a soft delete.
POST https://your-tenant.ema.ai/api/v1/docgen/documents
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json
{
"title": "Acme RFP response",
"workflow_id": "3f7a...",
"metadata": { "type": "rfp_section" }
}
Bulk create
POST /api/v1/docgen/documents/bulk creates a parent document plus N child sections in one transaction. Provide title and children to create a new parent; provide parent_document_id instead to add children to an existing parent (a missing or cross-workspace parent returns 404).
Versions
Every change to a document is recorded as a version, so you can review history and roll back.
- List versions:
GET /api/v1/docgen/documents/{id}/versions(without content). - Get one version:
GET /api/v1/docgen/documents/{id}/versions/{versionNumber}(withcontent_html). - Save a manual version:
POST /api/v1/docgen/documents/{id}/versions. If the document changed since you loaded it, the save returns409 Conflictso you don't overwrite a newer edit. - Restore:
POST /api/v1/docgen/documents/{id}/restore/{versionNumber}writes the chosen version's content back as the latest version.
Each Version records a source (ai_create, ai_edit, manual_save, or restore) and a change_description, plus the user or workflow run that produced it.
Generate content
POST /api/v1/docgen/documents/{id}/generate starts a workflow run scoped to a document and returns immediately. The workflow writes content back as a new version.
POST https://your-tenant.ema.ai/api/v1/docgen/documents/a1b2.../generate
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json
{
"section_title": "Executive summary",
"instructions": "Summarize our proposed approach in two paragraphs.",
"knowledge_base_ids": ["9b2c..."]
}
The body is optional. The workflow to run is resolved in order: the request body's workflow_id, then the document's bound workflow_id, otherwise a 422. Your JWT or API key is forwarded to the workflow service, so the run inherits your workspace and role. The response is 202 Accepted:
{
"run_id": "5e6f...",
"workflow_id": "3f7a...",
"stream_url": "/runs/5e6f.../stream"
}
Tail progress on the Workflow API SSE stream at the returned stream_url.
Outlines. POST /api/v1/docgen/generate-document-outline turns a user-supplied HTML outline (input_type: MANUAL) or the markdown of an uploaded document (input_type: UPLOAD) into a structured list of sections you can then create with bulk create. The AI-generated outline path is not yet available in this platform.
update_document tool
The update_document tool used by AI Employees to write back into a document follows replacement semantics. Every call replaces content, never appends.
| Parameter | Type | Description |
|---|---|---|
document_id |
string | The ID of the document to update. |
target_section_id |
string | The id attribute of the <section> to update. When provided, content_html replaces that section's entire inner HTML. |
content_html |
string | The HTML content. If target_section_id is set, this is the section's complete new inner HTML (include the full <table> element for table content; bare rows are rejected). Otherwise, this is the full document HTML. |
change_description |
string | A short description of the change, recorded in the version history. |
Section updates
When target_section_id is provided, content_html replaces the named section's entire inner HTML. Send the section's complete new content every time. The server does not merge or append.
Table markup must be complete. The HTML fragment parser silently drops table-structure tags (<tr>, <td>, <thead>, etc.) that appear outside a <table> wrapper, keeping only their text content. To prevent silent data loss, the server counts table-structure tags in the submitted markup and compares them against the parsed result. Any loss returns a VALIDATION_ERROR and the write is rejected. Nothing is stored. The error message instructs the caller to resend the section's complete inner HTML including the <table> element.
To add rows to a table in a section, resend the whole section including every existing row plus the new ones.
PATCH https://your-tenant.ema.ai/api/v1/docgen/documents/a1b2...
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json
{
"target_section_id": "results",
"content_html": "<h2>Results</h2><table><thead><tr><th>No</th><th>ID</th></tr></thead><tbody><tr><td>1</td><td>R-01</td></tr><tr><td>2</td><td>R-02</td></tr></tbody></table>",
"change_description": "Added row R-02 to results table"
}
Bare rows are rejected. Sending <tr><td>…</td></tr> without a surrounding <table> element returns a validation error. Always wrap table content in a complete <table> element.
Full document updates
When target_section_id is omitted, content_html must be the complete document HTML.
Export
POST /api/v1/docgen/export enqueues an export job for an ordered list of document IDs, then you poll for the result.
POST https://your-tenant.ema.ai/api/v1/docgen/export
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json
{
"document_ids": ["a1b2...", "c3d4..."],
"format": "html"
}
The response is 202 Accepted with a job. Poll GET /api/v1/docgen/export/{jobId} until status is complete (then read download_url) or error. The format field currently supports html; pdf and pptx return 501.
Chat assistant
Each document has an inline assistant for editing and questions. There is also a document-less Q&A assistant scoped to a workspace's Knowledge Bases.
- Document sessions:
GET,POST /api/v1/docgen/documents/{docId}/chat/sessions, plusGET,PATCH,DELETE .../chat/sessions/{id}. - Document messages:
GET,POST /api/v1/docgen/documents/{docId}/chat/sessions/{id}/messages. Sending a message returns the assistant's reply synchronously. - Workspace KB Q&A sessions: the same shapes under
/api/v1/docgen/chat/sessionsand.../chat/sessions/{id}/messages, with no document context.
POST https://your-tenant.ema.ai/api/v1/docgen/documents/a1b2.../chat/sessions/7d1e.../messages
Authorization: Bearer eyJhbGciOiJSUzI1Ni...
Content-Type: application/json
{
"content": "Tighten this paragraph and remove the jargon.",
"mode": "edit",
"selected_html": "<p>...</p>"
}
mode is edit (the default in-document writing assistant) or qa (Knowledge Base Q&A, no rewrite). For an edit request that rewrites a selection, the assistant message carries a replacement_text. On the document-less Q&A path, set mode: qa and a workflow_id to scope the Knowledge Base search.
Comments
Documents support threaded comments for review.
- Threads:
POST,GET /api/v1/docgen/documents/{docId}/comments/threads; resolve or reopen withPATCH .../comments/threads/{threadId}, and delete a thread (and its replies) withDELETE. - Replies:
POST .../comments/threads/{threadId}/replies; edit withPATCHand soft-delete withDELETE .../replies/{commentId}. - Across documents:
GET /api/v1/docgen/comments/threadsbatch-lists threads across several documents.
Template library
Templates standardize the look of generated documents. A template is DOCX or PPTX and carries a generation mode (slot_fill or html_rewrite).
- List / create:
GET,POST /api/v1/docgen/templates; upload a source file withPOST /api/v1/docgen/templates/upload. - Get / update / delete:
GET,PATCH,DELETE /api/v1/docgen/templates/{id}. - Render assets:
GET /api/v1/docgen/templates/{id}/htmland.../thumbnail. - Manage: set the workspace default with
PUT .../templates/{id}/set-default, duplicate withPOST .../duplicate, and re-run extraction withPOST .../re-extract. - PPTX slides: list, reorder, fetch, edit, and delete slides under
/api/v1/docgen/templates/{id}/slides.
Section profiles describe how a workspace labels document sections. Manage them with GET, POST /api/v1/docgen/section-profiles (and GET, PATCH, DELETE .../section-profiles/{id}), and classify a set of section titles against them with POST /api/v1/docgen/section-profiles/classify.
What's next
- Workflow API — the workflow that document generation triggers, and how to stream its run.
- Knowledge & Ingestion API — the Knowledge Bases a generation or Q&A request searches.