easl docs
Turn agent output into pages worth sharing — one API call is all it takes.
easl is a smart rendering layer for AI agents. Upload a CSV, Markdown file, JSON blob, or any supported content — easl detects the type and renders it with the best interactive viewer. No accounts required.
| API Base | https://api.easl.dev |
| Sites served at | https://{slug}.easl.dev |
| Anonymous limit | 50 files, 200 MB, expires in 7 days |
| Single-file shorthand | Send content + contentType directly |
Quick Start
With the CLI
curl -fsSL https://easl.dev/install.sh | sh # or: npm install -g @easl/cli easl publish report.md # => https://warm-dawn.easl.dev easl publish data.csv --title "Q4 Results" --open cat logs.json | easl publish --type json
With curl
One API call, content in the body, live URL in the response.
curl -X POST https://api.easl.dev/publish \ -H "Content-Type: application/json" \ -d '{ "content": "# Hello World\nSome **markdown** here.", "contentType": "text/markdown", "title": "My First Page" }' # Response: { "url": "https://warm-dawn.easl.dev", "slug": "warm-dawn", "expiresAt": "2026-03-30T..." }
That's it. Your content is live and beautifully rendered.
Publishing
One endpoint for everything. Send content as a string or an array of files — easl handles the rest.
Supported content types
| contentType | Rendered as |
|---|---|
text/markdown | Styled prose with headings, code, tables, blockquotes |
text/csv | Sortable, filterable interactive table |
application/json | Collapsible tree viewer with syntax highlighting |
text/html | Served as-is |
image/svg+xml | Zoomable, sanitized SVG viewer |
text/x-mermaid | Rendered Mermaid diagram (flowchart, sequence, etc.) |
text/plain | Monospaced text viewer |
Examples
CSV → Interactive Table
curl -X POST https://api.easl.dev/publish \ -H "Content-Type: application/json" \ -d '{ "content": "Name,Role,City\nAlice,Engineer,SF\nBob,Designer,NYC", "contentType": "text/csv", "title": "Team Directory" }'
JSON → Tree Viewer
curl -X POST https://api.easl.dev/publish \ -H "Content-Type: application/json" \ -d '{ "content": "{\"users\": [{\"name\": \"Alice\", \"active\": true}]}", "contentType": "application/json", "title": "API Response" }'
Mermaid → Rendered Diagram
curl -X POST https://api.easl.dev/publish \ -H "Content-Type: application/json" \ -d '{ "content": "graph TD\n A[Start] --> B{Decision}\n B -->|Yes| C[Do it]\n B -->|No| D[Skip]", "contentType": "text/x-mermaid", "title": "My Flowchart" }'
MCP Server
The MCP server lets AI agents publish content natively through the Model Context Protocol. Zero shell commands.
Installation
Add to your MCP configuration (Claude Desktop, Cursor, etc.):
{
"mcpServers": {
"easl": {
"command": "npx",
"args": ["-y", "@easl/mcp"]
}
}
}
Available tools
| Tool | Description |
|---|---|
publish_content | Publish raw content (string) → URL to a shareable page. The fastest path. |
publish_file | Publish a file from disk → URL to a shareable page |
publish_site | Publish a multi-file site (directory) |
list_sites | List published sites in this session |
delete_site | Delete a published site |
Usage
Once configured, just ask your AI agent naturally:
// Your agent can now:
"Publish this CSV as a shareable table"
"Turn this markdown into a beautiful page"
"Show me this JSON in a tree view"
"Create a Mermaid diagram of the architecture"
Agent Skill
The easl Agent Skill teaches AI agents when and how to use easl. It works alongside the MCP server — the MCP provides tools, the skill provides knowledge about content types, viewers, and best practices.
Installation
Install the skill with one command:
npx skills add AdirAmsalem/easl
Works with Claude Code, Cursor, GitHub Copilot, Codex, Gemini CLI, and more.
What it does
Once installed, your agent automatically knows:
- Which content types easl supports and how each is rendered
- When to suggest publishing content as a shareable page
- How to use the MCP tools and HTTP API effectively
- Available templates and limits
CLI
The easl CLI lets you publish files, directories, and piped content directly from the terminal. All commands output structured JSON when piped, making it easy to use in scripts and agent workflows.
Installation
curl -fsSL https://easl.dev/install.sh | sh
Or via npm: npm install -g @easl/cli
Commands
| Command | Description |
|---|---|
easl publish [path] | Publish a file, directory, stdin, or inline content |
easl list | List sites published from this machine |
easl get <slug> | Get site metadata |
easl delete <slug> | Delete a published site |
easl open [slug] | Open a site in your browser |
easl doctor | Check CLI version, API connectivity, and local config |
Examples
# Publish a file easl publish report.md # Publish with options easl publish data.csv --title "Q4 Results" --open # Publish from stdin cat data.csv | easl publish --type csv # Publish inline content easl publish --content "# Hello World" --type markdown # Password-protected — let easl generate & print one once easl publish board-update.md --generate-password # or choose your own: --password "spring-harbor-77" # Account-private — only you (signed in) can view; needs easl login first easl publish board-update.md --private # JSON output for scripting easl publish report.md --json # {"url":"...","slug":"...","claimToken":"...","expiresAt":"..."}
Publish options
| Flag | Description |
|---|---|
--content <text> | Inline content to publish |
--type <type> | Content type: markdown, csv, html, json, svg, mermaid |
--title <title> | Page title |
--slug <slug> | Custom slug (3-48 chars) |
--ttl <seconds> | Time to live (default: 7 days) |
--private | Account-private — only you (signed in) can view. Requires easl login. Combine with a password flag to require both gates |
--password <pw> | Password-protect the page with a value you choose. Works with or without --private. Mutually exclusive with --generate-password |
--generate-password | Password-protect the page with a strong password easl mints and prints once. Works with or without --private. Mutually exclusive with --password |
--open | Open in browser after publishing |
--copy | Copy URL to clipboard |
--json | Force JSON output |
Full documentation: @easl/cli on npm
REST API
Base URL: https://api.easl.dev
Publishing
Publish content and get a live URL back instantly. Supports single-file shorthand or multi-file arrays.
Auth: None required (anonymous, 7-day TTL)
Single-file shorthand
| Field | Type | Description |
|---|---|---|
content | string required | Raw content string |
contentType | string required | MIME type (e.g. text/markdown) |
title | string optional | Page title shown in header & browser tab |
template | string optional | Template: minimal, report, or dashboard |
private | boolean optional | Account-private gate — requires auth (see Private easls) |
password | string optional | Password gate (4–128 chars). Works alone or stacked with private |
generatePassword | boolean optional | Set true (and omit password) to have easl mint a password, returned once in the response |
Multi-file form
| Field | Type | Description |
|---|---|---|
files | array required | Array of {path, content, contentType, encoding?}. Set encoding: "base64" for binary files. |
slug | string optional | Custom slug (3-48 chars, lowercase alphanumeric + hyphens) |
title | string optional | Site title |
template | string optional | Template name |
ttl | number optional | TTL in seconds (default: 7 days) |
private | boolean optional | Account-private gate — requires auth (see Private easls) |
password | string optional | Password gate (4–128 chars). Works alone or stacked with private |
generatePassword | boolean optional | Set true (and omit password) to have easl mint a password, returned once in the response |
Response (201)
{
"url": "https://warm-dawn.easl.dev",
"slug": "warm-dawn",
"claimToken": "claim_...",
"ogImage": "https://warm-dawn.easl.dev/_easl/og.png",
"qrCode": "https://warm-dawn.easl.dev/_easl/qr.svg",
"embed": "<iframe src=\"...?embed=1\" ...></iframe>",
"shareText": "My Page: https://warm-dawn.easl.dev",
"expiresAt": "2026-03-30T12:00:00Z",
"anonymous": true
}
Site Management
Get site metadata including title, files, version, and expiry.
Auth: None required
Set the account gate and/or rotate the password gate. Body: { "private": boolean, "password"?: "..." }. private: true sets the account gate (an authenticated caller also becomes/stays the owner; a claim-token-only caller gets a pure password gate as before). private: false makes the site public and drops the password. password rotates the password gate and may only be sent with private: true — omitting it on the claim-token path mints one (returned once), while on the owner path it leaves the password gate off. Rotating the password invalidates existing unlock sessions.
Auth: owner session/Authorization: Bearer key, OR X-Claim-Token header (claim token works only while the site is still unowned)
Delete a site.
Auth: owner session/Authorization: Bearer key, OR X-Claim-Token header (claim token works only while the site is still unowned)
Feedback
Submit feedback programmatically. Agents can POST here to forward user feedback — no UI needed.
Auth: None required
Request body
| Field | Type | Description |
|---|---|---|
message | string required | Feedback message (max 10 KB) |
email | string optional | Submitter's email address |
name | string optional | Submitter's name |
metadata | object optional | Freeform JSON for context (e.g. site slug, content type) |
Response (201)
{
"success": true,
"id": "fb_abc123"
}
Example
curl -X POST https://api.easl.dev/feedback \ -H "Content-Type: application/json" \ -d '{ "message": "The CSV rendering is great!", "email": "user@example.com", "name": "Alice", "metadata": {"slug": "warm-dawn", "contentType": "text/csv"} }'
cURL Examples
Publish markdown
curl -X POST https://api.easl.dev/publish \ -H "Content-Type: application/json" \ -d '{"content":"# Report\n\nQ1 was great.","contentType":"text/markdown"}'
Publish CSV
curl -X POST https://api.easl.dev/publish \ -H "Content-Type: application/json" \ -d '{"content":"Name,Score\nAlice,95\nBob,87","contentType":"text/csv"}'
Multi-file publish
curl -X POST https://api.easl.dev/publish \ -H "Content-Type: application/json" \ -d '{ "files": [ {"path": "index.html", "content": "<h1>Hello</h1>", "contentType": "text/html"}, {"path": "style.css", "content": "body { color: #333 }", "contentType": "text/css"} ], "title": "My Site" }'
Smart Rendering
easl's core feature. When a site is served, the Worker detects the file type and generates an HTML shell with the right interactive viewer. The raw data is embedded as JSON in a <script> tag, and client-side JavaScript hydrates it into a rich viewer.
Supported Types
| File Type | Detection | Viewer |
|---|---|---|
| CSV | .csv or text/csv | Sortable table with column click-to-sort, alternating rows, sticky header |
| Markdown | .md or text/markdown | Styled prose — headings, lists, code blocks, blockquotes, tables, images |
| JSON | .json or application/json | Collapsible/expandable tree with syntax coloring and expand-all/collapse-all |
| HTML | .html or text/html | Served as-is (no wrapping, no viewer) |
| Images | .png/.jpg/.gif/.webp | Responsive centered image with max-width |
| SVG | .svg or image/svg+xml | Sanitized (scripts stripped), zoomable viewer |
.pdf | Embedded iframe viewer | |
| Mermaid | .mmd or text/x-mermaid | Rendered diagram via Mermaid.js CDN |
Embed Mode
Add ?embed=1 to any site URL to get a clean, headerless version suitable for iframes:
<iframe src="https://warm-dawn.easl.dev?embed=1" width="100%" height="500" frameborder="0" ></iframe>
The embed URL is also returned in the embed field of the publish response.
Anonymous Sites
No account needed. Publish instantly and get a live URL.
| Limit | Value |
|---|---|
| Lifetime | 7 days |
| Max files per site | 50 |
| Max total size | 200 MB |
| Inline content limit | 256 KB |
Each publish returns a claimToken — save it if you need to delete or change the privacy of an anonymous site later. Send it as the X-Claim-Token header on DELETE /sites/:slug or PATCH /sites/:slug/privacy. (Once the site is adopted into an account via claiming, the claim token stops working for mutations and the owner's session/Bearer key takes over.)
Private easls
Public is the default. Two independent, composable gates protect a page: a password gate (password / --password, anonymous-publishable) and an account gate (private: true / --private, requires login). Set either, both, or neither.
For the password gate you can supply your own password, or omit it and set generatePassword: true (CLI: --generate-password) to have easl mint a strong one. Either way the password is returned in the publish response under password — it is shown once, with no recovery.
curl -X POST https://api.easl.dev/publish \ -H "Content-Type: application/json" \ -d '{"content":"# Confidential","contentType":"text/markdown","generatePassword":true}' # → { "url": "...", "visibility": "public", "password": "dust-arch-fern-dark-1181", ... }
| Account gate | An account-private site (private: true) 302-redirects anonymous visitors to sign in; only the owner — or someone holding a signed share link — gets through. A non-owner who is signed in gets 403 |
| Password gate | Visitors enter the password once; a signed cookie keeps them in for 30 days (sliding). A private + password site requires both — share-link recipients still face the password prompt |
| Not cached / not indexed | Gated pages (account-private or password-protected) skip the edge cache, send noindex, and skip OG-image generation |
| Rotate / toggle | PATCH /sites/:slug/privacy with the owner's session/Bearer key or the claim token; rotating the password invalidates existing unlock sessions |
| CLI | Password-protect with --password <pw> or --generate-password; go account-private with --private (after easl login; on a headless/remote box use easl login --device). Passwords are saved to ~/.config/easl/sites.json and shown again by easl open <slug> |
Rate Limits
Publish endpoints are rate-limited per IP.
| Endpoint | Limit | Window |
|---|---|---|
/publish | 10 requests | 1 hour |
Error Codes
| Code | Meaning |
|---|---|
400 | Bad request — missing or invalid parameters |
401 | Unauthorized — no credential supplied (no owner session/Bearer key and no X-Claim-Token), private: true publish without auth, or password required to view a gated page |
403 | Forbidden — credential supplied but unauthorized for this site (wrong owner, or a claim token for an already-owned site) |
404 | Not found — site or version doesn't exist |
409 | Conflict — slug taken or version mismatch |
422 | Unprocessable — invalid request |
429 | Rate limited — try again later |
500 | Server error |