Lua API

atproto API

The atproto table provides atproto utility functions. Available in queries, procedures, and index hooks.

atproto.resolve_service_endpoint

local endpoint = atproto.resolve_service_endpoint(did)

Resolves a DID to its atproto service endpoint URL by fetching the DID document. Supports both did:plc:* (via the PLC directory) and did:web:* (via .well-known/did.json).

ParameterTypeDescription
didstringThe DID to resolve

Returns: The service endpoint URL as a string, or nil if resolution fails (DID not found, no PDS service in document, network error).

Examples

-- Resolve a did:plc DID
local endpoint = atproto.resolve_service_endpoint("did:plc:abc123")
-- endpoint = "https://pds.example.com"

-- Resolve a did:web DID
local endpoint = atproto.resolve_service_endpoint("did:web:example.com")
-- endpoint = "https://example.com"

-- Handle resolution failure
local endpoint = atproto.resolve_service_endpoint("did:plc:unknown")
if not endpoint then
  return { error = "Could not resolve DID" }
end

-- Use with HTTP API to call a remote XRPC endpoint
local endpoint = atproto.resolve_service_endpoint(did)
if endpoint then
  local resp = http.get(endpoint .. "/xrpc/com.example.method")
  local data = json.decode(resp.body)
end

atproto.get_labels

local labels = atproto.get_labels(uri)

Returns an array of labels for a single AT URI. Merges external labels (from subscribed labelers) with self-labels (from the record's labels.values[] field).

ParameterTypeDescription
uristringAT URI of the record to query

Each label in the array is a table with:

FieldTypeDescription
srcstringDID of the labeler (or record author)
uristringAT URI this label applies to
valstringLabel value (e.g. "nsfw", "!hide")
ctsstringTimestamp when the label was created

Expired labels are automatically filtered out. Returns an empty array if no labels exist.

atproto.get_labels_batch

local labels_by_uri = atproto.get_labels_batch(uris)

Batch version of get_labels. Takes an array of AT URIs and returns a table keyed by URI, where each value is an array of labels.

ParameterTypeDescription
uristableArray of AT URI strings

Returns: A table keyed by URI. Each value is an array of label tables (same shape as get_labels). URIs with no labels have an empty array.

Label examples

-- Get labels for a single game
local labels = atproto.get_labels("at://did:plc:abc/games.gamesgamesgamesgames.game/rkey1")
for _, label in ipairs(labels) do
  if label.val == "!hide" then
    -- skip this game in feed results
  end
end

-- Batch fetch labels for multiple games (efficient for feed hydration)
local uris = {}
for _, item in ipairs(skeleton) do
  uris[#uris + 1] = item.game
end

local labels_by_uri = atproto.get_labels_batch(uris)
for _, uri in ipairs(uris) do
  local labels = labels_by_uri[uri]
  for _, label in ipairs(labels) do
    if label.val == "!hide" then
      -- filter out this game
    end
  end
end

atproto.sign

local sig = atproto.sign(record)

Signs a record and returns the inline signature object. Only available when an attestation signer is configured — if no signer is configured, atproto.sign is nil.

ParameterTypeDescription
recordtableThe record data to sign

Returns: A signature table with:

FieldTypeDescription
keystringThe signing key ID (e.g. did:web:example#signing)
signaturetableContains $bytes with the signature

Examples

-- Sign a record before returning it
local record = { contributionType = "correction", changes = { name = "Test" } }
local sig = atproto.sign(record)
record.signature = sig
return record

-- Check if signing is available
if atproto.sign then
  local sig = atproto.sign(record)
end

atproto.verify_signature

local valid = atproto.verify_signature(record, signature, repo_did)

Verifies that an inline signature was produced by this HappyView instance. Only available when an attestation signer is configured — if no signer is configured, atproto.verify_signature is nil.

ParameterTypeDescription
recordtableThe record data
signaturetableThe signature object from atproto.sign()
repo_didstringThe repo DID

Returns: true if the signature is valid, false otherwise. Returns false on failure rather than raising an error.

Examples

-- Verify a signature roundtrip
local record = { contributionType = "correction", changes = { name = "Test" } }
local sig = atproto.sign(record)
local valid = atproto.verify_signature(record, sig, caller_did)
if not valid then
  return { error = "signature verification failed" }
end