API
Base URL: /api/v1. Send JSON and authenticate with a bearer authorization header.
Discover this key's capabilities
curl "$MARKFOLIO_BASE_URL/api/v1/me" \
-H "Authorization: Bearer $MARKFOLIO_API_KEY"The response reports the account tier, this key's scopes, its current account request limit, retention policy, and feature flags. Existing keys keep full access. New keys default to publish and update access without destructive operations.
Every create/list/read response includes a public url, a stable folder_id, plus explicit expiry_statusand expiry_label. For agents, that is the important output: Markdown in, shareable link out, with no guessing about whether the link expires.
Publish a Markdown file from an agent
Use this when an agent has written a local .md file and needs to return a Markfolio link instead of pasting the whole document into chat.
export MARKFOLIO_BASE_URL="https://your-domain.com"
export MARKFOLIO_API_KEY="your_api_key"
python3 - <<'PY' ./proposal.md
import json
import os
import sys
import urllib.request
from pathlib import Path
base_url = os.environ["MARKFOLIO_BASE_URL"].rstrip("/")
api_key = os.environ["MARKFOLIO_API_KEY"]
markdown = Path(sys.argv[1]).read_text(encoding="utf-8")
data = json.dumps({"markdown": markdown}).encode("utf-8")
request = urllib.request.Request(
f"{base_url}/api/v1/documents",
data=data,
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(request) as response:
payload = json.loads(response.read().decode("utf-8"))
print(payload['url'])
PYReplace ./proposal.md with the file path. The command prints only the public Markfolio URL, so another agent can capture it directly.
Create document
curl -X POST "$MARKFOLIO_BASE_URL/api/v1/documents" \
-H "Authorization: Bearer $MARKFOLIO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"markdown":"# Hello","title":"Hello","slug":"hello","folder_id":"fld_123"}'Free users cannot pass slug. Pro users can pass a custom slug; if it is taken during create, Markfolio falls back to a random slug. Use folder_id to file the document immediately.
List documents
curl "$MARKFOLIO_BASE_URL/api/v1/documents?folder_id=fld_123&q=proposal&publish_status=published&limit=20&offset=0" \
-H "Authorization: Bearer $MARKFOLIO_API_KEY"Add folder_id to return only documents filed in that exact folder. Unsupported folder query fields such as folder_nameare rejected instead of ignored. Search with q across title, slug, and external ID. Filter the independent lifecycle axes withpublish_status and expiry_status. Pagination includes has_more and next_offset; passinclude_total=true only when an exact count is needed.
Read document
curl "$MARKFOLIO_BASE_URL/api/v1/documents/doc_123" \
-H "Authorization: Bearer $MARKFOLIO_API_KEY"Update document
curl -X PATCH "$MARKFOLIO_BASE_URL/api/v1/documents/doc_123" \
-H "Authorization: Bearer $MARKFOLIO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"markdown":"# Updated","folder_id":null}'Pass folder_id to move the document, or nullto clear it. Free API keys may make folder-only moves; content, title, and slug PATCH remain Pro-only. Use only snake_case; folder-ish fields like folderId, folder, andfolder_name are rejected instead of ignored.
Folders
Folder listings include document_count for the documents currently filed in each folder.
curl "$MARKFOLIO_BASE_URL/api/v1/folders" \
-H "Authorization: Bearer $MARKFOLIO_API_KEY"curl -X POST "$MARKFOLIO_BASE_URL/api/v1/folders" \
-H "Authorization: Bearer $MARKFOLIO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"name":"Client work"}'curl -X PATCH "$MARKFOLIO_BASE_URL/api/v1/folders/fld_123" \
-H "Authorization: Bearer $MARKFOLIO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"name":"Archived client work"}'curl -X DELETE "$MARKFOLIO_BASE_URL/api/v1/folders/fld_123" \
-H "Authorization: Bearer $MARKFOLIO_API_KEY"Delete document
curl -X DELETE "$MARKFOLIO_BASE_URL/api/v1/documents/doc_123" \
-H "Authorization: Bearer $MARKFOLIO_API_KEY"Error codes
401 | Missing or invalid API key. |
403 | The account tier or API-key scope does not allow the requested operation. |
404 | Document not found. |
409 | Slug already taken. |
429 | The account exceeded its tier limit: 30 requests per minute on Free or 120 on Pro. |