Skip to main content

Python SDK for SurfaceDocs - Save LLM-generated documents

Project description

SurfaceDocs Python SDK

Save LLM-generated documents to SurfaceDocs.

Installation

pip install surfacedocs

Quick Start

from surfacedocs import SurfaceDocs, DOCUMENT_SCHEMA, SYSTEM_PROMPT
from openai import OpenAI

# Initialize clients
openai = OpenAI()
docs = SurfaceDocs(api_key="sd_live_...")

# Generate a document with your LLM
response = openai.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": "Document our REST API authentication flow"},
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "surfacedocs_document",
            "schema": DOCUMENT_SCHEMA,
        },
    },
)

# Save to SurfaceDocs
result = docs.save(response.choices[0].message.content)
print(result.url)  # https://app.surfacedocs.dev/d/abc123

What's Included

The SDK provides three exports:

Export Type Purpose
DOCUMENT_SCHEMA dict JSON schema for LLM structured output
SYSTEM_PROMPT str Instructions for LLM to generate documents
SurfaceDocs class HTTP client to save documents

API Reference

SurfaceDocs

from surfacedocs import SurfaceDocs

# Initialize with API key
client = SurfaceDocs(api_key="sd_live_...")

# Or use environment variable
# export SURFACEDOCS_API_KEY=sd_live_...
client = SurfaceDocs()

save(content, folder_id=None)

Save a document from LLM output.

# From JSON string
result = client.save(response.choices[0].message.content)

# From dict
result = client.save({
    "title": "My Document",
    "blocks": [{"type": "paragraph", "content": "Hello world"}]
})

# To specific folder
result = client.save(content, folder_id="folder_abc123")

save_raw(title, blocks, folder_id=None, metadata=None)

Save a document with explicit parameters.

result = client.save_raw(
    title="API Documentation",
    blocks=[
        {"type": "heading", "content": "Authentication", "metadata": {"level": 1}},
        {"type": "paragraph", "content": "Use Bearer tokens for auth."},
        {"type": "code", "content": "curl -H 'Authorization: Bearer ...'", "metadata": {"language": "bash"}},
    ],
    metadata={"source": "doc-generator", "version": "1.0"},
)

get_document(document_id)

Retrieve a document by ID.

doc = client.get_document("doc_abc123")
print(doc.title)                # "API Documentation"
print(doc.blocks[0].type)      # "heading"
print(doc.blocks[0].content)   # "Authentication"

delete_document(document_id)

Delete a document by ID.

client.delete_document("doc_abc123")

create_folder(name, parent_id=None)

Create a new folder.

folder = client.create_folder("API Docs")
print(folder.id)    # "fld_abc123"
print(folder.name)  # "API Docs"

# Create a subfolder
subfolder = client.create_folder("v2", parent_id=folder.id)

list_folders(parent_id=None)

List folders, optionally filtered by parent.

# List all root folders
folders = client.list_folders()

# List subfolders of a specific folder
subfolders = client.list_folders(parent_id="fld_abc123")

SaveResult

Both save() and save_raw() return a SaveResult:

result.id        # "doc_abc123"
result.url       # "https://app.surfacedocs.dev/d/doc_abc123"
result.folder_id # "folder_xyz"

Document

Returned by get_document():

doc.id            # "doc_abc123"
doc.url           # "https://app.surfacedocs.dev/d/doc_abc123"
doc.folder_id     # "folder_xyz"
doc.title         # "My Document"
doc.content_type  # "markdown"
doc.visibility    # "private"
doc.blocks        # list[Block]
doc.metadata      # dict or None
doc.created_at    # "2024-01-01T00:00:00Z"
doc.updated_at    # "2024-01-02T00:00:00Z"

Block

Each document contains a list of Block objects:

block.id        # "blk_abc123"
block.order     # 0
block.type      # "heading", "paragraph", "code", etc.
block.content   # "Hello world"
block.metadata  # {"level": 1} or None

Folder

Returned by create_folder() and list_folders():

folder.id         # "fld_abc123"
folder.name       # "API Docs"
folder.parent_id  # "fld_parent" or None
folder.path       # "/API Docs"
folder.depth      # 0
folder.created_at # "2024-01-01T00:00:00Z"

DOCUMENT_SCHEMA

JSON schema dict for LLM structured output. Pass directly to your LLM provider.

SYSTEM_PROMPT

System prompt string to instruct LLMs on document format.

from surfacedocs import SYSTEM_PROMPT

messages = [
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": "Document the login flow"},
]

Block Types

Documents are composed of blocks:

Type Description Metadata
heading Section header level (1-6)
paragraph Body text -
code Code block language (optional)
list Bullet/numbered list listType ("bullet" or "ordered")
quote Block quote -
table Markdown table -
image Image url (required), alt (optional)
divider Horizontal rule -

Text content supports inline markdown: **bold**, *italic*, `code`, [link](url)

Error Handling

from surfacedocs import (
    SurfaceDocs,
    SurfaceDocsError,
    AuthenticationError,
    DocumentNotFoundError,
    FolderNotFoundError,
    ValidationError,
)

