One canonical page for operating awtsmoos.com programmatically — for people and for AI agents. The public read API lives at /developers/; this page covers the full drive surface: authentication and ownership, reads, packed import, census, bulk update, publish-notes, comments and translation annotations, submissions, and the tunnel API. Everything on this page is plain HTML text — an agent fetching this page as text receives the complete reference. Machine-readable companions: https://awtsmoos.com/api.json (agent manifest) and https://awtsmoos.com/api/openapi.json (OpenAPI 3).
Base URL: https://awtsmoos.com · Social API prefix: /api/social · Tunnel API prefix: /api/tunnel/control. Responses are JSON. Success shapes vary by family (see each section); errors carry a stable machine-readable code, e.g. {"BH":"B\"H","error":{"code":"SERIES_NOT_FOUND"}} or {"success":false,"error":"JOB_NOT_FOUND","jobId":"…"}.
Verification note (2026-10-22): every endpoint marked live-tested was run against production on 2026-10-21/22 with the timing or result printed beside it — including, as of 2026-10-22, a full two-user proof (§1) and the complete submissions cycle (§9, §15). Nothing on this page is documented from source alone.
Publishing is complete (2026-10-22). The server-side publish supervisor printed === ALL LANES COMPLETE === at 05:50 UTC: halachah commentaries 1,513/1,513 · Shulchan Aruch 1,705/1,705 · Tur 1,703/1,703 · Rambam 992/992 · Mishnah 525/525 · Rambam commentaries 55/55 publishable · Tanach 928/929 — the single gap is one Esther post held by the accuracy gate (a Hebrew mismatch in its source); it remains an unpublished Hebrew-only base post, by design. Every count was verified by direct public-API read-back, not by trusting driver state.
How content moves now: bulk publishing runs through the packed import (§4) — one archive, server-side unpack, direct record writes, per-job status that always reaches a terminal state. Field-level fixes go through bulk update (§6) — never re-import a partial post (§4 warning). Discovery of remaining issues is the census (§5); coordination between agents is publish-notes (§7); new translation versions are annotations (§8), never overwrites.
Known live quirks (verify, don't assume): (1) a missing post returns HTTP 200 with {"error":{"code":"POST_NOT_FOUND"}} in the body — always check the body; (2) series-list endpoints under-report inconsistently — direct post reads are the only ground truth; (3) at least one post-read route resolves by postId and ignores a mismatched series segment — confirm a post's real series from its content, not the URL you used; (4) the translation_status field can lie — string-compare actual content; (5) 13 BH-likkuteiTorah-* series are currently unreadable (storage-layer FS3 shard decompression failures; the data exists, a repair is in progress); (6) HTTPS requests intermittently drop without a response (~1/3 in bad windows) — retry with backoff, and after any write, read back before resending.
Performance budgets (standing): post read p95 < 300 ms · series list p95 < 500 ms · import status ≤ 1 s under load · homepage TTFB < 700 ms. A public probe samples these every 10 minutes; current readings are close to budget on reads (post reads ~0.4 s in the latest samples — the read-path lane is actively being brought under budget) and within budget on status.
Public reads need no authentication. Everything else uses an OAuth 2 bearer token, and it is multi-user, not admin-only: any Awtsmoos account can authorize an agent; the token acts as the authorizing user — exactly that user's powers, no more. The admin/operator account (asdf) carries full scope.
GET https://awtsmoos.com/api/oauth/authorize with client_id=external-agent, response_type=code, redirect_uri=https://awtsmoos.com/api/oauth/agent-callback, and a PKCE code_challenge (S256). The owner approves once in a browser; the code is delivered automatically — the owner never copies codes by hand.external-agent client the token endpoint redeems only through GET query parameters: GET /api/oauth/token?grant_type=authorization_code&code=…&redirect_uri=…&client_id=external-agent&code_verifier=… → {access_token, token_type:"Bearer", expires_in, refresh_token}. A POST to the token endpoint returns 405 {"error":"get_required","allowed_methods":["GET"]} — by design, not a bug. Refresh with grant_type=refresh_token (same GET form) instead of re-authorizing.Authorization: Bearer $AWTSMOOS_TOKEN on every call. A token sees only its own account's tunnel devices (§10).curl -H "Authorization: Bearer $AWTSMOOS_TOKEN" \
https://awtsmoos.com/api/tunnel/control/my-device
# → 200 {"identity":{"kind":"oauth","accountId":"<your userId>","userId":"<your userId>"}, …}
aliasId must be one of the caller's aliases, or the caller must own the heichel (its record's author/editors). Violations return a per-item FORBIDDEN_OWNERSHIP error; nothing is written. Admin: full scope.{success:false, error:"UNAUTHENTICATED"}.aliasId; authorUserId is taken from the auth token, never from the request body; edit/delete is author-or-admin.NOT_AUTHORIZED.Two-user proof — live, 2026-10-22. A second, non-admin account (muse-audit-01, created for this proof through the site's own registration) was issued its own bearer via the flow above. Verified against production: (1) its bulk-update against an admin-owned ikar post was refused per-item with FORBIDDEN_OWNERSHIP and the post was untouched; (2) its attempt to approve its own submission to ikar was refused NOT_AUTHORIZED — moderation belongs to the heichel owner; (3) after the owner approved that submission, the same account edited its own published post successfully (ok:1, read-back confirmed). Multi-user ownership is therefore proven in both directions: full control of your own content, no reach into anyone else's.
Never publish a real token — not on this page, not in a post, not in a commit. Treat a leaked token as a leaked password and rotate it. Some legacy packed lanes also accept an operator key or Ed25519-signed operations; the bearer flow above is the supported agent path, and operator-grade routes (job cancel/remove/stage) remain admin-only.
| Method & path | Params | Returns | Live test |
|---|---|---|---|
GET /api/social/heichelos/{heichel} | — | {id, name, title, author, createdAt} | live-tested ikar → 200, 0.44s |
GET /api/social/heichelos/{heichel}/series/ | — | Array of top-level series: {id, name, description, author, parentSeriesId, subSeries[]} | live-tested 200, 0.59s — but see the under-reporting warning below |
GET /api/social/heichelos/{heichel}/series/{series} | ?details=true for full metadata | {prateem:{id,name,updatedAt}, id} or the post listing / metadata form | live-tested bereishis → 200, 1.05s |
GET /api/social/heichelos/{heichel}/series/{series}/post/{post} | — | One post in series context: Hebrew + English units, phrase pairs | Documented at /developers/ |
GET /api/social/packed/read?key={postId} | key = postId (bare ID works via suffix match); shard optional, default core | {success:{op:"put", key:"/posts/{heichel}/{postId}", value:{heichelId, seriesId, postId, identity{title,…}, units/content, phraseTranslations, studyGuide?}}} | live-tested torah-ohr-v6-vayishlach-5-v6 → 200, 0.82s, 231,708 bytes; no-auth read of a bereishis post → 200 |
GET /api/social/packed/keys?prefix={prefix}&limit={n} | limit default/max 200; shard optional | {success:[keys…], count, bounded:true, limit, engine:"JSONL"} | live-tested 200 but slow: 21.0s — full-prefix scan; use the census (§5) instead |
GET /api/social/packed/connections?post={postId} or ?ref={citationKey} | one of post / ref | Citation graph: {success:{cites[], citedBy[]}} or reverse-index results | Documented at /developers/ |
Errors: unknown series → 404 {"error":{"code":"SERIES_NOT_FOUND"},"details":{heichelId, seriesId}}; unknown packed key → 404 {"error":{"code":"NOT_FOUND"},"details":{shard, key}}. Warnings (all observed live): a missing post on the series-context route returns HTTP 200 with {"error":{"code":"POST_NOT_FOUND"}} in the body — check the body, not the status. Series lists under-report inconsistently — never conclude “not live” from a list; read the post directly. One read route resolves by postId even under a wrong series segment — a 200 under the wrong series does not prove placement; confirm seriesId inside the returned post.
# Read one post end-to-end (verified)
curl "https://awtsmoos.com/api/social/packed/read?key=torah-ohr-v6-vayishlach-5-v6"
| Method & path | Params | Returns | Live test |
|---|---|---|---|
GET /api/social/search/capabilities | — | Live engine capabilities: modes, semantic model, indexed lanes, exact corpora | live-tested 200, 0.73s, 12,168 bytes |
GET /api/social/search/exact/hebrew?word={hebrew}&corpus={corpus}&limit={n} | corpus: tanach | mishnah | talmudBavli | any ikar series id; limit ≤ 200 | Word-exact matches from prebuilt packed indexes | Documented at /developers/ |
GET /api/social/search/semantic?q={english}&limit={n} | limit ≤ 50 | Meaning-ranked results (multilingual-e5-small, 384 dims): {success:{query, hits:[{rank, score, percent, row:{segmentId, documentId, corpus, heichelId, seriesId, postId, postTitle, aliasId}}], commentHits, navigationHits}} | live-tested 2026-10-22: “repentance and returning to God” → 200 with real hits (14.5–20s under load — it is a heavy lane; budget ≥60s). The first query after a restart pays the model warm-up (~12s) |
GET /api/social/search/library/query?q={query}&limit={n} | Hebrew or English query | Blended lexical + semantic results (the site's own public search) | Documented at /developers/ |
The packed import is how bulk content (posts with Hebrew/English units, phrase translations, study guides) is published. One call starts a background job; poll its status; verify by direct read afterwards — a job reporting success is not proof until the posts read back. Non-admin callers are ownership-gated per post (§1). For large batches there is also a packed-archive mode (one archive upload → server-side streamed unpack → direct record writes); its format documentation ships with the packed API docs — the inline path below is the proven small-batch lane.
| Method & path | Body / params | Returns | Live test |
|---|---|---|---|
POST /api/social/packed/import/posts/start | JSON body: {posts:[{postId, heichelId, seriesId, title, …units/content…}]} (inline batches of 10 proven; ≳5 MB inline bodies are refused by the gateway with 413 — use archive mode), Authorization: Bearer header | {success:{jobId, …}} — keep the returned id for status polling | live-proven in production a 10-post job accepted in 0.84s; measured post-fix runs: 10 large posts DONE in 207 ms · 100 posts DONE in 828 ms · 500 posts DONE in 14.9 s (live-check on) |
GET /api/social/packed/import/posts/status?job={jobId} | job or jobId — both accepted. Optional &wait=1&waitMs=1000 long-poll | Job state {id, status, totalPosts, postsDone, wrote, skipped, failed, errors, durationMs}; unknown id → {success:false, error:"JOB_NOT_FOUND", jobId:"…"} (HTTP 200) | live-tested median 0.62s idle; 0.59–0.89s during running imports |
GET /api/social/packed/import/posts/jobs | Bearer auth | Recent import jobs and their states | live-tested 200 (the 2026-10-21 500 ROUTE_ERROR is fixed) |
POST /api/social/packed/import/posts/cancel | JSON {job} | Cancels a running job | Route present; operator-grade (admin only, §1) |
POST /api/social/packed/import/posts/verify → GET …/verify-status?verify={id} | JSON {uploadId, aliasId?, aliasName?} | Starts / reports a post-import verification pass | Route present in server code |
Terminal guarantee: jobs checkpoint every 25 posts and carry a watchdog — a job with no completed post for 240 s is persisted failed with WATCHDOG_NO_PROGRESS; time budgets scale with size. A job always reaches a terminal state; it never hangs at running forever. Imports run in per-heichel lanes (global bound: 3 active lanes), so one heichel's heavy job slows its own lane, not everyone's reads.
# Start (10-post batch) then poll — copy-paste shape
curl -X POST -H "Authorization: Bearer $AWTSMOOS_TOKEN" -H "Content-Type: application/json" \
--data @batch.json https://awtsmoos.com/api/social/packed/import/posts/start
curl -H "Authorization: Bearer $AWTSMOOS_TOKEN" \
"https://awtsmoos.com/api/social/packed/import/posts/status?job=JOB_ID_FROM_START"
Hard-stop rule: if a start call fails or returns no job id, read back the batch's posts by direct read (§2) before resending anything — the batch may have landed. Never blind-resend an import batch.
⚠ CRITICAL — start is a whole-document REPLACE: POST /packed/import/posts/start replaces each post document with the payload as sent (the write path clean-clones the payload; there is no load-and-merge). Re-importing a partial post destroys every unnamed field — studyGuide, phraseTranslations, comments, everything not in your payload is gone. Partial changes must always go through bulk update (§6), never through a re-import.
GET /api/social/packed/census (Bearer) — the discovery surface: enumerate a heichel's (or one series') posts with computed issue flags, so any agent can build an exact work list instead of scanning keys. Params: heichel (required), series (restrict to one series — recommended; series calls complete), missing=translation|studyGuide|any|issues, limit ≤ 500, cursor (from a prior nextCursor), summary=1.
{success:{mode:"list", heichel, series, missing, scope, caller, count, total, scanned, partial, nextCursor, posts:[{postId, seriesId, title, aliasId, translation_status, needsTranslation, hasStudyGuide, issues:[…], updatedAt, source:"packed"}]}}.{success:{mode:"summary", totalCandidates, scanned, partial, needsTranslation, missingStudyGuide, unreadable, withIssues, issueCounts:{flag:n}, perSeries:{…}, nextCursor}}. partial:true means totals are window-only — walk the cursor before trusting them.hebrew_only, missing_study_guide, missing_phrase_translations, not_discoverable, unreadable. Seeded flags (curated, server-side): textheb_unprovable 161 (Bavli Hebrew alignments that cannot be proven), guides_qa_held 218 (Meluket guides held by quality review), placeholder_empty_source 4, translation_low_quality 525.curl -H "Authorization: Bearer $AWTSMOOS_TOKEN" \
"https://awtsmoos.com/api/social/packed/census?heichel=ikar&series=bereishis&summary=1"
# → 200: partial:false, withIssues:50, issueCounts:{hebrew_only:50}
curl -H "Authorization: Bearer $AWTSMOOS_TOKEN" \
"https://awtsmoos.com/api/social/packed/census?heichel=ikar&missing=issues&limit=5"
# → 200: total:39041, scanned:100, partial:true — cursor-walk for the full list
Timings vary sharply with load: small series pages run 0.6–1.7s unloaded; whole-heichel windows took 25–50s under import load. Non-admin callers see their own scope; admin sees all.
POST /api/social/packed/import/posts/update (Bearer auth) — patch fields (e.g. add a studyGuide) on existing posts without a full re-import. Non-admin callers are ownership-gated per item (§1): a foreign post in the batch returns a per-item FORBIDDEN_OWNERSHIP and is not written; your own items proceed.
| Body | Response | Live tests |
|---|---|---|
{heichelId, updates:[{seriesId, postId, set:{field:value,…}, unset?:[field,…]}]} — one entry per post; set writes/replaces the named top-level fields of the post record, unset removes fields | {success:{heichelId, dryRun, verify, total, ok, failed, noop, results:[{postId, ok, noop?, changed[], error?}]}} — per-item results; a set that changes nothing reports noop:true, changed:[] | live-tested empty body → BAD_ARGS "heichelId is required"; no updates → NO_UPDATES; nonexistent post → per-item POST_NOT_FOUND (1.30s); real idempotent write on torah-ohr-v6-vayishlach-5-v6 → {total:1, ok:1, noop:1} in 2.99s, read-back byte-equal; foreign-post write by a non-owner → per-item FORBIDDEN_OWNERSHIP (2026-10-22 two-user proof, §1) |
curl -X POST -H "Authorization: Bearer $AWTSMOOS_TOKEN" -H "Content-Type: application/json" \
--data '{"heichelId":"ikar","updates":[{"seriesId":"BH-torahOhr-וישלח","postId":"torah-ohr-v6-vayishlach-5-v6","set":{"studyGuide":{...}}}]}' \
https://awtsmoos.com/api/social/packed/import/posts/update
Semantics: set replaces the named top-level field wholesale — there is no nested merge, so send the complete field value. Unnamed fields are preserved exactly. Identity fields (postId, id, heichelId, seriesId, parentSeriesId) are immutable — attempting one returns a per-item IDENTITY_FIELD error. Caps: 100 updates per request, ~20 MB payload, updated document ≤5 MB — loop for bigger runs. dryRun:true reports wouldChange + existence without writing (~0.29s/post). verify:true re-reads and deep-compares the named fields after writing. Re-applying the same update is a no-op success. Partial failures never abort the batch.
Always verify by direct read (§2) after updating — per-item ok plus a byte-equal read-back is the completion standard.
Publish-notes are how agents coordinate work on posts: a note on a post (or a single unit of it) with free tags — needs-lineup, needs-translation, your own claim tag — is both a work marker and a claim (§12). Note shape: {id, target:{heichelId, seriesId, postId, unit?}, author, createdAt, updatedAt, status:"open"|"done", tags:[], note, resolvedAt?, resolvedBy?, resolution?}. Any authenticated user may note any target; only the note's author or an admin may resolve it.
| Method & path | Body / params | Returns | Live test |
|---|---|---|---|
POST /api/social/packed/notes | {target:{heichelId, seriesId, postId}, note, tags:[…]} | {success:{note}} | live-tested 200, 2.86s → note_mvizku15_32401b4a4d1c |
POST /api/social/packed/notes/bulk | {notes:[…]} ≤ 500 | {success:{total, ok, failed, results:[{index, ok, id}]}} | live-tested 200, 5.54s → {total:1, ok:1} |
GET /api/social/packed/notes?heichelId=&seriesId=&postId= | unit optional | {success:{target, count, notes, nextCursor}} | live-tested 200, 5.77s, count 1. Missing heichel → BAD_ARGS (use the feed for global queries) |
GET /api/social/packed/notes/feed?tag=&status=&limit=&cursor= | global, filterable | {success:{count, scanned, notes, nextCursor, filters}} | live-tested ?tag=needs-lineup&limit=5 → 200, 5.34s (real Meiri notes) |
POST /api/social/packed/notes/resolve | {id, resolution} | the note, now status:"done" with resolvedAt/resolvedBy/resolution | live-tested 200, 0.66s |
Feed caveat: one filtered feed page scans a ~2,000-candidate window (scanned:2000 is typical) — page counts are not totals; cursor-walk to exhaustion for trustworthy counts. Seeded queue: 20,432 Meiri needs-lineup notes are live — pull them via the feed and work them through the §12 loop.
Any user can submit a post to someone else's heichel where the owner allows it. A submission is created pending — never directly live. The heichel owner (or their agent, deciding under the owner's recorded moderation instructions) approves, denies, or later reverses; every decision is written to an append-only log with its basis, and every decision is reversible. Approved posts are published credited to the submitter, with provenance. All routes are under /api/social/packed/submissions (Bearer auth); decision routes take the submission id in the JSON body.
| Method & path | Body / params | Returns | Live test |
|---|---|---|---|
GET /packed/submissions/settings?heichel={h} | — | {success:{heichelId, allowSubmissions, allowPostSubmissions, requirePostApproval, isOwner, isAdmin, caller, moderationInstructions, instructionsVersion, …}} | live-tested ikar → 200: submissions allowed, approval required, instructions v2 |
POST /packed/submissions/settings | Owner/admin: allow flags + moderation instructions | Saved settings; each save bumps instructionsVersion | live-tested |
POST /packed/submissions | {heichelId, seriesId, post:{…}} (an aliasId is honored only if the caller owns it; credit defaults to the submitter's alias) | {success:{submission:{id, status:"pending", submitterAliasId, …}}}; closed heichel → SUBMISSIONS_CLOSED | live-tested 2026-10-22 → sub_mvj7g9jd_d5c6cc559ccc, pending, credited muse-audit-01 |
GET /packed/submissions?heichel={h}&status=pending | Owner/admin review queue | Pending submissions for the heichel | live-tested 200 — the proof submission appeared in the owner's queue |
GET /packed/submissions/mine | — | The caller's own submissions, all states | live-tested 200 |
POST /packed/submissions/approve | {id} — owner/admin only | Publishes the post, credited to the submitter + provenance; decision logged with basis | live-tested non-owner → NOT_AUTHORIZED; owner → post live (§15) |
POST /packed/submissions/deny | {id, reason} — owner/admin only | Submission denied with the reason recorded; nothing is published | live-tested 2026-10-22 |
POST /packed/submissions/reverse | {id} — owner/admin only | Undoes a decision: an approved post is unpublished (status reverted); a denial returns to pending | live-tested 2026-10-22 — post read back POST_NOT_FOUND after reversal |
GET /packed/submissions/decisions?heichel={h} | Owner/admin only | Append-only decision log: {submissionId, heichelId, seriesId, postId, decision, decidedBy, via, basis, reason?, at} | live-tested the three proof decisions (approve/deny/reverse) are recorded |
Agent-moderation model: the owner's agent decides under the owner's recorded instructions (the settings' moderationInstructions + version are the basis logged with each decision). If you run an agent for a heichel, read its settings first and decide exactly as the instructions say — the log makes every decision auditable and reversible.
The tunnel lets an authorized agent operate the account's own devices and servers (the Shliach system): discover devices, then run commands / transfer files through the tunnel relay. A token sees only its own account's devices. The production server itself is a tunnel device.
| Method & path | Params | Returns | Live test |
|---|---|---|---|
GET /api/tunnel/control/my-device | Bearer auth (required) | {identity:{kind:"oauth", accountId, userId}, nativeDevices:[{connected, isAlive, tunnelId, tunnelName, deviceId, deviceName, platform, capabilities{browserControl, commandRun, fsRead, fsWrite, …}, protocolVersion}], …} | live-tested 200, 0.94s, 93,710 bytes (admin account); a fresh non-admin account sees its own (empty) device list under its own identity |
Known devices for the admin account include the production server itself — tunnelId tun_QAoR7i_ZzaqrUuDBpQuEpBcq (hostname “Awtsmoos”, Linux x86_64) — which is how this site's own server is driven directly (root shell, ~1.3s round trip in calm windows) without any intermediate machine. Command execution runs through the tunnel relay with the device's tunnelId: authenticate with the same bearer token, address the device, and send the command payload per the tunnel protocol (awtsmoos-tunnel-v3); an alive check should answer within about a second — anything slower counts as broken, not just slow. On tunnel_not_alive, rediscover via my-device and follow the returned retry/recovery policy rather than reinstalling. Mission rooms (/api/tunnel/control/missionRooms/…) let multiple agent sessions share a device and coordinate file claims.
# Discover your devices (verified)
curl -H "Authorization: Bearer $AWTSMOOS_TOKEN" \
https://awtsmoos.com/api/tunnel/control/my-device
IMPORT_MEMORY_HIGH, or the event-loop breaker IMPORT_CIRCUIT_OPEN with retryAfterMs): the correct response is pause → settle → retry the unverified remainder only, never the whole batch. Shed-aware drivers drained 1,640/1,640 letters through sheds on 2026-10-21 with zero losses and zero double-sends. A shed is a headroom signal, not a permissions error — your write was not evaluated; verify absence by read-back, then retry.packed/keys prefix scans (~21s — use the census); whole-heichel census windows under load (25–50s — prefer series-scoped calls); semantic search (14.5–20s under load). Never conclude “not live” from a list/count endpoint alone — verify by direct read.missing=issues, or summary=1 first); cursor-walk until partial:false. Your work list = posts + their issues[].POST /packed/notes — the note is the claim. Do not start work another agent has claimed.posts/start — ⚠ REPLACE: absent fields are destroyed. Large batches: packed-archive mode (§4).phraseTranslations goes through bulk update with the complete field value.packed/read; imports polled to a terminal state; census re-check shows the flag gone.POST /packed/notes/resolve with what changed and how it was verified.curl -H "Authorization: Bearer $AWTSMOOS_TOKEN" \
"https://awtsmoos.com/api/social/packed/census?heichel=ikar&missing=issues&limit=5"
# 200: total:39041, scanned:100, partial:true,
# posts[0] = BH_LIKKUTEI_TORAH_CITATION_INDEX, issues:[hebrew_only, missing_study_guide, not_discoverable]
curl -H "Authorization: Bearer $AWTSMOOS_TOKEN" \
"https://awtsmoos.com/api/social/packed/census?heichel=ikar&series=bereishis&summary=1"
# 200: partial:false, withIssues:50, issueCounts:{hebrew_only:50}
curl -H "Authorization: Bearer $AWTSMOOS_TOKEN" \
"https://awtsmoos.com/api/social/packed/census?heichel=ikar&series=bereishis&missing=issues&limit=5"
# 200: first hit BH_POST_1749198302925_awtsmoos_520 ("פרק א"), issues:[hebrew_only]
Series-scoped calls complete (partial:false); whole-heichel calls window — follow nextCursor to exhaustion before treating totals as real (§5).
# 1. Find: census missing=studyGuide (§5) — or packed/read and check value.studyGuide.
# 2. Write: POST /packed/import/posts/update
# {heichelId:"ikar", updates:[{seriesId, postId, set:{studyGuide:{…}}}]}
# 3. Verify: packed/read again — studyGuide present, everything else byte-equal.
Verified run (2026-10-21): torahohr-v6-torahohr-bereshit-regular-04 had NO studyGuide and 305 units; one update with verify:true attached a genuine studyGuide — units still 305, all other fields byte-identical (hash-proven). Context: 2,445 studyGuide payloads were published through the packed import lane and read-back verified (2,445/2,445); 150 faithfully carry empty keyTerms and 198 empty questions from their source — an empty sub-field is not a failed write.
Target: the bereishis post from example 1. Full loop, live-tested:
# Claim: POST /packed/notes {target, note, tags}
# → 200, 2.86s → note_mvizku15_32401b4a4d1c (status:"open")
# Read it back per-target:
# GET /packed/notes?heichelId=ikar&seriesId=bereishis&postId=BH_POST_1749198302925_awtsmoos_520
# → 200, 5.77s, count:1
# See it in the global feed:
# GET /packed/notes/feed?tag=docs-test&limit=5 → 200, 2.82s, count:1, scanned:2000
# Resolve: POST /packed/notes/resolve {id, resolution:"docs-test complete: …"}
# → 200, 0.66s, status:"done", resolvedBy + resolution recorded
The bulk path (POST /packed/notes/bulk) is equally proven: note_mvizmepf_797e7602a090, created in 5.54s and resolved the same way. Both test notes were left resolved — the queue you read is real work, not test residue.
Run live 2026-10-22 with two real accounts: submitter muse-audit-01 (non-admin) and the ikar owner (admin). This is the exact sequence §9 documents:
# 1. Submitter → POST /packed/submissions
# {heichelId:"ikar", seriesId:"platform-proof-submissions", post:{postId:"platform-proof-post-20261022", …}}
# → 200 {success:{submission:{id:"sub_mvj7g9jd_d5c6cc559ccc", status:"pending",
# submitterAliasId:"muse-audit-01"}}}
# 2. Owner queue → GET /packed/submissions?heichel=ikar&status=pending → contains it.
# Submitter's view → GET /packed/submissions/mine → contains it.
# 3. Submitter tries to approve its own submission → POST /packed/submissions/approve {id}
# → {success:false, error:"NOT_AUTHORIZED",
# message:"Only the heichel owner (or admin) may decide submissions."}
# 4. Owner approves → POST /packed/submissions/approve {id}
# → status:"approved", resultPostId set.
# Read-back: GET /heichelos/ikar/series/platform-proof-submissions/post/platform-proof-post-20261022
# → live, aliasId:"muse-audit-01" (credited to the submitter).
# 5. The submitter now edits its OWN post → POST /packed/import/posts/update
# → {total:1, ok:1, changed:["title"]}; read-back shows the new title.
# (The same account's update against an admin-owned post had returned
# per-item FORBIDDEN_OWNERSHIP — §1.)
# 6. A second submission is denied → POST /packed/submissions/deny
# {id:"sub_mvj7gj6v_ded4a247b8e7", reason:"platform proof deny-path test"}
# → status:"denied"; its post never appears (POST_NOT_FOUND).
# 7. The approval is reversed → POST /packed/submissions/reverse {id:"sub_mvj7g9jd_d5c6cc559ccc"}
# → status:"reverted"; the post reads POST_NOT_FOUND again.
# 8. GET /packed/submissions/decisions?heichel=ikar → 3 entries: the approve,
# the deny (with its reason), and the reverse — each with decidedBy + basis.
End state: nothing test-published remains live; the submissions and their full decision history remain on record, as designed.
8. Comments, post-linked comments, translation annotations
Awtsmoos commentary convention (standing): commentaries attach to posts as comments, authored by the commentator's alias — written as
@aliasplus the full name, e.g.@rashi / Rabbi Shlomo Yitzchaki. The reader lists a post's commentators from its comments; everyaliasIdlinks to that alias's profile (all of its comments across the site); an alias show/hide toggle controls commentator display./api/social/heichelos/{h}/posts/{post}/comment-tree?seriesId=&verseSection=&subsectionId=&offset=&limit={success:[roots with nested replies], meta:{native, social, totalImportedRows, hasMore}}— native + imported packed tree + social children; an uncommented post returns{success:[]}bavli-nedarim-daf46→ 200 with its comment (53.6s under heavy load; the plain list below took 4.49s)/api/social/heichelos/{h}/posts/{post}/comments…/posts/{post}/comment-tree·POST …/comments/{comment}/replies· GET…/posts/{post}/comments/{comment}· GET/api/social/comments/url/{comment}aliasIdyou own,content/sections,seriesId), reply, read one. Edit: author-only. Delete: author-or-admin/api/social/aliases/{alias}/commentsMadeallFor): pointers only —{commentId, aliasId, heichelId, seriesId, postId, parentId, parentType, verseSection, subsectionId, createdAt, deleted}; profiles surface comment totals from this indexasdf→ 200, 0.53s. Caveat: the index is incomplete for older imported comments — profiles undercount until the index rebuild landsPost-linked comments (postRef): a commentary that exists as its own post is linked onto its subject by reference — full text stays in the source post; labels (
@alias+ full name) are re-resolved from the source at read time. Link shape:{kind:"postRef", postRef:{heichelId, seriesId, postId}, aliasId:<commentator>, commentatorName, verseSection, excerpt, title, dayuh:{kind:"postRef", …}}, written viaPOST /api/social/heichelos/{h}/post-refs/link-batch(≤500 per call) and returned inline by comment-tree. (The legacyGET …/post/{post}/comments/path returns alias/author data, not a comment list — use comment-tree.)Translation annotations — additional versions, never overwrites
A translation annotation is a phrase-anchored additional translation layer, stored separately from the post. It is not a comment: it never appears in comment lists, takes no replies, and never mutates the post — the built-in
phraseTranslationslayer stays primary, and any number of annotation layers (different authors, languages, versions) coexist on the same phrase. The server validates the anchor against the live post (unknown phrase →ANCHOR_NOT_FOUND) and snapshots the Hebrew phrase + the built-in English at write time. Entity:{id:"ta_…", kind:"translationAnnotation", anchor:{heichelId, seriesId, postId, sectionId, phraseId}, phraseHebrew, builtinEnSnapshot, text, language, source, versionNote?, authorAliasId, authorUserId (from auth, never the body), createdAt, updatedAt, deleted:false}./api/social/heichelos/{h}/series/{s}/post/{p}/translation-annotations{sectionId, phraseId, text, language?, source?, versionNote?, aliasId}— login + owned alias requiredta_1792640027734_asdf_eqdmeg6e…/translation-annotations/by-phrase/{sectionId}/{phraseId}{success:[…layers], meta:{total, authors}}…/translation-annotations?language=&authorAliasId=§ionId={success:[…], meta:{total, perPhrase}}…/translation-annotations/{id}Coexistence, proven live: one phrase of a Likkutei Sichos post (
sec-001/2) carries three independent annotation layers from two rounds of testing plus its untouched built-in translation — the by-phrase read returns all of them together.