Reading & writing Standard.site documents over AT Protocol (curl)

standard-site-documents-curl.md
· Created 3mo ago ·
4.1 kB
Markdown

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) #

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) #

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) #

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:

# 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)
# Create the site.standard.document record
curl -s "$PDS/xrpc/com.atproto.repo.createRecord" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @- <<JSON | jq .
{
  "repo": "$DID",
  "collection": "site.standard.document",
  "record": {
    "\$type": "site.standard.document",
    "site": "https://yourblog.example",
    "title": "Hello from curl",
    "description": "A document created straight over XRPC.",
    "path": "/hello-from-curl",
    "publishedAt": "2026-06-26T12:00:00Z",
    "textContent": "This is the plaintext body of the document."
  }
}
JSON

Notes on the fields:

  • site (required, uri) — the publication this belongs to. An at:// URI to a site.standard.publication record, or an https:// URL for a loose document.
  • publishedAt (required, datetime) — ISO 8601.
  • path — combined with the site URL to build the canonical URL; lead with /.
  • content is optional and open; omit it and textContent alone is a valid, portable document any reader can display.
  • createRecord mints the rkey (a TID) for you; pass "rkey" yourself to set it.

Offprint specifics (optional) #

Offprint pairs each site.standard.document with an app.offprint.document.article record that holds a com.atproto.repo.strongRef to the document (same pattern for app.offprint.publication -> 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.

rom.kape.xyz
·

can you please provide a createRecord call with the optional offprint specifics?

Login to leave a reply

Delete comment?

This cannot be undone.

Sign up or Login to add to the discussion.