|9 min read

By Arjav Jain · Co-founder, OpenOfficeAI

How to Give an AI Agent the Ability to Create Documents

LLM agents are great at reasoning, but when they need to hand back a spreadsheet or a report they usually dump a Markdown table into the chat. Here is the pattern that turns that into a real, shareable document — and the runnable code to add it to your agent today.

The fastest way to give an agent document output is a single tool that calls a document API and returns a URL. The agent decides when to use it, supplies the data, and your code runs the request. The result is a link the user can open and edit — not 2,000 tokens of pipe-delimited text the model had to generate character by character.

Why a table-in-chat is the wrong output

Generating a large table token-by-token is slow, expensive, error-prone, and produces something the user still has to copy, clean, and paste into a real spreadsheet. Worse, the model often truncates or mis-aligns columns. A document API removes all of that: the agent sends structured rows, and a hosted file comes back with a shareable link. For the full reasoning, see why AI agents need a document API.

The pattern: one tool, one POST, one link

Every framework below uses the same three-step shape: (1) define a tool with a title androws input, (2) inside it, POST to a document API, (3) return the URL. Here is the bare API call the tool wraps:

The underlying call (any language)
POST https://openofficeai.com/api/v1/sheets
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{ "title": "Q3 Revenue", "sheets": [{ "rows": [
  ["Month", "Revenue"], ["July", 48200], ["August", 61900]
] }] }

# → { "url": "https://openofficeai.com/s/..." }

OpenAI function calling

Declare a function tool; when the model calls it, run the request and feed the URL back.

Python
tools = [{
  "type": "function",
  "function": {
    "name": "create_spreadsheet",
    "description": "Create a shareable spreadsheet; returns a URL.",
    "parameters": {"type": "object", "properties": {
      "title": {"type": "string"},
      "rows": {"type": "array", "items": {"type": "array"}}},
      "required": ["title", "rows"]}
  }
}]

def create_spreadsheet(title, rows):
    r = requests.post("https://openofficeai.com/api/v1/sheets",
        headers={"Authorization": "Bearer YOUR_API_KEY"},
        json={"title": title, "sheets": [{"rows": rows}]})
    return r.json()["url"]

Full walkthrough: OpenAI function calling integration.

Claude tool use

Claude proposes a tool_use block; you execute it and return a tool_result with the link.

Python (Anthropic)
tools = [{
  "name": "create_spreadsheet",
  "description": "Create a shareable spreadsheet and return its URL.",
  "input_schema": {"type": "object", "properties": {
    "title": {"type": "string"},
    "rows": {"type": "array", "items": {"type": "array"}}},
    "required": ["title", "rows"]}
}]
# On tool_use, POST to /api/v1/sheets and return the url as tool_result.

Full walkthrough: Claude tool use / MCP integration.

LangChain & CrewAI

Framework agents wrap the same call in their tool decorator.

LangChain (Python)
from langchain_core.tools import tool

@tool
def create_spreadsheet(title: str, rows: list) -> str:
    """Create a shareable spreadsheet; first row is the header. Returns a URL."""
    r = requests.post("https://openofficeai.com/api/v1/sheets",
        headers={"Authorization": "Bearer YOUR_API_KEY"},
        json={"title": title, "sheets": [{"rows": rows}]})
    return r.json()["url"]

Same idea in LangChain, CrewAI, LlamaIndex, PydanticAI, and the Vercel AI SDK — see the full integrations directory for copy-paste code per framework.

Four practices that make agent tool calls reliable

  • Keep the signature flat. A title + rows (array of arrays) shape calls reliably; deeply nested schemas cause malformed calls.
  • Write the description for the model. State that the first row is the header and the tool returns a URL — the model uses this to format data correctly.
  • Keep the API key server-side. The tool executes in your code, never inside the model, so the key stays in an environment variable.
  • Return just the URL. One short string is easy for the model to hand back; avoid returning large payloads it then re-summarizes.

Documents, not just spreadsheets

The same pattern produces formatted documents: add a second tool that posts to /api/v1/docs with a content array of headings and paragraphs, and your agent can return reports, memos, and proposals as shareable links — downloadable as PDF or DOCX. See the API docs for the full schema.

FAQ

Why not just have the agent write a CSV?

A CSV string still has to be saved, opened, and shared by the user, and large tables burn tokens and risk truncation. A document API returns a finished, hosted, editable artifact in one call.

Does this work with local models?

Yes — any tool-capable model works, including local ones via Ollama. The document call is the only thing that leaves your machine.

How do I start?

Grab a free API key (500 calls/month), pick your framework in the integrations directory, and paste in the tool.

Give your agent document output

Free tier: 500 API calls/month, no card required.