Endpoints
API v1 endpoint reference.
All endpoints are under:
/api/v1Me
GET /meReturns the profile, enabled scopes, and credit status for the bearer token.
Collections
GET /collections
GET /collections/:id
POST /collectionsGET requires collections:read. POST requires collections:write for API
keys; an authenticated Nestornotes OAuth session may create its own collection.
Create request:
{
"name": "AI Research",
"description": "Articles and source updates about AI",
"nestornotesEmail": "ai-research@nnotes.me"
}Collection responses use public camelCase fields and omit internal owner fields:
{
"id": "collection-id",
"name": "AI Research",
"slug": "ai-research",
"description": "Articles and source updates about AI",
"nestornotesEmail": "ai-research@nnotes.me",
"defaultCollection": false,
"defaultSetNestornotesEmail": null,
"scheduleType": "daily",
"scheduleTime": 8,
"scheduleDay": null,
"scheduleTimezone": "Europe/Berlin",
"sendEmailSummaries": true
}Knowledge
GET /knowledge
GET /knowledge/:id
PATCH /knowledge/:idGET requires knowledge:read. PATCH requires knowledge:write.
List filters:
collection_idobject_typesummarizedbookmarkedarchivedseentag_idcreated_aftercreated_beforeupdated_afterupdated_beforelimitoffset
Mutable fields:
{
"seen": true,
"bookmarked": true,
"archived": false,
"include_in_digest": true
}Knowledge responses use public camelCase fields and omit internal owner fields:
{
"id": "knowledge-id",
"objectId": "article-id",
"objectType": "article",
"collectionId": "collection-id",
"summarized": true,
"includeInDigest": true,
"sentInDigest": false,
"seen": false,
"bookmarked": true,
"archived": false,
"storedInDocuments": false,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-03T00:00:00.000Z"
}Tags
GET /tags
GET /knowledge/:id/tags
POST /knowledge/:id/tags
DELETE /knowledge/:id/tags/:tag_id
GET /sources/:source_type/:source_id/tags?collection_id=:collection_id
POST /sources/:source_type/:source_id/tags?collection_id=:collection_id
DELETE /sources/:source_type/:source_id/tags/:tag_id?collection_id=:collection_idReads require tags:read. Mutations require tags:write for API keys and also accept an authenticated Supabase session.
Source tags support rss_channel, youtube_channel, x_account,
linkedin_source, podcast_show, and website_monitor. They are scoped to
the owner and collection. New items inherit the current assignments; removing
an assignment does not remove tags from already collected items.
Create or attach a tag by name:
{
"name": "Important"
}Tag mutation responses return public camelCase link fields and omit owner fields:
{
"id": "knowledge-tag-link-id",
"knowledgeId": "knowledge-id",
"tagId": "tag-id",
"tag": {
"id": "tag-id",
"name": "Important",
"color": ""
}
}Source-tag mutation responses use the same embedded tag shape plus source identity:
{
"id": "source-tag-link-id",
"collectionId": "collection-id",
"sourceId": "source-id",
"sourceType": "rss_channel",
"tagId": "tag-id",
"tag": {
"id": "tag-id",
"name": "Important",
"color": ""
}
}Search
POST /search/keyword
POST /search/semanticRequires search:read.
{
"query": "recent posts about vector databases",
"collection_id": "optional-collection-id",
"limit": 10
}Semantic search also accepts idempotency_key and consumes one credit.
Search responses return public result DTOs:
{
"id": "document-id",
"content": "A matching document snippet",
"metadata": {
"object_id": "article-id"
},
"similarity": 0.91
}Subscribed Sources
GET /rss/channels
GET /youtube/channels
GET /x/accounts
GET /linkedin/sources
GET /podcasts/showsThese endpoints list profile-owned subscriptions and accept collection_id, limit, and offset. LinkedIn and podcast source results include subscription status and errorMessage fields; podcast rows also include showSubscriptionId, which can be used to unsubscribe while a new show is still resolving.
Each subscribed source also has a profile-scoped detail endpoint:
GET /rss/channels/:id
GET /youtube/channels/:id
GET /x/accounts/:id
GET /linkedin/sources/:id
GET /podcasts/shows/:idUse the canonical source ID returned by the corresponding list endpoint. Podcast show detail also accepts showSubscriptionId, allowing resolving or failed subscriptions without a canonical show ID to be inspected. RSS and YouTube details include feed-health diagnostics; LinkedIn and podcast details include resolution status and errors; X details include fetch and availability metadata.
Source subscription mutations are available under:
POST /rss/subscriptions
DELETE /rss/subscriptions
POST /youtube/subscriptions
DELETE /youtube/subscriptions
POST /x/subscriptions
DELETE /x/subscriptions
POST /linkedin/subscriptions
DELETE /linkedin/subscriptions
POST /podcasts/subscriptions
DELETE /podcasts/subscriptionsFor example, a LinkedIn subscription request uses:
{
"linkedinUrl": "https://www.linkedin.com/company/example",
"collectionId": "collection-id"
}A podcast subscription request uses a public provider or RSS show URL:
{
"sourceUrl": "https://open.spotify.com/show/example",
"collectionId": "collection-id"
}Podcast unsubscribe requests use the stable subscription ID:
{
"subscriptionId": "podcast-subscription-id"
}Newsletter signup automation is available separately:
POST /newsletters/subscriptions{
"newsletterUrl": "https://example.com/newsletter",
"collectionId": "collection-id"
}The endpoint returns HTTP 202 with status: "started" and a Browser-Use task ID. This confirms that automation started; it does not confirm that the external newsletter accepted the signup.
Website Monitoring
Website monitor reads require websites:read. Mutations require
websites:write for API keys and also accept an authenticated first-party
Supabase session.
GET /websites/monitors
POST /websites/monitors
GET /websites/monitors/:id
PATCH /websites/monitors/:id
DELETE /websites/monitors/:id
GET /websites/changes
GET /websites/changes/:idMonitor lists accept collection_id, status, limit, and offset. Change
lists accept the standard knowledge filters plus monitor_id, collection and
date filters, limit, and offset.
{
"url": "https://example.com/pricing",
"collectionId": "collection-id"
}A new monitor returns HTTP 201; submitting the same normalized URL to the
same collection returns the existing active monitor with HTTP 200. The first
successful check creates a baseline and does not create a change item.
Pause or resume a monitor, or explicitly approve a robots override:
{
"status": "active",
"allowRobotsOverride": true
}Removing a monitor stops future checks but preserves its existing change
history. Change details return compact diff statistics, structured before/after
blocks, a diffTruncated flag, and the AI report when ready. They never expose
raw HTML, complete snapshots, profile IDs, private storage paths, prompts, or
provider payloads. A successfully delivered change costs 2 credits per profile;
insufficient-credit changes are not returned and are not backfilled later.
Typed Content
Typed content endpoints expose the source-specific rows behind knowledge metadata.
GET /articles
GET /articles/:id
GET /emails
GET /emails/:id
GET /rss/items
GET /rss/items/:id
GET /youtube/videos
GET /youtube/videos/:id
GET /x/posts
GET /x/posts/:id
GET /linkedin/posts
GET /linkedin/posts/:id
GET /podcasts/episodes
GET /podcasts/episodes/:idRequired scopes:
| Endpoints | Scope |
|---|---|
/articles/** | articles:read |
/emails/** | emails:read |
/rss/items/** | rss:read |
/youtube/videos/** | youtube:read |
/x/posts/** | x:read |
/linkedin/posts/** | linkedin:read |
/podcasts/episodes/** | podcasts:read |
List filters:
collection_idarchivedbookmarkedsummarizedfromtolimitoffset
Source-specific list filters:
- RSS items:
channel_id - YouTube videos:
channel_id - X posts:
account_id - LinkedIn posts:
source_id - Podcast episodes:
show_id
Detail ids are profile-scoped item ids for source items: profile_item_id, profile_post_id, or profile_episode_id. Articles use article id; emails use incoming_email_id.
Typed content responses use public camelCase DTO fields. Article lists return metadata state, while article details return summary content together with the stored article body and MHTML storage path:
{
"id": "article-id",
"name": "Saved article",
"articleUrl": "https://example.com/saved-article",
"collectionId": "collection-id",
"content": "The full saved article body...",
"mhtmlPath": "s3://article-bucket/articles/profile-id/article-id.mhtml",
"articleSummaryId": "article-summary-id",
"shortDescription": "A short summary",
"oneMinuteSummary": "A one-minute summary",
"threeMinuteSummary": "A three-minute summary",
"createdAt": "2026-01-03T00:00:00.000Z"
}Email list responses omit the full HTML body; email detail responses include htmlEmail:
{
"incomingEmailId": "email-id",
"subject": "Product update",
"emailFrom": "sender@example.com",
"emailReceivedAt": "2026-01-05T00:00:00.000Z",
"includeInDigest": true,
"shortDescription": "A short summary"
}RSS item responses use profileItemId as the detail id and include worker-owned
fetch/fallback status fields:
{
"profileItemId": "rss-profile-item-id",
"id": "rss-item-id",
"rssChannelId": "rss-channel-id",
"title": "Feed item title",
"link": "https://example.com/post",
"pubDate": "2026-01-06T00:00:00.000Z",
"includeInDigest": true,
"processingStatus": "summarized",
"articleFetchStatus": "fetched",
"fallbackUsed": false,
"contentSource": "article"
}YouTube video responses use profileItemId as the detail id and expose public video
metadata plus summary state:
{
"profileItemId": "youtube-profile-item-id",
"id": "youtube-item-id",
"videoId": "youtube-video-id",
"youtubeChannelId": "youtube-channel-id",
"title": "Video title",
"publishedAt": "2026-01-06T00:00:00.000Z",
"thumbnailUrl": "https://img.youtube.com/vi/youtube-video-id/default.jpg",
"duration": "PT5M",
"viewCount": 100,
"includeInDigest": true
}X, LinkedIn, and podcast responses also expose public source metadata and summary state through camelCase fields. Provider raw blobs are omitted.
{
"profileItemId": "x-profile-item-id",
"id": "x-item-id",
"xAccountId": "x-account-id",
"title": "X post title",
"bodyText": "Post body text",
"postUrl": "https://x.com/example/status/123",
"sourceStatus": "fetched",
"sourceUrls": ["https://example.com/source"],
"includeInDigest": true
}{
"profilePostId": "linkedin-profile-post-id",
"id": "linkedin-post-id",
"linkedinSourceId": "linkedin-source-id",
"sourceName": "Example Company",
"postUrl": "https://www.linkedin.com/feed/update/...",
"text": "Post text",
"postedAt": "2026-01-08T00:00:00.000Z",
"includeInDigest": true
}{
"profileEpisodeId": "podcast-profile-episode-id",
"id": "podcast-episode-id",
"podcastShowId": "podcast-show-id",
"showTitle": "Example Podcast",
"title": "Episode title",
"publishedAt": "2026-01-09T00:00:00.000Z",
"transcriptStatus": "completed",
"transcriptProvider": "assemblyai",
"includeInDigest": true
}Credits And Usage
GET /credits
GET /usageGET /credits requires credits:read and returns the current credit status plus active action costs.
GET /usage requires usage:read.
Credit responses use public camelCase fields:
{
"status": {
"creditsRemaining": 10,
"creditsLimit": 10
},
"actionCosts": [
{
"actionType": "api_semantic_search",
"credits": 1,
"description": "Semantic search"
}
]
}Usage filters:
action_typefromtolimitoffset
Usage responses also use camelCase fields and omit internal owner fields:
{
"id": "usage-id",
"actionType": "api_semantic_search",
"credits": 1,
"objectType": "api_key",
"objectId": "api-key-id",
"idempotencyKey": "idempotency-key",
"metadata": {},
"periodStart": "2026-01-01T00:00:00.000Z",
"periodEnd": "2026-02-01T00:00:00.000Z",
"createdAt": "2026-01-07T00:00:00.000Z"
}