InterlinedList API Reference
Generated from the live OpenAPI 3.1 document (350 endpoints). Base URL https://interlinedlist.com/v1. Developer overview
Getting Started
- Create a personal access token in Settings → Developer, choosing only the scopes you need. It is shown once.
- Send it on every request:
Authorization: Bearer ilp_… - Try it:
curl -H "Authorization: Bearer $IL_TOKEN" https://interlinedlist.com/v1/me
Conventions
- Errors always look like
{"error":{"code","message","details"}}; that shape is omitted from each endpoint below. - Collections return
{"data":[…],"page":{"next_cursor","has_more"}}; passcursorandlimitto page. - Public endpoints work without a token; Token endpoints need one; Admin endpoints need a moderator or admin role.
- Token scopes:
profile:read,profile:write,messages:read,messages:write,lists:read,lists:write,documents:read,documents:write,dm:read,dm:write,notifications:read,social:read,social:write,media:write,exports:run,webhooks:manage. A request without the scope an endpoint needs fails withscope_missing.
ai
get/v1/ai/statusAI availability and remaining quotaToken
post/v1/ai/previewStart an AI preview (202; streamed as ai.delta / ai.done on /user/{usr})Token
| Field | Type | Notes |
|---|---|---|
feature* | "compose_assist" | "suggest_tags" | "alt_text" | "summarize" | "generate_list" | "generate_document" | "schedule_series" | "rewrite" | "translate" | |
input | any | |
options | object (map) | default {} |
202Accepted
| Field | Type | Notes |
|---|---|---|
generation_id* | string | |
channel* | string |
post/v1/ai/confirmApply a preview (creates list, docs, scheduled series); counts as one actionToken
| Field | Type | Notes |
|---|---|---|
generation_id* | string | |
edits | any | |
syndication | object | |
syndication.targets* | object[] | ≤ 20 items |
syndication.targets[].account_id* | string | pattern ^sa_[0-7][0-9A-HJKMNP-TV-Z]{25}$ |
syndication.targets[].target_ref | string | null | default null |
syndication.linkedin_link_as_first_comment | boolean | default false |
200OK
auth
post/v1/auth/signupSign up (SubstratalApps); returns verification_sent and, when subscribed, device tokensPublic
| Field | Type | Notes |
|---|---|---|
email* | string (email) | |
password* | string | 12–128 chars |
display_name* | string | 1–100 chars |
client_kind | "ios" | "macos" | "android" | "cli" | "desktop" | "web" | default "cli" |
200OK
post/v1/auth/loginSign in → device tokens, or an MFA challengePublic
| Field | Type | Notes |
|---|---|---|
email* | string (email) | |
password* | string | 1–128 chars |
client_kind | "ios" | "macos" | "android" | "cli" | "desktop" | "web" | default "cli" |
device_label | string | ≤ 80 chars |
200OK
post/v1/auth/mfa/verifyComplete MFAPublic
| Field | Type | Notes |
|---|---|---|
mfa_ticket* | string | |
code | string | |
recovery_code | string | |
client_kind | "ios" | "macos" | "android" | "cli" | "desktop" | "web" | default "cli" |
200OK
post/v1/auth/refreshRotate an IL device token pairPublic
post/v1/auth/logoutRevoke this device token (or all of mine)Token
| Field | Type | Notes |
|---|---|---|
all_sessions | boolean | default false |
200OK
get/v1/auth/accessAccess status for the current tokenPublic
post/v1/auth/password/forgotPassword reset email (always 202) (proxied)Public
post/v1/auth/password/resetReset the password with the emailed token (proxied)Public
post/v1/auth/email/verifyVerify the email address (proxied)Public
post/v1/auth/email/verify/resendResend the verification email (proxied)Token
post/v1/auth/reauthenticateStep-up re-authentication (proxied)Token
post/v1/auth/invitations/acceptAccept a SubstratalApps organization invitation (proxied)Public
developer
get/v1/me/tokensMy personal access tokensToken
post/v1/me/tokensCreate a personal access token (shown once; step-up)Token
| Field | Type | Notes |
|---|---|---|
name* | string | 1–80 chars |
scopes* | "profile:read" | "profile:write" | "messages:read" | "messages:write" | "lists:read" | "lists:write" | "documents:read" | "documents:write" | "dm:read" | "dm:write" | "notifications:read" | "social:read" | "social:write" | "media:write" | "exports:run" | "webhooks:manage"[] | ≥ 1 items |
expires_in_days | integer | null | 1–365 · default 90 |
200OK
delete/v1/me/tokens/{id}Revoke a tokenToken
get/v1/me/webhooksMy webhooksToken
post/v1/me/webhooksAdd a webhook (the signing secret is shown once)Token
| Field | Type | Notes |
|---|---|---|
url* | string (uri) | ≤ 2048 chars |
events* | "message.created" | "message.updated" | "message.deleted" | "mention.created" | "follow.created" | "list.row.created" | "list.row.updated" | "list.row.deleted" | "list.updated" | "document.updated" | "document.published" | "share.granted" | "syndication.failed" | "export.ready"[] | ≥ 1 items |
200OK
patch/v1/me/webhooks/{id}Update URL, events or status (active|paused)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
url | string (uri) | ≤ 2048 chars |
events | "message.created" | "message.updated" | "message.deleted" | "mention.created" | "follow.created" | "list.row.created" | "list.row.updated" | "list.row.deleted" | "list.updated" | "document.updated" | "document.published" | "share.granted" | "syndication.failed" | "export.ready"[] | ≥ 1 items |
status | "active" | "paused" |
200OK
delete/v1/me/webhooks/{id}Delete a webhookToken
post/v1/me/webhooks/{id}/testSend a test event (202)Token
post/v1/me/webhooks/{id}/rotate-secretRotate the signing secret (shown once)Token
get/v1/me/webhooks/{id}/deliveriesDelivery log (30 days)Token
post/v1/me/webhooks/{id}/deliveries/{did}/redeliverRedeliver one delivery (202)Token
get/v1/me/usageMy API, AI and storage usage (?since=&until=&group_by=token|route)Token
| Name | In | Type | Notes |
|---|---|---|---|
since | query | string (date-time) | |
until | query | string (date-time) | |
group_by | query | "token" | "route" | default "token" |
200OK
dm
get/v1/dm/conversationsConversations (?folder=inbox|trash)Token
| Name | In | Type | Notes |
|---|---|---|---|
folder | query | "inbox" | "trash" | default "inbox" |
limit | query | integer | 1–100 · default 25 |
cursor | query | string |
200OK
post/v1/dm/conversationsStart a conversation (direct ones are de-duplicated)Token
| Field | Type | Notes |
|---|---|---|
participant_ids* | string[] | 1–31 items |
title | string | null | ≤ 100 chars |
200OK
get/v1/dm/conversations/{id}A conversationToken
patch/v1/dm/conversations/{id}Title (groups) and muteToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
title | string | null | ≤ 100 chars |
muted_until | string | null (date-time) | |
mute | boolean |
200OK
post/v1/dm/conversations/{id}/leaveLeave (moves to trash)Token
get/v1/dm/conversations/{id}/messagesMessages (newest first; ?cursor= for older, or ?after= for newer, oldest first)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
limit | query | integer | 1–100 · default 50 |
cursor | query | string | |
after | query | string |
200OK
post/v1/dm/conversations/{id}/messagesSend (Idempotency-Key required)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
Idempotency-Key* | header | string | 1–255 chars |
| Field | Type | Notes |
|---|---|---|
body | string | ≤ 10000 chars · default "" |
media_ids | string[] | ≤ 8 items · default [] |
reply_to_id | string | null |
200OK
patch/v1/dm/messages/{id}Edit within 15 minutes (sender)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
body* | string | 1–10000 chars |
200OK
delete/v1/dm/messages/{id}Delete for me (moves to trash; ?for=everyone by the sender within 15 minutes)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
for | query | "me" | "everyone" | default "me" |
200OK
get/v1/dm/messages/deletedMy trashed messages, newest-deleted first (Archived / Deleted)Token
| Name | In | Type | Notes |
|---|---|---|---|
limit | query | integer | 1–100 · default 50 |
cursor | query | string |
200OK
post/v1/dm/messages/{id}/restoreRestore from my trashToken
put/v1/dm/messages/{id}/reactions/{emoji}ReactToken
delete/v1/dm/messages/{id}/reactions/{emoji}Remove reactionToken
post/v1/dm/conversations/{id}/readMark read up to a messageToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
last_read_message_id* | string |
200OK
get/v1/dm/unread-countUnread DMs across conversationsToken
get/v1/dm/recipientsPeople I can message (?q=)Token
get/v1/dm/eligibility/{userId}Can I message this person?Token
post/v1/dm/conversations/{id}/reportReport a conversation (staff may review the reported messages)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
reason* | "spam" | "harassment" | "misinformation" | "inappropriate" | "illegal" | "other" | |
detail | string | null | ≤ 2000 chars |
message_ids | string[] | ≤ 20 items · default [] |
200OK
documents
get/v1/documentsMy documents (?scope=mine|shared|watching|all|trash&folder_id=&q=)Token
| Name | In | Type | Notes |
|---|---|---|---|
limit | query | integer | 1–100 |
cursor | query | string | |
scope | query | "mine" | "shared" | "watching" | "all" | "trash" | default "mine" |
folder_id | query | string | |
q | query | string | ≤ 200 chars |
200OK
post/v1/documentsCreate a documentToken
| Field | Type | Notes |
|---|---|---|
title* | string | 1–200 chars |
folder_id | string | null | |
markdown | string | |
template_id | string | null | |
visibility | "private" | "link" | "public" | default "private" |
200OK
get/v1/documents/treeFolders and documents as a treeToken
get/v1/documents/syncDelta sync for the CLI and desktop (?since=ISO)Token
| Name | In | Type | Notes |
|---|---|---|---|
since | query | string (date-time) | |
limit | query | integer | 1–100 · default 100 |
200OK
post/v1/documents/syncPush local changes; per-item results (207)Token
| Field | Type | Notes |
|---|---|---|
documents* | object[] | 1–100 items |
documents[].id | string | |
documents[].title* | string | 1–200 chars |
documents[].folder_id | string | null | |
documents[].markdown* | string | |
documents[].base_version | integer | |
documents[].base_hash | string | |
documents[].deleted | boolean |
200OK
get/v1/documents/templatesMy document templates (flagged, or in a _templates folder)Token
post/v1/documents/templates/seed-defaultsCreate the default templates (idempotent)Token
post/v1/documents/from-templateNew document from a templateToken
| Field | Type | Notes |
|---|---|---|
template_id* | string | |
title | string | 1–200 chars |
folder_id | string | null | |
visibility | "private" | "link" | "public" |
200OK
get/v1/documents/{id}Metadata + markdown (SSR/read); ?expand=owner adds the ownerPublic
patch/v1/documents/{id}Title, folder, visibility, slug (manager+)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
title | string | 1–200 chars |
folder_id | string | null | |
visibility | "private" | "link" | "public" | |
slug | string | null | pattern ^[a-z0-9][a-z0-9-]{0,119}$ |
200OK
delete/v1/documents/{id}Delete (owner; soft)Token
get/v1/documents/{id}/stateBinary Yjs state (compacted state merged with pending updates); ETag = update watermarkToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
200Yjs update (application/octet-stream)
Response body: any JSON
post/v1/documents/{id}/updatesAppend a binary Yjs update batch (≤ 256 KB); published to /doc/{id}Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
Request body (application/octet-stream): any JSON
200OK
put/v1/documents/{id}/contentNon-collaborative write {markdown} + If-Match (merged into the Yjs doc server-side)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
markdown* | string |
200OK
post/v1/documents/{id}/restoreRestore a deleted document (owner)Token
get/v1/documents/{id}/versionsVersion history (automatic + named snapshots)Token
post/v1/documents/{id}/versionsSave a named version (label optional)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
label | string | null | ≤ 120 chars |
200OK
get/v1/documents/{id}/versions/{vid}A version with its MarkdownToken
post/v1/documents/{id}/versions/{vid}/restoreRestore a version: its content is applied as a new update (the current content is kept as a 'Before restore' version)Token
get/v1/documents/{id}/commentsDocument comments (?include_resolved=true)Token
post/v1/documents/{id}/commentsComment (commenter+)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
body* | string | 1–5000 chars |
parent_id | string | null | |
anchor | object (map) | null |
200OK
patch/v1/documents/{id}/comments/{cid}Edit my commentToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
cid* | path | string |
| Field | Type | Notes |
|---|---|---|
body* | string | 1–5000 chars |
200OK
delete/v1/documents/{id}/comments/{cid}Delete a comment (author or manager)Token
post/v1/documents/{id}/comments/{cid}/resolveResolve (or reopen with {resolved:false})Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
cid* | path | string |
| Field | Type | Notes |
|---|---|---|
resolved | boolean | default true |
200OK
get/v1/documents/{id}/exportExport (?format=md|html|pdf); pdf is an export jobToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
format | query | "md" | "html" | "pdf" | default "md" |
inline | query | "true" | "false" |
200OK
post/v1/documents/{id}/publishPublish as a blog post (now or at publish_at)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
slug | string | pattern ^[a-z0-9][a-z0-9-]{0,119}$ |
excerpt | string | null | ≤ 500 chars |
hero_media_id | string | null | |
tags | string[] | ≤ 10 items · default [] |
publish_at | string | null (date-time) | |
announce | object | null | |
announce.syndication | any |
200OK
delete/v1/documents/{id}/publishUnpublishToken
get/v1/doc-foldersMy document foldersToken
post/v1/doc-foldersCreate a document folderToken
| Field | Type | Notes |
|---|---|---|
name* | string | 1–80 chars |
parent_id | string | null | |
is_templates | boolean | default false |
200OK
patch/v1/doc-folders/{id}Rename or move a document folderToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
name | string | 1–80 chars |
parent_id | string | null |
200OK
delete/v1/doc-folders/{id}Delete a document folder (documents move to the parent)Token
post/v1/doc-folders/{id}/exportExport a folder of documents (202)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
format | "md" | "html" | "pdf" | default "md" |
200OK
get/v1/blogThe official blog: published posts flagged official (public, regardless of owner claim status)Public
| Name | In | Type | Notes |
|---|---|---|---|
official | query | "true" | "false" | |
tag | query | string | 1–64 chars |
limit | query | integer | 1–100 |
cursor | query | string |
200OK
get/v1/blog/{slug}One official blog post by blog slug (legacy /blog/{slug} URLs)Public
post/v1/blog/subscribeSubscribe to the official blog or a member blog (double opt-in; always 202)Public
| Field | Type | Notes |
|---|---|---|
email* | string (email) | ≤ 320 chars |
blog* | string | 1–64 chars |
website | string | ≤ 200 chars |
200OK
post/v1/blog/subscribe/confirmConfirm a blog subscription from the emailed link (single use)Public
exports
get/v1/exportsMy exportsToken
post/v1/exportsStart an export (202; Idempotency-Key required)Token
| Name | In | Type | Notes |
|---|---|---|---|
Idempotency-Key* | header | string | 1–255 chars |
| Field | Type | Notes |
|---|---|---|
kinds* | "messages" | "lists" | "list_rows" | "documents" | "follows" | "bookmarks" | "blog"[] | ≥ 1 items |
format | "csv" | "json" | "md" | default "json" |
scope | "mine" | "shared" | "all" | default "mine" |
include_platform_bundle | boolean | default false |
200OK
get/v1/exports/countsRecord counts per export kind for a scope (what an export would include)Token
| Name | In | Type | Notes |
|---|---|---|---|
scope | query | "mine" | "shared" | "all" | default "mine" |
200OK
get/v1/exports/{id}Export status with a 24 h presigned download URL when readyToken
imports
post/v1/importsStart an import from an uploaded file (202)Token
| Field | Type | Notes |
|---|---|---|
kind* | "list_csv" | "markdown_zip" | "mastodon_archive" | "bluesky_car" | |
media_id* | string | |
options | object (map) | default {} |
200OK
get/v1/imports/{id}Import statusToken
integrations
get/v1/integrationsConnected accounts with healthToken
post/v1/integrations/{id}/verifyLive provider check (X and Bluesky are queued to the worker: 202)Token
patch/v1/integrations/{id}Set default / labelToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
is_default | boolean | |
label | string | null | ≤ 80 chars |
200OK
delete/v1/integrations/{id}Disconnect (tokens are destroyed)Token
get/v1/integrations/linkedin/targetsLinkedIn targets (profile and company pages)Token
put/v1/integrations/linkedin/defaultsDefault LinkedIn targetsToken
| Field | Type | Notes |
|---|---|---|
target_ids* | string[] | ≤ 20 items |
200OK
get/v1/integrations/github/reposAll my GitHub repositories, or one organization’s (?org=); fully paginatedToken
| Name | In | Type | Notes |
|---|---|---|---|
org | query | string | pattern ^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}$ |
200OK
get/v1/integrations/github/orgsMy GitHub organizations (fully paginated)Token
get/v1/integrations/github/repos/{o}/{r}/labelsRepository labels (fully paginated)Token
get/v1/integrations/github/repos/{o}/{r}/assigneesRepository assignees (fully paginated)Token
get/v1/integrations/github/repos/{o}/{r}/next-issue-numberThe number GitHub will give the next issue (issues and PRs share one sequence)Token
get/v1/integrations/github/issuesIssues in a repository (?repo=owner/name&state=open|closed|all), newest first; pull requests are left outToken
| Name | In | Type | Notes |
|---|---|---|---|
repo* | query | string | pattern ^[\w.-]+\/[\w.-]+$ |
state | query | "open" | "closed" | "all" | default "open" |
limit | query | integer | 1–100 · default 25 |
cursor | query | string | pattern ^[1-9]\d{0,4}$ |
200OK
post/v1/integrations/github/issuesCreate an issueToken
| Field | Type | Notes |
|---|---|---|
repo* | string | pattern ^[\w.-]+\/[\w.-]+$ |
title* | string | 1–256 chars |
body | string | ≤ 65536 chars |
labels | string[] | ≤ 100 items |
assignees | string[] | ≤ 10 items |
200OK
patch/v1/integrations/github/issues/{o}/{r}/{n}Update an issue (title, body, state, labels, assignees)Token
| Name | In | Type | Notes |
|---|---|---|---|
o* | path | string | |
r* | path | string | |
n* | path | string |
| Field | Type | Notes |
|---|---|---|
title | string | 1–256 chars |
body | string | null | ≤ 65536 chars |
state | "open" | "closed" | |
labels | string[] | ≤ 100 items |
assignees | string[] | ≤ 10 items |
200OK
post/v1/integrations/github/issues/{o}/{r}/{n}/commentsComment on an issueToken
| Name | In | Type | Notes |
|---|---|---|---|
o* | path | string | |
r* | path | string | |
n* | path | string |
| Field | Type | Notes |
|---|---|---|
body* | string | 1–65536 chars |
200OK
lists
get/v1/listsMy lists (?scope=mine|shared|watching|all|trash&folder_id=&q=); trash is my deleted listsToken
| Name | In | Type | Notes |
|---|---|---|---|
limit | query | integer | 1–100 |
cursor | query | string | |
scope | query | "mine" | "shared" | "watching" | "all" | "trash" | default "mine" |
folder_id | query | string | |
q | query | string | ≤ 200 chars |
200OK
post/v1/listsCreate a list (fields, template, initial rows)Token
Request body (application/json), optional: See 05-api-specification.md (recursive schema)
200OK
get/v1/lists/graphNodes and edges for the ERD and tree viewsToken
get/v1/lists/{id}A list (?expand=fields,views,owner,my_role)Public
patch/v1/lists/{id}Update title, description, folder, parent, visibility, collaborative, searchable_rows (manager+)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
title | string | 1–200 chars |
description | string | null | ≤ 5000 chars |
folder_id | string | null | |
parent_list_id | string | null | |
visibility | "private" | "link" | "public" | |
collaborative | boolean | |
searchable_rows | boolean |
200OK
delete/v1/lists/{id}Delete (owner; soft, 30 days)Token
post/v1/lists/{id}/restoreRestore a deleted list (owner)Token
post/v1/lists/{id}/duplicateDuplicate into my lists (fields, views, rows)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
title | string | ≤ 200 chars |
include_rows | boolean | default true |
200OK
post/v1/lists/{id}/transferTransfer ownership (owner); the previous owner keeps managerToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
to_user_id | string | |
handle | string | 1–64 chars |
200OK
get/v1/lists/{id}/fieldsFields (?include_archived=true)Public
post/v1/lists/{id}/fieldsAdd a field (manager+; If-Match schema version)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
Request body (application/json), optional: See 05-api-specification.md (recursive schema)
200OK
patch/v1/lists/{id}/fields/{key}Change a field; type changes are non-destructive (?dry_run=true lists failing rows); null clears an optional propertyToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
key* | path | string |
Request body (application/json), optional: See 05-api-specification.md (recursive schema)
200OK
delete/v1/lists/{id}/fields/{key}Archive a field (data kept 30 days)Token
post/v1/lists/{id}/fields/{key}/restoreRestore an archived fieldToken
put/v1/lists/{id}/fields:orderReorder fieldsToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
keys* | string[] | 1–200 items |
200OK
get/v1/lists/{id}/rowsRows with server-side filter/sort (?view_id=&filter=<RuleGroup JSON>&sort=field:asc&q=); keyset, or ?offset= for numbered pagesPublic
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
limit | query | integer | 1–100 |
cursor | query | string | |
view_id | query | string | |
filter | query | string | ≤ 10000 chars |
sort | query | string | ≤ 200 chars |
q | query | string | ≤ 200 chars |
offset | query | integer | null | 0–10000 |
200OK
post/v1/lists/{id}/rowsAdd a row (editor+)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
data* | object (map) | |
after_id | string |
200OK
post/v1/lists/{id}/rows:batchAdd ≤ 500 rows, all-or-nothing validation (Idempotency-Key required)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
Idempotency-Key* | header | string | 1–255 chars |
| Field | Type | Notes |
|---|---|---|
rows* | object (map)[] | 1–500 items |
200OK
post/v1/lists/{id}/rows:batch-deleteSoft-delete ≤ 500 rows, all-or-nothing (editor+)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
row_ids* | string[] | 1–500 items |
200OK
get/v1/lists/{id}/rows/{rowId}A rowPublic
patch/v1/lists/{id}/rows/{rowId}{changes, expect} + If-Match → 200, or 409 {conflicts[]} (field-level merge in one statement)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
rowId* | path | string |
| Field | Type | Notes |
|---|---|---|
changes* | object (map) | |
expect | object (map) |
200OK
delete/v1/lists/{id}/rows/{rowId}Delete a row (soft; on a GitHub-backed list, closes the issue and keeps the row)Token
post/v1/lists/{id}/rows/{rowId}/restoreRestore a deleted row (on a GitHub-backed list, reopens the issue)Token
put/v1/lists/{id}/rows/{rowId}/positionMove a row ({after_id | before_id}; fractional index)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
rowId* | path | string |
| Field | Type | Notes |
|---|---|---|
after_id | string | null | |
before_id | string | null |
200OK
get/v1/lists/{id}/rows/{rowId}/historyRow historyToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
rowId* | path | string | |
limit | query | integer | 1–100 |
cursor | query | string |
200OK
post/v1/lists/{id}/rows/{rowId}/history/{version}/restoreRestore a row to a historical versionToken
get/v1/lists/{id}/rows/{rowId}/commentsRow commentsToken
post/v1/lists/{id}/rows/{rowId}/commentsComment on a row (commenter+)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
rowId* | path | string |
| Field | Type | Notes |
|---|---|---|
body* | string | 1–5000 chars |
parent_id | string | null |
200OK
patch/v1/lists/{id}/rows/{rowId}/comments/{commentId}Edit my commentToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
rowId* | path | string | |
commentId* | path | string |
| Field | Type | Notes |
|---|---|---|
body* | string | 1–5000 chars |
200OK
delete/v1/lists/{id}/rows/{rowId}/comments/{commentId}Delete a comment (author or manager)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
rowId* | path | string | |
commentId* | path | string |
200OK
post/v1/lists/{id}/importImport CSV/TSV into the list (202; import job)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
media_id* | string | |
mapping | object (map) | default {} |
200OK
get/v1/lists/{id}/exportExport (?format=csv|json|md); small lists inline, large ones become an export jobToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
format | query | "csv" | "json" | "md" | default "csv" |
200OK
post/v1/lists/{id}/syncRefresh a GitHub-backed listToken
get/v1/lists/{id}/rssRSS of recent rows (public lists only)Public
get/v1/lists/{id}/contributorsPeople who created or edited rowsToken
get/v1/lists/{id}/viewsShared views plus my personal viewsToken
post/v1/lists/{id}/viewsCreate a view (shared views need manager+)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
Request body (application/json), optional: See 05-api-specification.md (recursive schema)
200OK
patch/v1/lists/{id}/views/{viewId}Update a viewToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
viewId* | path | string |
Request body (application/json), optional: See 05-api-specification.md (recursive schema)
200OK
delete/v1/lists/{id}/views/{viewId}Delete a viewToken
post/v1/lists/{id}/views/{viewId}/forkFork a view into a personal copyToken
put/v1/lists/{id}/views/prefsMy last/default viewToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
last_view_id | string | null | |
default_view_id | string | null |
200OK
get/v1/list-foldersMy list foldersToken
post/v1/list-foldersCreate a list folderToken
| Field | Type | Notes |
|---|---|---|
name* | string | 1–80 chars |
parent_id | string | null |
200OK
patch/v1/list-folders/{id}Rename or move a list folderToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
name | string | 1–80 chars |
parent_id | string | null | |
position | string | ≤ 64 chars |
200OK
delete/v1/list-folders/{id}Delete a list folder (?contents=keep moves its lists and subfolders to the parent; delete also deletes my lists anywhere inside it, soft, and its subfolders)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
contents | query | "keep" | "delete" | default "keep" |
200OK
get/v1/list-connectionsConnections between my lists (?list_id=)Token
post/v1/list-connectionsConnect two listsToken
| Field | Type | Notes |
|---|---|---|
from_list_id* | string | |
to_list_id* | string | |
label | string | null | ≤ 80 chars |
200OK
delete/v1/list-connections/{id}Remove a connectionToken
get/v1/list-templatesGlobal and my list templatesToken
post/v1/list-templatesCreate a personal list templateToken
Request body (application/json), optional: See 05-api-specification.md (recursive schema)
200OK
post/v1/lists/{id}/save-as-templateSave a list’s fields (and up to 10 sample rows) as a templateToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
name | string | 1–120 chars |
include_rows | boolean | default false |
200OK
materialize
post/v1/materialize/previewPropose a schema, rows and Markdown from sourcesToken
| Field | Type | Notes |
|---|---|---|
source* | object | |
source.type* | "messages" | "list" | "rows" | "document" | "selection" | |
source.ids | string[] | ≤ 500 items · default [] |
source.list_id | string | |
source.selection | object | |
source.selection.text* | string | ≤ 100000 chars |
target* | "list" | "document" | "list_and_document" | "message" | |
options | object | default {} |
options.title | string | ≤ 200 chars |
200OK
post/v1/materializeCreate from sources (re-fetches and re-authorizes all sources)Token
Request body (application/json), optional: See 05-api-specification.md (recursive schema)
200OK
me
get/v1/meThe signed-in user: projection, profile, handle, access, standingToken
200Me
| Field | Type | Notes |
|---|---|---|
id* | string | pattern ^u_[0-7][0-9A-HJKMNP-TV-Z]{25}$ |
handle* | string | pattern ^[a-z0-9_]{3,30}$ |
display_name* | string | ≤ 100 chars |
avatar_url* | string | null (uri) | |
header_url* | string | null (uri) | |
bio* | string | null | ≤ 500 chars |
links* | object[] | ≤ 4 items |
links[].url* | string (uri) | ≤ 2048 chars |
links[].label | string | ≤ 60 chars |
links[].verified | boolean | default false |
pronouns* | string | null | ≤ 40 chars |
location_label* | string | null | ≤ 100 chars |
is_private* | boolean | |
noindex* | boolean | |
inactive* | boolean | |
redacted* | boolean | |
counts* | object | |
counts.messages* | integer | ≥ 0 |
counts.followers* | integer | ≥ 0 |
counts.following* | integer | ≥ 0 |
counts.lists | integer | ≥ 0 |
counts.documents | integer | ≥ 0 |
pinned* | object[] | ≤ 3 items |
pinned[].type* | "message" | "list" | "document" | |
pinned[].id* | string | |
created_at* | string (date-time) | |
viewer | object | |
viewer.following* | "none" | "pending" | "accepted" | |
viewer.followed_by* | boolean | |
viewer.blocking* | boolean | |
viewer.muting* | boolean | |
substratal_user_id* | string | |
email_verified* | boolean | |
access* | "active" | "none" | "lapsed" | |
standing* | "active" | "restricted" | "suspended" | "banned" | |
timezone* | string | null | |
locale* | string | null |
get/v1/me/capabilitiesCapabilities from the single server-side functionToken
200Capabilities
| Field | Type | Notes |
|---|---|---|
access* | "active" | "none" | "lapsed" | |
roles* | string[] | |
permissions* | string[] | |
standing* | "active" | "restricted" | "suspended" | "banned" | |
email_verified* | boolean | |
limits* | object (map) | |
can* | object (map) | |
denials* | object (map) |
patch/v1/me/profileUpdate profile (display_name writes through to SubstratalApps)Token
| Field | Type | Notes |
|---|---|---|
display_name | string | 1–100 chars |
bio | string | null | ≤ 500 chars |
links | object[] | ≤ 4 items |
links[].url* | string (uri) | ≤ 2048 chars |
links[].label | string | ≤ 60 chars |
pronouns | string | null | ≤ 40 chars |
location_label | string | null | ≤ 100 chars |
pinned | object[] | ≤ 3 items |
pinned[].type* | "message" | "list" | "document" | |
pinned[].id* | string |
200OK
put/v1/me/handleClaim or change the handle (409 handle_taken, 429 once per 30 days)Token
get/v1/handles/{handle}/availabilityIs a handle available?Public
get/v1/me/settingsPer-app settings (resolved/overrides) plus SQL-projected fieldsToken
patch/v1/me/settingsUpdate settings; SQL-filtered keys are copied onto the users row in the same requestToken
| Field | Type | Notes |
|---|---|---|
is_private | boolean | |
dm_policy | "mutuals" | "following" | "anyone" | |
mention_policy | "anyone" | "following" | "nobody" | |
discoverable | boolean | |
noindex | boolean | |
show_replies_in_timeline | boolean | |
timezone | string | 1–64 chars |
default_timeline_tab | "following" | "everything" | "mine" | "followers" | |
remember_timeline_tab | boolean | |
message_soft_limit | integer | null | 100–5000 |
200OK
post/v1/me/header/uploadsStart a profile header image upload (IL media flow)Token
| Field | Type | Notes |
|---|---|---|
content_type* | string | |
bytes* | integer |
200OK
get/v1/me/emailCurrent and pending email (proxied to SubstratalApps)Token
post/v1/me/emailChange email (pending verification; proxied to SubstratalApps)Token
| Field | Type | Notes |
|---|---|---|
email* | string (email) | ≤ 254 chars |
200OK
post/v1/me/avatar/from-urlSet the avatar from an image URL: fetched server-side (SSRF-safe), then the SubstratalApps avatar flowToken
post/v1/me/avatar/uploadsStart an avatar upload (SubstratalApps avatar flow) (proxied to SubstratalApps)Token
post/v1/me/avatar/uploads/{id}/completeComplete an avatar upload (proxied to SubstratalApps)Token
delete/v1/me/avatarRemove the avatar (proxied to SubstratalApps)Token
get/v1/me/sessionsList platform sessions (proxied to SubstratalApps)Token
delete/v1/me/sessions/{id}Revoke a session (proxied to SubstratalApps)Token
post/v1/me/sessions/revoke-allRevoke all other sessions (proxied to SubstratalApps)Token
post/v1/me/mfa/totpStart TOTP enrollment (step-up) (proxied to SubstratalApps)Token
post/v1/me/mfa/totp/confirmConfirm TOTP (proxied to SubstratalApps)Token
post/v1/me/mfa/totp/disableDisable TOTP (proxied to SubstratalApps)Token
post/v1/me/passwordChange password (proxied to SubstratalApps)Token
get/v1/me/subscriptionSubstratalApps subscription summary (proxied to SubstratalApps)Token
post/v1/me/subscription/portalBilling portal session (proxied to SubstratalApps)Token
post/v1/me/deletionRequest account deletion (step-up) (proxied to SubstratalApps)Token
delete/v1/me/deletionCancel account deletion (proxied to SubstratalApps)Token
get/v1/me/devicesPush devices (proxied to SubstratalApps)Token
post/v1/me/devicesRegister a push device (proxied to SubstratalApps)Token
delete/v1/me/devices/{id}Remove a push device (proxied to SubstratalApps)Token
media
post/v1/media/uploadsStart an upload: presigned S3 POST to raw/Token
| Field | Type | Notes |
|---|---|---|
kind | "image" | "video" | "file" | default "image" |
content_type* | string | 3–100 chars |
bytes* | integer | |
purpose | "message" | "dm" | "document" | "list_cell" | "header" | "blog_hero" | "import" | default "message" |
200OK
get/v1/media/{id}Media status, variants and (signed) URLsToken
patch/v1/media/{id}Set alt textToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
alt_text* | string | null | ≤ 1500 chars |
200OK
post/v1/media/{id}/alt-text:suggestAI alt-text suggestion (async; ai.done on user channel)Token
messages
post/v1/messagesCompose a message (201 with syndication[] pending). Idempotency-Key requiredToken
| Name | In | Type | Notes |
|---|---|---|---|
Idempotency-Key* | header | string | 1–255 chars |
| Field | Type | Notes |
|---|---|---|
content | string | ≤ 5000 chars · default "" |
tags | string[] | ≤ 10 items · default [] |
visibility* | "public" | "followers" | "private" | "org" | |
org_id | string | null | pattern ^org_[0-9A-Za-z]+$ · default null |
content_warning | string | null | ≤ 200 chars · default null |
kind | "post" | "reply" | "quote" | default "post" |
parent_id | string | null | pattern ^m_[0-7][0-9A-HJKMNP-TV-Z]{25}$ · default null |
subject_id | string | null | pattern ^m_[0-7][0-9A-HJKMNP-TV-Z]{25}$ · default null |
media_ids | string[] | ≤ 8 items · default [] |
scheduled_at | string | null (date-time) | default null |
syndication | object | |
syndication.targets* | object[] | ≤ 20 items |
syndication.targets[].account_id* | string | pattern ^sa_[0-7][0-9A-HJKMNP-TV-Z]{25}$ |
syndication.targets[].target_ref | string | null | default null |
syndication.linkedin_link_as_first_comment | boolean | default false |
draft_id | string | null | pattern ^m_[0-7][0-9A-HJKMNP-TV-Z]{25}$ · default null |
link_preview | boolean | default true |
200OK
get/v1/messages/{id}A message (?expand=thread_context,subject)Public
patch/v1/messages/{id}Edit content/CW/visibility (narrowing only); scheduled messages can be rescheduled and their cross-post targets changed. If-MatchToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
content | string | ≤ 5000 chars |
content_warning | string | null | ≤ 200 chars |
visibility | "public" | "followers" | "private" | |
tags | string[] | ≤ 10 items |
scheduled_at | string | null (date-time) | |
syndicate_edit | boolean | |
syndication | object | null | |
syndication.targets* | object[] | ≤ 20 items |
syndication.targets[].account_id* | string | pattern ^sa_[0-7][0-9A-HJKMNP-TV-Z]{25}$ |
syndication.targets[].target_ref | string | null | default null |
syndication.linkedin_link_as_first_comment | boolean | default false |
200OK
delete/v1/messages/{id}Delete (soft); cross-posted copies are always deleted tooToken
get/v1/messages/{id}/editsEdit history (if the message is visible)Public
get/v1/messages/{id}/thread{ancestors[], message, replies (tree, depth ≤ 6)}Public
get/v1/messages/{id}/repliesDirect replies (paged, oldest first)Public
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
limit | query | integer | 1–100 |
cursor | query | string |
200OK
put/v1/messages/{id}/digDig (idempotent)Token
delete/v1/messages/{id}/digRemove a digToken
get/v1/messages/{id}/diggersWho dug (author only, D-12)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
limit | query | integer | 1–100 |
cursor | query | string |
200OK
post/v1/messages/{id}/pushPush (repost); public messages only. Boosts the remote copy of the original from the chosen/default accounts on each network it reachedToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
syndication | object | |
syndication.targets* | object[] | ≤ 20 items |
syndication.targets[].account_id* | string | |
syndication.targets[].target_ref | string | null | default null |
200OK
delete/v1/messages/{id}/pushUndo a pushToken
put/v1/me/bookmarks/{messageId}BookmarkToken
| Name | In | Type | Notes |
|---|---|---|---|
messageId* | path | string |
| Field | Type | Notes |
|---|---|---|
folder | string | null | ≤ 80 chars |
200OK
delete/v1/me/bookmarks/{messageId}Remove a bookmarkToken
get/v1/me/bookmarksBookmarks (?folder=)Token
| Name | In | Type | Notes |
|---|---|---|---|
limit | query | integer | 1–100 |
cursor | query | string | |
folder | query | string |
200OK
get/v1/me/draftsDrafts (autosaved)Token
post/v1/me/draftsSave a draftToken
| Field | Type | Notes |
|---|---|---|
content | string | ≤ 5000 chars · default "" |
tags | string[] | ≤ 10 items · default [] |
visibility | "public" | "followers" | "private" | default "public" |
content_warning | string | null | ≤ 200 chars · default null |
kind | "post" | "reply" | "quote" | default "post" |
parent_id | string | null | default null |
subject_id | string | null | default null |
media_ids | string[] | ≤ 8 items · default [] |
syndication | any |
200OK
get/v1/me/scheduledScheduled messages (?from=&to= window on scheduled_at; from inclusive, to exclusive)Token
| Name | In | Type | Notes |
|---|---|---|---|
limit | query | integer | 1–100 |
cursor | query | string | |
from | query | string (date-time) | |
to | query | string (date-time) |
200OK
get/v1/me/messagesMy published messages (incl. pushes), newest first, with a per-account syndication summaryToken
patch/v1/me/drafts/{id}Update a draftToken
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
content | string | ≤ 5000 chars |
tags | string[] | ≤ 10 items |
visibility | "public" | "followers" | "private" | |
content_warning | string | null | ≤ 200 chars |
kind | "post" | "reply" | "quote" | |
parent_id | string | null | |
subject_id | string | null | |
media_ids | string[] | ≤ 8 items |
syndication | any |
200OK
delete/v1/me/drafts/{id}Delete a draftToken
post/v1/me/drafts/{id}/publishPublish a draft (optionally scheduled)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
scheduled_at | string | null (date-time) |
200OK
post/v1/messages/{id}/publish-nowPublish a scheduled message nowToken
post/v1/messages/{id}/unscheduleCancel a scheduled message back to a draftToken
get/v1/me/engagementDigs, pushes and replies I received (?window=day|week|month) and recent eventsToken
| Name | In | Type | Notes |
|---|---|---|---|
window | query | "day" | "week" | "month" | default "week" |
200OK
post/v1/messages/{id}/external-counts/refreshRefresh engagement counts from cross-posted copies (at most every 10 minutes)Token
post/v1/messages/{id}/link-previews/refreshRe-fetch the link cards of your message, bypassing the preview cache (once per 5 minutes per message; link_preview.ready on the user channel)Token
get/v1/link-previewsCached preview, or 202 + fetch job (link_preview.ready on the user channel)Token
meta
get/healthShallow health check (no database call)Public
get/v1/limitsPublic fair-use limits and accepted media formats (the only source for help, pricing and composer)Public
200Limits
| Field | Type | Notes |
|---|---|---|
limits* | object (map) | |
media_formats | any |
get/v1/sitemap/{kind}Indexable public URLs with lastmod for one sitemap kind (profiles|posts|lists|documents|tags)Public
notifications
get/v1/notificationsNotification inbox (SubstratalApps), hydrated with IL objectsToken
| Name | In | Type | Notes |
|---|---|---|---|
state | query | "unread" | "read" | "archived" | |
category | query | string | ≤ 64 chars |
limit | query | integer | 1–100 · default 25 |
cursor | query | string |
200OK
get/v1/notifications/unread-countUnread countToken
patch/v1/notifications/{id}Mark read/unread/archived (IL "delete" archives)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
state* | "read" | "unread" | "archived" |
200OK
post/v1/notifications/mark-readMark read ({ids[]} or {before})Token
| Field | Type | Notes |
|---|---|---|
ids | string[] | ≤ 100 items |
before | string (date-time) |
200OK
get/v1/me/notification-preferencesNotification matrix (category × in_app/push/email)Token
put/v1/me/notification-preferencesReplace the notification matrixToken
get/v1/unsubscribeOne-click unsubscribe (?t= signed token from the email footer)Public
post/v1/unsubscribeRFC 8058 List-Unsubscribe-Post target (?t=)Public
realtime
post/v1/realtime/ticketShort-lived ticket listing allowed channels (validated by interlinedlist-rt-auth)Token
| Field | Type | Notes |
|---|---|---|
channels | string[] | ≤ 50 items · default [] |
200OK
safety
post/v1/users/{id}/reportReport a user (reporting again updates the report)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
reason* | "spam" | "harassment" | "misinformation" | "inappropriate" | "illegal" | "other" | |
detail | string | null | ≤ 2000 chars |
200OK
put/v1/me/blocks/{id}Block (removes follows both ways)Token
delete/v1/me/blocks/{id}UnblockToken
get/v1/me/blocksBlocked accountsToken
put/v1/me/mutes/{id}Mute (optionally until)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
until | string | null (date-time) |
200OK
delete/v1/me/mutes/{id}UnmuteToken
get/v1/me/mutesMuted accountsToken
get/v1/me/muted-wordsMuted wordsToken
post/v1/me/muted-wordsAdd a muted wordToken
| Field | Type | Notes |
|---|---|---|
phrase* | string | 1–100 chars |
whole_word | boolean | default false |
until | string | null (date-time) |
200OK
delete/v1/me/muted-words/{id}Remove a muted wordToken
post/v1/messages/{id}/reportReport a message (reporting again updates the report)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
reason* | "spam" | "harassment" | "misinformation" | "inappropriate" | "illegal" | "other" | |
detail | string | null | ≤ 2000 chars |
200OK
search
get/v1/searchSearch (?type=messages|people|tags|lists|documents); operators from:@h #tag has:media before:/after:YYYY-MM-DDPublic
| Name | In | Type | Notes |
|---|---|---|---|
limit | query | integer | 1–100 |
cursor | query | string | |
q* | query | string | 1–200 chars |
type | query | "messages" | "people" | "tags" | "lists" | "documents" | default "messages" |
200OK
get/v1/exploreTrending, featured, new peoplePublic
sharing
get/v1/lists/{id}/accessEffective grants, links and invites + my_role (lists)Token
post/v1/lists/{id}/grantsGrant access (manager+; up to your own role) (lists)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
principal_type* | "user" | "org" | |
principal_id | string | |
handle | string | |
role* | "viewer" | "commenter" | "editor" | "manager" | |
expires_at | string | null (date-time) | |
notify_email | boolean |
200OK
patch/v1/lists/{id}/grants/{grantId}Change a grant (lists)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
grantId* | path | string |
| Field | Type | Notes |
|---|---|---|
role | "viewer" | "commenter" | "editor" | "manager" | |
expires_at | string | null (date-time) |
200OK
delete/v1/lists/{id}/grants/{grantId}Revoke a grant (manager+, or the grantee removing themself) (lists)Token
post/v1/lists/{id}/linksCreate a share link; the token is shown once (lists)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
role* | "viewer" | "commenter" | "editor" | |
expires_at | string (date-time) | "never" | "1d" | "7d" | "30d" | null | default "never" |
password | string | 4–128 chars |
200OK
delete/v1/lists/{id}/links/{linkId}Revoke a share link (lists)Token
post/v1/lists/{id}/invitesInvite by email (lists)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
email* | string (email) | ≤ 320 chars |
role* | "viewer" | "commenter" | "editor" | "manager" | |
expires_at | string | null |
200OK
delete/v1/lists/{id}/invites/{inviteId}Revoke an invite (lists)Token
put/v1/lists/{id}/watchWatch for changes (lists)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
mode | "digest" | "immediate" | default "digest" |
200OK
delete/v1/lists/{id}/watchStop watching (lists)Token
get/v1/documents/{id}/accessEffective grants, links and invites + my_role (documents)Token
post/v1/documents/{id}/grantsGrant access (manager+; up to your own role) (documents)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
principal_type* | "user" | "org" | |
principal_id | string | |
handle | string | |
role* | "viewer" | "commenter" | "editor" | "manager" | |
expires_at | string | null (date-time) | |
notify_email | boolean |
200OK
patch/v1/documents/{id}/grants/{grantId}Change a grant (documents)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
grantId* | path | string |
| Field | Type | Notes |
|---|---|---|
role | "viewer" | "commenter" | "editor" | "manager" | |
expires_at | string | null (date-time) |
200OK
delete/v1/documents/{id}/grants/{grantId}Revoke a grant (manager+, or the grantee removing themself) (documents)Token
post/v1/documents/{id}/linksCreate a share link; the token is shown once (documents)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
role* | "viewer" | "commenter" | "editor" | |
expires_at | string (date-time) | "never" | "1d" | "7d" | "30d" | null | default "never" |
password | string | 4–128 chars |
200OK
delete/v1/documents/{id}/links/{linkId}Revoke a share link (documents)Token
post/v1/documents/{id}/invitesInvite by email (documents)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
email* | string (email) | ≤ 320 chars |
role* | "viewer" | "commenter" | "editor" | "manager" | |
expires_at | string | null |
200OK
delete/v1/documents/{id}/invites/{inviteId}Revoke an invite (documents)Token
put/v1/documents/{id}/watchWatch for changes (documents)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
mode | "digest" | "immediate" | default "digest" |
200OK
delete/v1/documents/{id}/watchStop watching (documents)Token
get/v1/list-folders/{id}/accessEffective grants, links and invites + my_role (list-folders)Token
post/v1/list-folders/{id}/grantsGrant access (manager+; up to your own role) (list-folders)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
principal_type* | "user" | "org" | |
principal_id | string | |
handle | string | |
role* | "viewer" | "commenter" | "editor" | "manager" | |
expires_at | string | null (date-time) | |
notify_email | boolean |
200OK
patch/v1/list-folders/{id}/grants/{grantId}Change a grant (list-folders)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
grantId* | path | string |
| Field | Type | Notes |
|---|---|---|
role | "viewer" | "commenter" | "editor" | "manager" | |
expires_at | string | null (date-time) |
200OK
delete/v1/list-folders/{id}/grants/{grantId}Revoke a grant (manager+, or the grantee removing themself) (list-folders)Token
post/v1/list-folders/{id}/linksCreate a share link; the token is shown once (list-folders)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
role* | "viewer" | "commenter" | "editor" | |
expires_at | string (date-time) | "never" | "1d" | "7d" | "30d" | null | default "never" |
password | string | 4–128 chars |
200OK
delete/v1/list-folders/{id}/links/{linkId}Revoke a share link (list-folders)Token
post/v1/list-folders/{id}/invitesInvite by email (list-folders)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
email* | string (email) | ≤ 320 chars |
role* | "viewer" | "commenter" | "editor" | "manager" | |
expires_at | string | null |
200OK
delete/v1/list-folders/{id}/invites/{inviteId}Revoke an invite (list-folders)Token
put/v1/list-folders/{id}/watchWatch for changes (list-folders)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
mode | "digest" | "immediate" | default "digest" |
200OK
delete/v1/list-folders/{id}/watchStop watching (list-folders)Token
get/v1/doc-folders/{id}/accessEffective grants, links and invites + my_role (doc-folders)Token
post/v1/doc-folders/{id}/grantsGrant access (manager+; up to your own role) (doc-folders)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
principal_type* | "user" | "org" | |
principal_id | string | |
handle | string | |
role* | "viewer" | "commenter" | "editor" | "manager" | |
expires_at | string | null (date-time) | |
notify_email | boolean |
200OK
patch/v1/doc-folders/{id}/grants/{grantId}Change a grant (doc-folders)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
grantId* | path | string |
| Field | Type | Notes |
|---|---|---|
role | "viewer" | "commenter" | "editor" | "manager" | |
expires_at | string | null (date-time) |
200OK
delete/v1/doc-folders/{id}/grants/{grantId}Revoke a grant (manager+, or the grantee removing themself) (doc-folders)Token
post/v1/doc-folders/{id}/linksCreate a share link; the token is shown once (doc-folders)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
role* | "viewer" | "commenter" | "editor" | |
expires_at | string (date-time) | "never" | "1d" | "7d" | "30d" | null | default "never" |
password | string | 4–128 chars |
200OK
delete/v1/doc-folders/{id}/links/{linkId}Revoke a share link (doc-folders)Token
post/v1/doc-folders/{id}/invitesInvite by email (doc-folders)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
email* | string (email) | ≤ 320 chars |
role* | "viewer" | "commenter" | "editor" | "manager" | |
expires_at | string | null |
200OK
delete/v1/doc-folders/{id}/invites/{inviteId}Revoke an invite (doc-folders)Token
put/v1/doc-folders/{id}/watchWatch for changes (doc-folders)Token
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
mode | "digest" | "immediate" | default "digest" |
200OK
delete/v1/doc-folders/{id}/watchStop watching (doc-folders)Token
get/v1/invites/{token}Resolve an email invite for its landing pagePublic
post/v1/invites/{token}/acceptAccept an email invite (verified email must match)Token
social
post/v1/users/{id}/followFollow (returns accepted|pending)Token
delete/v1/users/{id}/followUnfollow or cancel a requestToken
get/v1/me/follow-requests/countPending follow request counts {incoming, outgoing}Token
get/v1/me/follow-requestsFollow requests (?direction=incoming|outgoing)Token
| Name | In | Type | Notes |
|---|---|---|---|
limit | query | integer | 1–100 |
cursor | query | string | |
direction | query | "incoming" | "outgoing" | default "incoming" |
200OK
post/v1/me/follow-requests/{usr}/approveApprove a follow requestToken
post/v1/me/follow-requests/{usr}/rejectReject a follow requestToken
delete/v1/me/followers/{id}Remove a followerToken
subscribe
get/v1/subscribe/pricesSubstratalApps Standard prices (live)Public
post/v1/subscribe/checkoutStart checkout (platform session; Idempotency-Key required)Token
post/v1/subscribe/confirmConfirm checkout and mint accessToken
tags
put/v1/me/tag-follows/{tag}Follow a tagToken
delete/v1/me/tag-follows/{tag}Unfollow a tagToken
get/v1/me/tag-followsFollowed tagsToken
timeline
get/v1/timelineTimeline (?tab=following|everything|mine|followers); keysetPublic
| Name | In | Type | Notes |
|---|---|---|---|
limit | query | integer | 1–100 |
cursor | query | string | |
tab | query | "following" | "everything" | "mine" | "followers" | default "following" |
200OK
get/v1/timeline/updatesCount + newest id since a message id (realtime covers this normally)Token
| Name | In | Type | Notes |
|---|---|---|---|
since* | query | string | |
tab | query | "following" | "everything" | default "following" |
200OK
users
get/v1/users/suggestionsWho to followToken
get/v1/users/{idOrHandle}Public profile (viewer-aware; private accounts redacted)Public
| Name | In | Type | Notes |
|---|---|---|---|
idOrHandle* | path | string |
200Profile
| Field | Type | Notes |
|---|---|---|
id* | string | pattern ^u_[0-7][0-9A-HJKMNP-TV-Z]{25}$ |
handle* | string | pattern ^[a-z0-9_]{3,30}$ |
display_name* | string | ≤ 100 chars |
avatar_url* | string | null (uri) | |
header_url* | string | null (uri) | |
bio* | string | null | ≤ 500 chars |
links* | object[] | ≤ 4 items |
links[].url* | string (uri) | ≤ 2048 chars |
links[].label | string | ≤ 60 chars |
links[].verified | boolean | default false |
pronouns* | string | null | ≤ 40 chars |
location_label* | string | null | ≤ 100 chars |
is_private* | boolean | |
noindex* | boolean | |
inactive* | boolean | |
redacted* | boolean | |
counts* | object | |
counts.messages* | integer | ≥ 0 |
counts.followers* | integer | ≥ 0 |
counts.following* | integer | ≥ 0 |
counts.lists | integer | ≥ 0 |
counts.documents | integer | ≥ 0 |
pinned* | object[] | ≤ 3 items |
pinned[].type* | "message" | "list" | "document" | |
pinned[].id* | string | |
created_at* | string (date-time) | |
viewer | object | |
viewer.following* | "none" | "pending" | "accepted" | |
viewer.followed_by* | boolean | |
viewer.blocking* | boolean | |
viewer.muting* | boolean |
get/v1/users/{id}/messagesA user's messages (?kind=posts|replies|media|dug)Public
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
limit | query | integer | 1–100 |
cursor | query | string | |
kind | query | "posts" | "replies" | "media" | "dug" | default "posts" |
200OK
get/v1/users/{id}/listsA user's public listsPublic
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
limit | query | integer | 1–100 |
cursor | query | string |
200OK
get/v1/users/{id}/documentsA user's public documentsPublic
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
limit | query | integer | 1–100 |
cursor | query | string |
200OK
get/v1/users/{id}/blogA user's published blog postsPublic
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
limit | query | integer | 1–100 |
cursor | query | string |
200OK
get/v1/users/{handle}/lists/{slug}A list by owner handle and slug (or id)Public
get/v1/users/{handle}/documents/{slug}A document by owner handle and slug (or id)Public
get/v1/users/{handle}/blog/{slug}A published blog post by slugPublic
get/v1/users/{id}/followersfollowers (honors privacy)Public
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
limit | query | integer | 1–100 |
cursor | query | string |
200OK
get/v1/users/{id}/followingfollowing (honors privacy)Public
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
limit | query | integer | 1–100 |
cursor | query | string |
200OK
get/v1/users/{id}/mutualsmutuals (honors privacy)Public
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string | |
limit | query | integer | 1–100 |
cursor | query | string |
200OK
webhooks
post/hooks/substratalSubstratalApps webhook receiver (signed, de-duplicated, handled inline)
post/hooks/githubGitHub webhooks for GitHub-backed lists
post/hooks/sesSES bounce/complaint notifications via SNS
admin
get/v1/admin/reportsReport queue (?status=)Admin
Link to this endpoint · Requires an app role (moderator/admin).
| Name | In | Type | Notes |
|---|---|---|---|
status | query | "open" | "in_review" | "actioned" | "dismissed" | default "open" |
limit | query | integer | 1–100 · default 50 |
200OK
get/v1/admin/reports/{id}A report with snapshot and related actionsAdmin
Link to this endpoint · Requires an app role (moderator/admin).
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
200OK
post/v1/admin/reports/{id}/actionsAct on a reportAdmin
Link to this endpoint · Requires an app role (moderator/admin).
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
action* | "dismiss" | "in_review" | "remove_content" | "warn" | "restrict" | "suspend" | "ban" | |
reason | string | null | ≤ 2000 chars |
until | string | null (date-time) |
200OK
get/v1/admin/appealsAppeals queueAdmin
Link to this endpoint · Requires an app role (moderator/admin).
200OK
post/v1/admin/appeals/{id}/decideDecide an appealAdmin
Link to this endpoint · Requires an app role (moderator/admin).
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
decision* | "upheld" | "overturned" | |
note | string | ≤ 2000 chars |
200OK
get/v1/admin/usersFind users (?q=&standing=&verified=&sort=&page=&limit=&counts=1)Admin
Link to this endpoint · Requires an app role (moderator/admin).
| Name | In | Type | Notes |
|---|---|---|---|
q | query | string | ≤ 100 chars · default "" |
standing | query | "active" | "restricted" | "suspended" | "banned" | |
verified | query | "true" | "false" | |
sort | query | "created_desc" | "created_asc" | "handle" | default "created_desc" |
counts | query | "0" | "1" | default "0" |
page | query | integer | 1–10000 · default 1 |
limit | query | integer | 1–100 · default 50 |
200OK
get/v1/admin/users/{id}User detail (staff view)Admin
Link to this endpoint · Requires an app role (moderator/admin).
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
200OK
post/v1/admin/users/{id}/standingChange standingAdmin
Link to this endpoint · Requires an app role (moderator/admin).
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
standing* | "active" | "restricted" | "suspended" | "banned" | |
reason | string | null | ≤ 2000 chars |
until | string | null (date-time) |
200OK
post/v1/admin/users:bulk-standingBulk standing changeAdmin
Link to this endpoint · Requires an app role (moderator/admin).
| Field | Type | Notes |
|---|---|---|
standing* | "active" | "restricted" | "suspended" | "banned" | |
reason | string | null | ≤ 2000 chars |
until | string | null (date-time) | |
user_ids* | string[] | 1–200 items |
200OK
post/v1/admin/users:bulk-spamMark accounts as spam: ban and remove their messagesAdmin
Link to this endpoint · Requires an app role (moderator/admin).
| Field | Type | Notes |
|---|---|---|
user_ids* | string[] | 1–200 items |
dry_run | boolean | default true |
200OK
post/v1/admin/purgePurge chosen content of a user (dry_run by default)Admin
Link to this endpoint · Requires an app role (moderator/admin).
| Field | Type | Notes |
|---|---|---|
user_id* | string | |
categories | "messages" | "lists" | "documents" | "media"[] | ≥ 1 items · default ["messages","lists","documents","media"] |
dry_run | boolean | default true |
200OK
post/v1/admin/users:bulk-purgePurge chosen content of many users (dry_run by default)Admin
Link to this endpoint · Requires an app role (moderator/admin).
| Field | Type | Notes |
|---|---|---|
user_ids* | string[] | 1–200 items |
categories | "messages" | "lists" | "documents" | "media"[] | ≥ 1 items · default ["messages","lists","documents","media"] |
dry_run | boolean | default true |
200OK
get/v1/admin/auditIL audit merged with the SubstratalApps audit (?source=il|platform|both)Admin
Link to this endpoint · Requires an app role (moderator/admin).
| Name | In | Type | Notes |
|---|---|---|---|
source | query | "il" | "platform" | "both" | default "both" |
limit | query | integer | 1–200 · default 100 |
200OK
get/v1/admin/blocked-email-domainsBlocked email domainsAdmin
Link to this endpoint · Requires an app role (moderator/admin).
200OK
post/v1/admin/blocked-email-domainsBlock an email domainAdmin
Link to this endpoint · Requires an app role (moderator/admin).
| Field | Type | Notes |
|---|---|---|
domain* | string | pattern ^[a-z0-9.-]+\.[a-z]{2,}$/i |
reason | string | null | ≤ 200 chars |
200OK
delete/v1/admin/blocked-email-domains/{domain}Unblock a domainAdmin
Link to this endpoint · Requires an app role (moderator/admin).
| Name | In | Type | Notes |
|---|---|---|---|
domain* | path | string |
200OK
get/v1/admin/reserved-handlesReserved handlesAdmin
Link to this endpoint · Requires an app role (moderator/admin).
200OK
post/v1/admin/reserved-handlesReserve a handleAdmin
Link to this endpoint · Requires an app role (moderator/admin).
| Field | Type | Notes |
|---|---|---|
handle* | string | |
reason | string | null | ≤ 200 chars |
200OK
get/v1/admin/email-logEmail log with filters (?status=&template=&since=&recipient=&page=); hashed recipientsAdmin
Link to this endpoint · Requires an app role (moderator/admin).
| Name | In | Type | Notes |
|---|---|---|---|
status | query | "sent" | "delivered" | "bounce" | "complaint" | "suppressed" | "deferred_unverified" | |
template | query | string | ≤ 64 chars |
since | query | "today" | "7d" | "30d" | "all" | default "all" |
recipient | query | string | ≤ 320 chars |
page | query | integer | 1–10000 · default 1 |
limit | query | integer | 1–100 · default 50 |
200OK
get/v1/admin/platform/usageSubstratalApps API usage (fetched on demand)Admin
Link to this endpoint · Requires an app role (moderator/admin).
200OK
get/v1/admin/limitsEffective limits and overridesAdmin
Link to this endpoint · Requires an app role (moderator/admin).
200OK
put/v1/admin/limitsReplace limit overrides (camelCase keys of the limits registry; body is {overrides} or the overrides object)Admin
Link to this endpoint · Requires an app role (moderator/admin).
Request body (application/json): any JSON
200OK
get/v1/admin/flagsFeature flagsAdmin
Link to this endpoint · Requires an app role (moderator/admin).
200OK
put/v1/admin/flagsReplace feature flags (body is {flags} or the flags object)Admin
Link to this endpoint · Requires an app role (moderator/admin).
Request body (application/json): any JSON
200OK
get/v1/admin/blogOfficial blog posts in any state (same officialness rule as /v1/blog)Admin
Link to this endpoint · Requires an app role (moderator/admin).
200OK
put/v1/admin/blog/{id}/officialAdd a blog post to, or remove it from, the official blogAdmin
Link to this endpoint · Requires an app role (moderator/admin).
| Name | In | Type | Notes |
|---|---|---|---|
id* | path | string |
| Field | Type | Notes |
|---|---|---|
official* | boolean |
200OK