# Reading & writing Standard.site documents over AT Protocol (curl) A "Standard.site document" (a blog post / article on Offprint, Unthread, etc.) is just a record in the `site.standard.document` collection of the author's repo. - **Reading** is unauthenticated. You hit `com.atproto.repo.listRecords` / `getRecord` on the author's **PDS** (not bsky.social, not the appview). - **Writing** is the same `com.atproto.repo.createRecord` call you already use for `app.bsky.feed.post` — same auth, different `collection` and `record`. Lexicon: `at://did:plc:re3ebnp5v7ffagz6rb6xfei4/com.atproto.lexicon.schema/site.standard.document` Required fields: `site`, `title`, `publishedAt`. --- ## 0. Resolve the author's PDS (records live on their PDS) ```bash HANDLE="bunniesin.space" # handle -> DID DID=$(curl -s "https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?handle=$HANDLE" \ | jq -r .did) # DID -> PDS endpoint (works for did:plc and did:web) PDS=$(curl -s "https://plc.directory/$DID" \ | jq -r '.service[] | select(.id=="#atproto_pds") | .serviceEndpoint') echo "$DID @ $PDS" # did:plc:gijpvbkdbr56kazbdjhfvb3d @ https://eurosky.social ``` ## 1. List a repo's documents (no auth) ```bash curl -s "$PDS/xrpc/com.atproto.repo.listRecords?repo=$DID&collection=site.standard.document&limit=10" \ | jq '.records[] | {uri, title: .value.title, publishedAt: .value.publishedAt}' ``` ## 2. Fetch one document by rkey (no auth) ```bash RKEY="3moef5ysngd23" # last segment of the at:// URI curl -s "$PDS/xrpc/com.atproto.repo.getRecord?repo=$DID&collection=site.standard.document&rkey=$RKEY" \ | jq . ``` The body lives in `value`. Two fields matter: - `value.textContent` — plaintext of the whole doc. Use this if you just want the text. - `value.content` — the rich block tree, an **open union**. Each item has a `$type` (Offprint emits `app.offprint.block.*`; other apps emit their own). Render the ones you know, fall back to `textContent` for the rest. --- ## 3. Create a document (auth required) Auth is identical to posting to Bluesky. App-password example: ```bash # Get a session (or reuse the access token you already have from OAuth) PDS="https://your-pds.example" # the AUTHENTICATED user's PDS RESP=$(curl -s "$PDS/xrpc/com.atproto.server.createSession" \ -H "Content-Type: application/json" \ -d '{"identifier":"you.bsky.social","password":"xxxx-xxxx-xxxx-xxxx"}') TOKEN=$(echo "$RESP" | jq -r .accessJwt) DID=$(echo "$RESP" | jq -r .did) ``` ```bash # Create the site.standard.document record curl -s "$PDS/xrpc/com.atproto.repo.createRecord" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d @- < `site.standard.publication`). The portable document is the `site.standard.*` one; the `app.offprint.*` records are Offprint's app-side index. If you only care about the content, read `site.standard.document` and ignore the rest.