Upsert Record
Create a new record, or update an existing one if the client provides its rkey.
Lexicon type: procedure
function handle() local rkey = input.rkey or TID() local uri = "at://" .. caller_did .. "/" .. collection .. "/" .. rkey
local r = Record.load(uri) if r then -- Update existing record r.status = input.status r.updatedAt = now() r:save() else -- Create new record r = Record(collection, { status = input.status, createdAt = now(), updatedAt = now(), }) r:set_rkey(rkey) r:save() end
return { uri = r._uri, cid = r._cid }endHow it works
Section titled “How it works”- Use the client-provided
input.rkeyif present, otherwise generate a newTID(). This means omittingrkeyalways creates, while providing one enables updates. - Build the AT URI from the caller’s DID, the target collection, and the rkey, then try to load it with
Record.load. - If the record exists, update its fields and save. Since
_uriis already set,r:save()callsputRecord. - If it doesn’t exist, create a new record, set the rkey explicitly with
r:set_rkey(), and save. This callscreateRecordwith the specified rkey.
Create (no rkey, so a new TID is generated):
const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.statusphere.setStatus", { method: "POST", headers: { "X-Client-Key": CLIENT_KEY, Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ status: "hello" }),});const data = await response.json();// → { "uri": "at://did:plc:abc/xyz.statusphere.status/3abc123", "cid": "bafyrei..." }const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.statusphere.setStatus", { method: "POST", headers: { "X-Client-Key": CLIENT_KEY, Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ status: "hello" }),});const data = await response.json();// → { "uri": "at://did:plc:abc/xyz.statusphere.status/3abc123", "cid": "bafyrei..." }let response = client .post("http://127.0.0.1:3000/xrpc/xyz.statusphere.setStatus") .header("X-Client-Key", client_key) .header("Authorization", format!("Bearer {}", token)) .json(&serde_json::json!({ "status": "hello" })) .send() .await?;// → { "uri": "at://did:plc:abc/xyz.statusphere.status/3abc123", "cid": "bafyrei..." }body := `{ "status": "hello" }`req, _ := http.NewRequest("POST", "http://127.0.0.1:3000/xrpc/xyz.statusphere.setStatus", bytes.NewBufferString(body))req.Header.Set("X-Client-Key", clientKey)req.Header.Set("Authorization", "Bearer "+token)req.Header.Set("Content-Type", "application/json")resp, err := http.DefaultClient.Do(req)// → { "uri": "at://did:plc:abc/xyz.statusphere.status/3abc123", "cid": "bafyrei..." }curl -X POST http://127.0.0.1:3000/xrpc/xyz.statusphere.setStatus \ -H "X-Client-Key: $CLIENT_KEY" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "status": "hello" }'# → { "uri": "at://did:plc:abc/xyz.statusphere.status/3abc123", "cid": "bafyrei..." }Update (pass the rkey back to update the same record):
const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.statusphere.setStatus", { method: "POST", headers: { "X-Client-Key": CLIENT_KEY, Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ rkey: "3abc123", status: "updated" }),});const data = await response.json();// → { "uri": "at://did:plc:abc/xyz.statusphere.status/3abc123", "cid": "bafyrei..." }const response = await fetch("http://127.0.0.1:3000/xrpc/xyz.statusphere.setStatus", { method: "POST", headers: { "X-Client-Key": CLIENT_KEY, Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ rkey: "3abc123", status: "updated" }),});const data = await response.json();// → { "uri": "at://did:plc:abc/xyz.statusphere.status/3abc123", "cid": "bafyrei..." }let response = client .post("http://127.0.0.1:3000/xrpc/xyz.statusphere.setStatus") .header("X-Client-Key", client_key) .header("Authorization", format!("Bearer {}", token)) .json(&serde_json::json!({ "rkey": "3abc123", "status": "updated" })) .send() .await?;// → { "uri": "at://did:plc:abc/xyz.statusphere.status/3abc123", "cid": "bafyrei..." }body := `{ "rkey": "3abc123", "status": "updated" }`req, _ := http.NewRequest("POST", "http://127.0.0.1:3000/xrpc/xyz.statusphere.setStatus", bytes.NewBufferString(body))req.Header.Set("X-Client-Key", clientKey)req.Header.Set("Authorization", "Bearer "+token)req.Header.Set("Content-Type", "application/json")resp, err := http.DefaultClient.Do(req)// → { "uri": "at://did:plc:abc/xyz.statusphere.status/3abc123", "cid": "bafyrei..." }curl -X POST http://127.0.0.1:3000/xrpc/xyz.statusphere.setStatus \ -H "X-Client-Key: $CLIENT_KEY" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "rkey": "3abc123", "status": "updated" }'# → { "uri": "at://did:plc:abc/xyz.statusphere.status/3abc123", "cid": "bafyrei..." }Use case
Section titled “Use case”This is useful when the client knows whether it’s creating or editing, but you want a single endpoint for both. The client omits rkey for new records and includes it when editing an existing one. The rkey from the initial create response acts as the record’s stable identifier for future updates.

