Troubleshooting
If a request fails, run zero doctor check-connector --env-name WEREAD_TOKEN or
zero doctor check-connector --url "https://i.weread.qq.com/api/agent/gateway".
A non-zero errcode in the JSON response means the request failed — surface the
message to the user. A 401/403 usually means the API key is missing, expired,
or not connected.
Official Docs
- WeRead Skill page: https://weread.qq.com/r/weread-skills
Prerequisites
Connect the WeRead connector at app.vm0.ai/connectors.
The API key is obtained by opening the WeRead Skill page,
scanning the QR code with WeChat to sign in, and copying the generated key (format
wrk-xxxxxxxx).
The connector provides:
WEREAD_TOKEN— thewrk-API key, bound to one WeChat Reading account
Gateway API
WeRead exposes every capability through a single gateway endpoint. There is no
path-based REST — the api_name field in the request body selects the sub-API.
Endpoint and auth
POST https://i.weread.qq.com/api/agent/gateway
Authorization: Bearer $WEREAD_TOKEN
Content-Type: application/json
The API key is bound to the user's WeRead identity (vid). Endpoints that need a
user identity resolve it automatically from the key — never pass vid manually.
Request format
- Method
POST,Content-Type: application/json. - Body is a flat JSON object:
api_nameselects the sub-API, business parameters are flattened at the top level, and every request must includeskill_version(use1.0.3).
curl -X POST "https://i.weread.qq.com/api/agent/gateway" \
-H "Authorization: Bearer $WEREAD_TOKEN" \
-H "Content-Type: application/json" \
-d '{"api_name": "/store/search", "keyword": "三体", "skill_version": "1.0.3"}'
Correct — business params flattened next to api_name:
{"api_name":"/user/notebooks","count":100,"lastSort":1516907353,"skill_version":"1.0.3"}
Wrong — do NOT wrap params in params/data/body. The backend will not
forward them and silently returns the first page with defaults:
{"api_name":"/user/notebooks","params":{"count":100},"skill_version":"1.0.3"}
Only array/object fields explicitly declared by an endpoint (such as the reviews
array of /book/readreviews) may be passed as nested business fields.
Response format
- JSON, trimmed to core fields.
errcode != 0means an error — give the user a clear message.POST {"api_name":"/_list","skill_version":"1.0.3"}returns every available sub-API with its parameter definitions — use it to discover or verify endpoints.- If a response contains an
upgrade_infofield, the bundled skill is behind the server. Finish the user's request on a best-effort basis, then tell the user the WeRead connector skill may need an update — do not attempt to self-download.
General rules
- Read this file before calling an endpoint. Field names are easy to misread (units, counting scope); follow the field notes here rather than guessing from the name.
- bookId resolution: when the user gives a book title, call
/store/searchfirst to get thebookId, then continue. - Context carry-over: remember
bookIds already resolved in the conversation so the user need not repeat them. - Timestamps: every Unix timestamp field (
updateTime,createTime,finishTime,readUpdateTime, etc.) must be displayed asYYYY-MM-DD, never the raw number. - Reading durations: all duration fields are in seconds — display as "X hours Y minutes". Never treat them as minutes or hours.
Capabilities
| Capability | Sub-API | Notes |
|---|---|---|
| Search books | /store/search |
Bookstore search, scope selects the tab |
| Book info | /book/info |
Title, author, rating, ISBN, word count |
| Chapter list | /book/chapterinfo |
Table of contents, chapterUid for other calls |
| Reading progress | /book/getprogress |
Percent + cumulative reading time |
| Bookshelf | /shelf/sync |
Ebooks (books) + audiobooks (albums) |
| Reading stats | /readdata/detail |
Duration, days, preferences by period |
| Notebook overview | /user/notebooks |
Per-book note counts |
| Highlights (one book) | /book/bookmarklist |
Highlight text, filters out bookmarks |
| Personal thoughts | /review/list/mine |
Highlight thoughts + chapter/book reviews |
| Popular highlights | /book/bestbookmarks |
Book-wide popular highlights with text |
| Highlight heat | /book/underlines |
Per-range highlight counts, no text |
| Thoughts under a highlight | /book/readreviews |
Comments under specific ranges |
| Single thought detail | /review/single |
One thought with comments/likes |
| Public book reviews | /review/list |
Others' public reviews of a book |
| Recommendations | /book/recommend |
Personalised "for you" |
| Similar books | /book/similar |
Books similar to a given book |
Search — /store/search
Params: keyword (required), scope (int, see below), maxIdx (paging offset),
count (page size).
scope: 0 all, 10 ebooks (default for "find a book"), 16 web novels,
14 audiobooks, 6 authors, 12 full-text, 13 booklists, 2 official
accounts, 4 articles. Use 10 when the user clearly wants to find a book, 0
for a generic search.
Response is the V3 format: results[] groups, each with books[] carrying
bookInfo (bookId, title, author, cover, category, price), plus
readingCount, newRating (0-100), newRatingCount. Page with the last item's
searchIdx as the next maxIdx while hasMore == 1.
Book — /book/info, /book/chapterinfo, /book/getprogress
/book/info—bookId→ title, author, translator, cover, intro, category, publisher, ISBN,wordCount,newRating(0-100),newRatingCount./book/chapterinfo—bookId→chapters[]withchapterUid,chapterIdx,title,wordCount,level(indent depth),price/paid.chapterUidis the input for/book/underlinesand/book/bestbookmarks./book/getprogress—bookId→book.progressis an integer 0-100 percent (1 means 1%, not 100%); only100(withfinishTime) means finished. Always display with a%sign.book.recordReadingTimeis cumulative seconds.
Bookshelf — /shelf/sync
No params (identity comes from the key). Returns books[] (ebooks),
albums[] (audiobooks — a separate array), and mp (an article-collection entry
object).
Shelf total when the user asks "how many books on my shelf":
books.length + albums.length + (mp is non-empty ? 1 : 0). Audiobooks
(albums[]) count as books on the shelf. Do not use server-side count fields.
- Private reads =
books[].secret == 1count +albums[].albumInfoExtra.secret == 1count + (mpnon-empty ? 1 : 0). - Ebook-only count =
bookCountorbooks.length.
Reading stats — /readdata/detail
Params: mode (weekly / monthly (default) / annually / overall),
baseTime (int timestamp, 0 = current period).
Key fields: totalReadTime (period total, seconds — the authoritative total),
readDays (days with >= 1 min reading), dayAverageReadTime (per-calendar-day
seconds), compare (ratio vs previous period), readLongest[] (top books by
readTime), readStat[] (summary like 读过/读完/笔记), preferCategory[],
preferTime[] (24-hour buckets starting at 06:00), preferAuthor[].
/readdata/detail only supports fixed natural periods. For an arbitrary date
range, combine periods: whole years via annually, whole months via monthly,
then add/subtract. Use a completed period's totalReadTime; for a partial
boundary period prefer dailyReadTimes (day-level detail) when present.
Notes & highlights
Note counting: total notes = bookmarks + highlights + thoughts, i.e.
reviewCount + noteCount + bookmarkCount. noteCount is the highlight count, not
the total. reviewCount already includes personal reviews — do not double-count.
/user/notebooks— overview of all books with notes. Paramscount,lastSort(cursor). ReturnstotalBookCount,totalNoteCount,hasMore, andbooks[]withreviewCount,noteCount,bookmarkCount,sort. Paginate withcount+lastSort(the last item'ssort) untilhasMore == 0. Never useoffset/limit./book/bookmarklist—bookId→updated[]highlights (markText,chapterUid,range,createTime,colorStyle) pluschapters[]. Bookmarks are filtered out; bookmark content cannot be exported./review/list/mine—bookid,synckey,count→ the user's own highlight thoughts, chapter reviews, and whole-book reviews (reviews[].review)./book/bestbookmarks—bookId, optionalchapterUid→ book-wide popular highlights (items[]withmarkText,range,totalCount). Server returns the top 20, no paging./book/underlines—bookId,chapterUid→ per-range highlight heat (underlines[]withrange,count,score) — no highlight text./book/readreviews—bookId,chapterUid,reviews[](each{range}) → thoughts written under those ranges./review/single—reviewId→ one thought with comments and likes.
To export a single book's notes, call /book/bookmarklist (highlights) AND
/review/list/mine (thoughts) and merge — highlights alone are incomplete.
Public reviews — /review/list
Params: bookId, reviewListType (0 all / 1 recommended / 2 negative /
3 newest / 4 neutral), count, maxIdx, synckey. reviews[].review.review.star
is 20/40/60/80/100 for 1-5 stars. Page with the last item's idx.
Discovery — /book/recommend, /book/similar
/book/recommend— paramscount,maxIdx→ personalisedbooks[]with areasonfield. Page withsearchIdx./book/similar— paramsbookId,count,maxIdx,sessionId→ similar books underbooksimilar.books[]. Page with the lastidxplussessionId.
Deep links (URL schema)
When showing books, chapters, or highlights, append a weread:// link so the
user can jump straight into the WeRead app.
- Open a book at its last position:
weread://reading?bId={bookId} - Jump to a chapter:
weread://reading?bId={bookId}&chapterUid={chapterUid} - Jump to a highlight: split
range("900-2004") intorangeStart/rangeEnd:weread://bestbookmark?bookId={bookId}&chapterUid={chapterUid}&rangeStart={start}&rangeEnd={end}
Output guidance
- Show lists with numbers so the user can pick by index.
- Search results: lead with title, author, rating, reading count, category.
- Do not translate response field names literally — explain them using the field notes in this file.
- Convert all timestamps to
YYYY-MM-DDand all durations (seconds) to "X hours Y minutes" before displaying.