{"openapi":"3.1.0","info":{"title":"nowreading.dev API","summary":"Agent-native reading list: read any user's public list, or curate a user's list on their behalf.","description":"The nowreading.dev HTTP API lets AI agents and developers:\n\n- **Read** any user's public reading list as JSON (no auth required).\n- **Write** (create, update, delete, refresh) to a user's list with a per-user bearer token.\n\nAgents should first call `GET /api/me` to verify the token and discover the handle it represents.\nEvery reading list is also published as RSS, Atom, JSON Feed, Markdown, plain text, and llms.txt at `/:handle.*`.\n\n**Rate limits** are returned on every response via `X-RateLimit-Limit`, `X-RateLimit-Remaining`,\nand `X-RateLimit-Reset` headers. Agents receiving `429 Too Many Requests` should respect the\n`Retry-After` header.\n","version":"1.0.0","contact":{"name":"nowreading.dev","email":"v@codenamev.com","url":"https://nowreading.dev"},"license":{"name":"Proprietary"},"x-logo":{"url":"https://nowreading.dev/icon.png"}},"servers":[{"url":"https://nowreading.dev","description":"Production"}],"externalDocs":{"description":"Markdown API documentation (llms.txt)","url":"https://nowreading.dev/llms.txt"},"security":[{"bearerAuth":[]}],"tags":[{"name":"me","description":"Token-holder identity & discovery."},{"name":"articles","description":"Create, read, update, and delete the authenticated user's articles."},{"name":"users","description":"Public reads of other users' reading lists (no auth)."}],"paths":{"/api/me":{"get":{"operationId":"getMe","tags":["me"],"summary":"Verify bearer token and discover the handle it represents.","description":"Returns the handle, reading-list URL, feed URLs, and article count for the authenticated user. Call this first after a token handoff.","security":[{"bearerAuth":[]}],"responses":{"200":{"$ref":"#/components/responses/MeResponse"},"401":{"$ref":"#/components/responses/UnauthorizedError"}}}},"/api/articles":{"get":{"operationId":"listArticles","tags":["articles"],"summary":"List the authenticated user's articles (including drafts).","security":[{"bearerAuth":[]}],"parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1},"description":"1-based page number. Page size is 50."}],"responses":{"200":{"$ref":"#/components/responses/ArticleListResponse"},"401":{"$ref":"#/components/responses/UnauthorizedError"}}},"post":{"operationId":"createArticle","tags":["articles"],"summary":"Add an article to the authenticated user's reading list.","description":"If the URL already exists on the user's list, returns 200 with `{ message: 'Already saved.' }`. Otherwise returns 201. Metadata extraction runs asynchronously; the first response may have partial fields.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticleCreateRequest"},"examples":{"basic":{"value":{"url":"https://example.com/post"}},"withNotes":{"value":{"url":"https://example.com/post","notes":"Revisit before next demo."}}}}}},"responses":{"200":{"$ref":"#/components/responses/ArticleResponse"},"201":{"$ref":"#/components/responses/ArticleResponse"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/api/articles/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"patch":{"operationId":"updateArticle","tags":["articles"],"summary":"Update one of the authenticated user's articles.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticleUpdateRequest"}}}},"responses":{"200":{"$ref":"#/components/responses/ArticleResponse"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"},"422":{"$ref":"#/components/responses/ValidationError"}}},"delete":{"operationId":"deleteArticle","tags":["articles"],"summary":"Remove an article from the authenticated user's reading list.","security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Deleted."},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/api/articles/{id}/refresh_metadata":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"post":{"operationId":"refreshArticleMetadata","tags":["articles"],"summary":"Queue a metadata re-extraction for an article.","description":"Returns 202 immediately; the extraction runs asynchronously.","security":[{"bearerAuth":[]}],"responses":{"202":{"$ref":"#/components/responses/ArticleResponse"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}},"/api/users/{handle}/articles":{"get":{"operationId":"listUserArticles","tags":["users"],"summary":"List any user's published articles (public; no auth).","description":"Returns the 50 most recent published articles. Equivalent to `/:handle.json` (JSON Feed 1.1).","security":[],"parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-zA-Z0-9_-]+$"}}],"responses":{"200":{"$ref":"#/components/responses/PublicArticleListResponse"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Opaque per-user token (has_secure_token)","description":"Per-user bearer token. The human user reveals it at /token and gives it to the agent."}},"parameters":{},"schemas":{"Article":{"type":"object","required":["id","url","added_at"],"properties":{"id":{"type":"integer","example":42},"url":{"type":"string","format":"uri","example":"https://example.com/post"},"title":{"type":"string","nullable":true,"example":"Diffusion based LLMs are here"},"summary":{"type":"string","nullable":true},"author":{"type":"string","nullable":true},"source":{"type":"string","nullable":true,"description":"Canonical site or publisher."},"publication_date":{"type":"string","format":"date-time","nullable":true,"description":"When the original source was published."},"content_type":{"type":"string","nullable":true,"example":"article"},"length":{"type":"integer","nullable":true,"description":"Word count if known."},"keywords":{"type":"array","items":{"type":"string"},"nullable":true},"added_at":{"type":"string","format":"date-time","description":"When the article entered the reading list."},"notes":{"type":"string","nullable":true,"description":"Owner-only."},"draft":{"type":"boolean","description":"Owner-only. Drafts are excluded from public feeds."},"updated_at":{"type":"string","format":"date-time","description":"Owner-only."}}},"ArticleCreateRequest":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"notes":{"type":"string"}}},"ArticleUpdateRequest":{"type":"object","properties":{"notes":{"type":"string"},"draft":{"type":"boolean"},"title":{"type":"string"},"summary":{"type":"string"}}},"ArticleListResponse":{"type":"object","required":["page","page_size","articles"],"properties":{"page":{"type":"integer","minimum":1},"page_size":{"type":"integer","example":50},"articles":{"type":"array","items":{"$ref":"#/components/schemas/Article"}}}},"PublicArticleListResponse":{"type":"object","description":"JSON Feed 1.1 formatted reading list.","properties":{"version":{"type":"string","example":"https://jsonfeed.org/version/1.1"},"title":{"type":"string"},"home_page_url":{"type":"string","format":"uri"},"feed_url":{"type":"string","format":"uri"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","format":"uri"},"title":{"type":"string"},"content_text":{"type":"string"},"date_published":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"type":"string"}}}}}}},"ArticleCreateOrShowResponse":{"type":"object","properties":{"message":{"type":"string","example":"Article added!"},"article":{"$ref":"#/components/schemas/Article"}}},"MeResponse":{"type":"object","required":["handle","reading_list_url","article_count","feeds"],"properties":{"handle":{"type":"string","pattern":"^[a-zA-Z0-9_-]+$"},"reading_list_url":{"type":"string","format":"uri"},"llms_txt_url":{"type":"string","format":"uri"},"docs_url":{"type":"string","format":"uri"},"article_count":{"type":"integer"},"feeds":{"type":"object","properties":{"rss":{"type":"string","format":"uri"},"atom":{"type":"string","format":"uri"},"json":{"type":"string","format":"uri"},"md":{"type":"string","format":"uri"},"text":{"type":"string","format":"uri"}}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Stable machine-readable error code.","example":"unauthorized"},"resolution":{"type":"string","description":"Suggested agent action to resolve the error."},"docs_url":{"type":"string","format":"uri","description":"Link to relevant documentation."}}},"ValidationErrorBody":{"type":"object","required":["errors"],"properties":{"errors":{"type":"array","items":{"type":"string"}},"code":{"type":"string","example":"validation_failed"},"resolution":{"type":"string"}}}},"responses":{"MeResponse":{"description":"Token is valid; returns the owning handle and feed URLs.","headers":{"X-RateLimit-Limit":{"description":"Request quota per window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Epoch seconds when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}},"ArticleListResponse":{"description":"List of the authenticated user's articles.","headers":{"X-RateLimit-Limit":{"description":"Request quota per window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Epoch seconds when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticleListResponse"}}}},"PublicArticleListResponse":{"description":"Public JSON Feed for the given handle.","headers":{"X-RateLimit-Limit":{"description":"Request quota per window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Epoch seconds when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicArticleListResponse"}}}},"ArticleResponse":{"description":"A single article with optional message.","headers":{"X-RateLimit-Limit":{"description":"Request quota per window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Epoch seconds when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticleCreateOrShowResponse"}}}},"UnauthorizedError":{"description":"Missing or invalid bearer token.","headers":{"X-RateLimit-Limit":{"description":"Request quota per window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Epoch seconds when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFoundError":{"description":"Resource does not exist or is not visible to this token.","headers":{"X-RateLimit-Limit":{"description":"Request quota per window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Epoch seconds when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ValidationError":{"description":"Request failed validation.","headers":{"X-RateLimit-Limit":{"description":"Request quota per window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Epoch seconds when the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorBody"}}}}},"headers":{"X-RateLimit-Limit":{"description":"Request quota per window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Epoch seconds when the window resets.","schema":{"type":"integer"}}}}}