OpenPen

Developer docs

Use OpenPen as a final writing layer.

The Non-AI Writer API compiles messy agent context into a writing brief, then creates an async draft run your workflow can poll until the final draft is ready.

Free while beta

Try it in a real workflow.

Beta accounts can create hashed API keys, use free draft runs first, then call the production /v1/drafts endpoint from agents or automations.

Capped free runs

Adapter

Context to brief

POST /v1/briefs turns conversation history, tool outputs, notes, and desired output into a ready-to-submit draft request.

Authentication

API keys

Send your key as Authorization: Bearer <api_key>. Raw keys are shown once, stored hashed, and can be revoked during the private beta.

Sources

Text only in v0

Pass retrieved source text directly. URL ingestion is not supported yet, so agents should fetch or retrieve context before calling the API.

Runs

Async by default

POST /v1/drafts creates a run. GET /v1/drafts/:id returns status, output, provenance, usage, and errors.

SERP scanto article draft
Reddit researchto landing copy
Product docsto launch article

Compile context

POST /v1/briefs

Connectors use this when Claude or an agent already did the research and planning. It does not spend draft runs or write final prose; it returns draft_request for/v1/drafts.

curl -X POST https://non-ai-writerproduction.up.railway.app"/v1/briefs \
  -H "Authorization: Bearer naiw_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "desired_output": "blog article",
    "messages": [
      {
        "role": "user",
        "content": "Research the SERP and Reddit threads, then write the final article."
      },
      {
        "role": "assistant",
        "content": "The strongest buyer pain is that teams have keyword lists but no sharp point of view."
      }
    ],
    "tool_outputs": [
      {
        "name": "serp_and_reddit_research",
        "type": "research",
        "content": "Competitor pages repeat the same keyword clusters. Reddit threads complain about generic AI-written SEO posts..."
      }
    ],
    "options": {
      "target_words": "auto",
      "output_format": "markdown"
    }
  }'

Create a draft

POST /v1/drafts

curl -X POST https://non-ai-writerproduction.up.railway.app"/v1/drafts \
  -H "Authorization: Bearer naiw_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Turn this source material into a launch post for technical founders.",
    "sources": [
      {
        "type": "text",
        "name": "product_notes",
        "content": "We are launching a writing engine for agentic content workflows..."
      }
    ],
    "options": {
      "target_words": "auto",
      "output_format": "markdown"
    },
    "metadata": {
      "external_id": "agent-run-123"
    }
  }'

Poll a draft

GET /v1/drafts/:id

curl https://non-ai-writerproduction.up.railway.app"/v1/drafts/<draft_id> \
  -H "Authorization: Bearer naiw_live_..."

JavaScript

Fetch example

const response = await fetch("https://non-ai-writerproduction.up.railway.app"/v1/drafts", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.OPENPEN_API_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    prompt: "Turn this research into a newsletter section.",
    sources: [{ type: "text", name: "research", content: sourceText }],
    options: { target_words: "auto", output_format: "markdown" }
  })
});

const draftRun = await response.json();

Adapter workflow

Context to draft

const brief = await fetch("https://non-ai-writerproduction.up.railway.app"/v1/briefs", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.OPENPEN_API_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    desired_output: "article",
    messages: claudeMessages,
    tool_outputs: [{ name: "research", type: "serp_scan", content: researchText }]
  })
}).then((response) => response.json());

if (brief.ready && brief.draft_request) {
  const draftRun = await fetch("https://non-ai-writerproduction.up.railway.app"/v1/drafts", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.OPENPEN_API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(brief.draft_request)
  }).then((response) => response.json());
}

Agent skill

Use it from Claude or Codex

The public OpenPen agent skill at GitHub lets Claude or Codex package the visible conversation, research notes, and tool outputs, call /v1/briefs, submit the returned draft_request to /v1/drafts, then poll the final draft. The script cannot read a private conversation by itself; the agent passes relevant visible context when the skill runs.

Python

Requests example

import os
import requests

response = requests.post(
    "https://non-ai-writerproduction.up.railway.app"/v1/drafts",
    headers={
        "Authorization": f"Bearer {os.environ['OPENPEN_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "prompt": "Turn this source material into a landing page section.",
        "sources": [{"type": "text", "name": "notes", "content": source_text}],
        "options": {"target_words": "auto", "output_format": "markdown"},
    },
)

draft_run = response.json()

Errors

Predictable failure modes

Every API failure returns an error object with a stable code, human-readable message, and retryability flag.

{
  "error": {
    "code": "preflight_failed",
    "message": "This request could not be grounded safely.",
    "retryable": false
  }
}
invalid_requestunauthorizedapi_key_revokedapi_beta_requiredrate_limitedinsufficient_creditsunsupported_source_typepreflight_failedbrief_failedengine_failednot_foundinternal_error

Automation

n8n and agent workflows

In Claude or Codex, use the agent skill to package visible conversation context automatically. In n8n, use one HTTP Request node for /v1/briefs, a second for /v1/drafts, then poll the returned run id. In LangChain, CrewAI, or a custom agent, wrap the same sequence as a tool.

Known limits

Private beta constraints

Text sources only. Up to five sources per request. Up to 100k total source characters. Poll at a modest cadence so key-level rate limits are reserved for real runs. Streaming, URL ingestion, SDKs, and webhooks come after repeated beta workflows prove the shape.