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 by updated_at descending, paginated with limit and offset. Filter by metadata with type and parent_document_id. Pass include_content=true to include content_html in each item; omit it for lightweight poll responses.
  • Create: POST /api/v1/docgen/documents — requires title; optionally seed content_html, bind a workflow_id, attach a session_id, and set metadata.
  • Get / update / delete: GET, PATCH, DELETE /api/v1/docgen/documents/{id}. PATCH updates the title and/or shallow-merges a metadata patch; DELETE is 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} (with content_html).
  • Save a manual version: POST /api/v1/docgen/documents/{id}/versions. If the document changed since you loaded it, the save returns 409 Conflict so 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, plus GET, 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/sessions and .../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 with PATCH .../comments/threads/{threadId}, and delete a thread (and its replies) with DELETE.
  • Replies: POST .../comments/threads/{threadId}/replies; edit with PATCH and soft-delete with DELETE .../replies/{commentId}.
  • Across documents: GET /api/v1/docgen/comments/threads batch-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 with POST /api/v1/docgen/templates/upload.
  • Get / update / delete: GET, PATCH, DELETE /api/v1/docgen/templates/{id}.
  • Render assets: GET /api/v1/docgen/templates/{id}/html and .../thumbnail.
  • Manage: set the workspace default with PUT .../templates/{id}/set-default, duplicate with POST .../duplicate, and re-run extraction with POST .../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

Last updated: Oct 12, 2026