try:
    result = client.save(content)
except AuthenticationError:
    print("Invalid API key")
except ValidationError as e:
    print(f"Invalid document: {e}")
except SurfaceDocsError as e:
    print(f"API error: {e}")

try:
    doc = client.get_document("doc_abc123")
except DocumentNotFoundError:
    print("Document does not exist")

try:
    folder = client.create_folder("Docs", parent_id="fld_nonexistent")
except FolderNotFoundError:
    print("Parent folder does not exist")

Environment Variables

# API key (alternative to passing in code)
export SURFACEDOCS_API_KEY=sd_live_...

Examples

OpenAI

from surfacedocs import SurfaceDocs, DOCUMENT_SCHEMA, SYSTEM_PROMPT
from openai import OpenAI

openai = OpenAI()
docs = SurfaceDocs()

response = openai.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": "Write documentation for user authentication"},
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {"name": "document", "schema": DOCUMENT_SCHEMA},
    },
)

result = docs.save(response.choices[0].message.content)
print(f"Saved: {result.url}")

Anthropic

Using Claude's structured outputs with tool use:

from surfacedocs import SurfaceDocs, DOCUMENT_SCHEMA, SYSTEM_PROMPT
import anthropic

client = anthropic.Anthropic()
docs = SurfaceDocs()

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=4096,
    system=SYSTEM_PROMPT,
    messages=[
        {"role": "user", "content": "Write documentation for user authentication"},
    ],
    tools=[{
        "name": "create_document",
        "description": "Create a structured document",
        "input_schema": DOCUMENT_SCHEMA,
    }],
    tool_choice={"type": "tool", "name": "create_document"},
)

tool_use = next(b for b in response.content if b.type == "tool_use")
result = docs.save(tool_use.input)
print(f"Saved: {result.url}")

Google Gemini

Using Gemini's structured output with JSON schema:

from surfacedocs import SurfaceDocs, DOCUMENT_SCHEMA, SYSTEM_PROMPT
import google.generativeai as genai

genai.configure(api_key="...")
docs = SurfaceDocs()

model = genai.GenerativeModel(
    model_name="gemini-2.0-flash",
    system_instruction=SYSTEM_PROMPT,
    generation_config=genai.GenerationConfig(
        response_mime_type="application/json",
        response_schema=DOCUMENT_SCHEMA,
    ),
)

response = model.generate_content("Write documentation for user authentication")
result = docs.save(response.text)
print(f"Saved: {result.url}")

Manual Document

from surfacedocs import SurfaceDocs

docs = SurfaceDocs()

result = docs.save_raw(
    title="Meeting Notes",
    blocks=[
        {"type": "heading", "content": "Action Items", "metadata": {"level": 1}},
        {"type": "list", "content": "- Review PR #123\n- Update docs", "metadata": {"listType": "bullet"}},
        {"type": "divider", "content": ""},
        {"type": "paragraph", "content": "Next meeting: Monday 10am"},
    ],
    metadata={"source": "meeting-bot"},
)

Managing Documents

from surfacedocs import SurfaceDocs, DocumentNotFoundError

docs = SurfaceDocs()

# Save a document
result = docs.save_raw(
    title="API Guide",
    blocks=[{"type": "paragraph", "content": "Welcome to the API."}],
)

# Retrieve it
doc = docs.get_document(result.id)
print(doc.title)  # "API Guide"

# Delete it
docs.delete_document(result.id)

Managing Folders

from surfacedocs import SurfaceDocs

docs = SurfaceDocs()

# Create a folder hierarchy
parent = docs.create_folder("Engineering")
child = docs.create_folder("Backend", parent_id=parent.id)

# List root folders
for folder in docs.list_folders():
    print(folder.name)

# List subfolders
for folder in docs.list_folders(parent_id=parent.id):
    print(f"  {folder.name}")

# Save a document to a folder
result = docs.save_raw(
    title="Architecture Overview",
    blocks=[{"type": "paragraph", "content": "Our system uses..."}],
    folder_id=child.id,
)

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

surfacedocs-0.3.0.tar.gz (50.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

surfacedocs-0.3.0-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file surfacedocs-0.3.0.tar.gz.

File metadata

  • Download URL: surfacedocs-0.3.0.tar.gz
  • Upload date:
  • Size: 50.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.0

File hashes

Hashes for surfacedocs-0.3.0.tar.gz
Algorithm Hash digest
SHA256 a2176b3ffaf6e1a403a3c754120ee47502ee0183989d81facb66281a1b05a739
MD5 bd96f35141c0f34d5d5368ec143a90f8
BLAKE2b-256 070f2e06bdf072214d126ccd40822f58eeb1de15344b45f00ebb251597678ccc

See more details on using hashes here.

File details

Details for the file surfacedocs-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for surfacedocs-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b4254811731d02da1d0687f46a575c7c4e06776ef612b883545330e02f55d799
MD5 b143e6a9cdc829d1495e872e294f51e1
BLAKE2b-256 b0dbb7d0f715b7f06fa318987e737c3f52cae22d6d9645720ecdd8cb46ecd09f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page