import { err, ok, type Result } from "./types/result.ts"; import type { AccessToken, Did, EmailAddress, Handle, Nsid, RefreshToken, Rkey, } from "./types/branded.ts"; import { unsafeAsAccessToken, unsafeAsDid, unsafeAsEmail, unsafeAsHandle, unsafeAsISODate, unsafeAsRefreshToken, } from "./types/branded.ts"; import type { AccountInfo, ApiErrorCode, AppPassword, CompletePasskeySetupResponse, ConfirmSignupResult, CreateAccountParams, CreateAccountResult, CreateBackupResponse, CreatedAppPassword, CreateRecordResponse, DidDocument, DidType, EmailUpdateResponse, EnableTotpResponse, FinishPasskeyRegistrationResponse, GetInviteCodesResponse, InviteCodeInfo, LegacyLoginPreference, ListBackupsResponse, ListPasskeysResponse, ListRecordsResponse, ListReposResponse, ListSessionsResponse, ListTrustedDevicesResponse, NotificationHistoryResponse, NotificationPrefs, PasskeyAccountCreateResponse, PasswordStatus, ReauthPasskeyStartResponse, ReauthResponse, ReauthStatus, RecommendedDidCredentials, RecordResponse, RegenerateBackupCodesResponse, RepoDescription, ResendMigrationVerificationResponse, ReserveSigningKeyResponse, SearchAccountsResponse, ServerConfig, ServerDescription, ServerStats, Session, SetBackupEnabledResponse, StartPasskeyRegistrationResponse, SuccessResponse, TotpSecret, TotpStatus, UpdateLegacyLoginResponse, UpdateLocaleResponse, UploadBlobResponse, VerificationChannel, VerifyMigrationEmailResponse, VerifyTokenResponse, } from "./types/api.ts"; const API_BASE = "/xrpc"; export class ApiError extends Error { public did?: Did; public reauthMethods?: string[]; constructor( public status: number, public error: ApiErrorCode, message: string, did?: string, reauthMethods?: string[], ) { super(message); this.name = "ApiError"; this.did = did ? unsafeAsDid(did) : undefined; this.reauthMethods = reauthMethods; } } let tokenRefreshCallback: (() => Promise) | null = null; export function setTokenRefreshCallback( callback: () => Promise, ) { tokenRefreshCallback = callback; } interface XrpcOptions { method?: "GET" | "POST"; params?: Record; body?: unknown; token?: string; skipRetry?: boolean; } async function xrpc(method: string, options?: XrpcOptions): Promise { const { method: httpMethod = "GET", params, body, token, skipRetry } = options ?? {}; let url = `${API_BASE}/${method}`; if (params) { const searchParams = new URLSearchParams(params); url += `?${searchParams}`; } const headers: Record = {}; if (token) { headers["Authorization"] = `Bearer ${token}`; } if (body) { headers["Content-Type"] = "application/json"; } const res = await fetch(url, { method: httpMethod, headers, body: body ? JSON.stringify(body) : undefined, }); if (!res.ok) { const errData = await res.json().catch(() => ({ error: "Unknown", message: res.statusText, })); if ( res.status === 401 && (errData.error === "AuthenticationFailed" || errData.error === "ExpiredToken") && token && tokenRefreshCallback && !skipRetry ) { const newToken = await tokenRefreshCallback(); if (newToken && newToken !== token) { return xrpc(method, { ...options, token: newToken, skipRetry: true }); } } throw new ApiError( res.status, errData.error as ApiErrorCode, errData.message, errData.did, errData.reauthMethods, ); } return res.json(); } async function xrpcResult( method: string, options?: XrpcOptions, ): Promise> { try { const value = await xrpc(method, options); return ok(value); } catch (e) { if (e instanceof ApiError) { return err(e); } return err( new ApiError(0, "Unknown", e instanceof Error ? e.message : String(e)), ); } } export interface VerificationMethod { id: string; type: string; publicKeyMultibase: string; } export type { AppPassword, DidDocument, InviteCodeInfo as InviteCode, Session }; export type { ConfirmSignupResult, CreateAccountParams, CreateAccountResult, DidType, VerificationChannel, }; function castSession(raw: unknown): Session { const s = raw as Record; return { did: unsafeAsDid(s.did as string), handle: unsafeAsHandle(s.handle as string), email: s.email ? unsafeAsEmail(s.email as string) : undefined, emailConfirmed: s.emailConfirmed as boolean | undefined, preferredChannel: s.preferredChannel as VerificationChannel | undefined, preferredChannelVerified: s.preferredChannelVerified as boolean | undefined, isAdmin: s.isAdmin as boolean | undefined, active: s.active as boolean | undefined, status: s.status as Session["status"], migratedToPds: s.migratedToPds as string | undefined, migratedAt: s.migratedAt ? unsafeAsISODate(s.migratedAt as string) : undefined, accessJwt: unsafeAsAccessToken(s.accessJwt as string), refreshJwt: unsafeAsRefreshToken(s.refreshJwt as string), }; } export const api = { async createAccount( params: CreateAccountParams, byodToken?: string, ): Promise { const url = `${API_BASE}/com.atproto.server.createAccount`; const headers: Record = { "Content-Type": "application/json", }; if (byodToken) { headers["Authorization"] = `Bearer ${byodToken}`; } const response = await fetch(url, { method: "POST", headers, body: JSON.stringify({ handle: params.handle, email: params.email, password: params.password, inviteCode: params.inviteCode, didType: params.didType, did: params.did, signingKey: params.signingKey, verificationChannel: params.verificationChannel, discordId: params.discordId, telegramUsername: params.telegramUsername, signalNumber: params.signalNumber, }), }); const data = await response.json(); if (!response.ok) { throw new ApiError(response.status, data.error, data.message); } return data; }, async createAccountWithServiceAuth( serviceAuthToken: string, params: { did: Did; handle: Handle; email: EmailAddress; password: string; inviteCode?: string; }, ): Promise { const url = `${API_BASE}/com.atproto.server.createAccount`; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${serviceAuthToken}`, }, body: JSON.stringify({ did: params.did, handle: params.handle, email: params.email, password: params.password, inviteCode: params.inviteCode, }), }); const data = await response.json(); if (!response.ok) { throw new ApiError(response.status, data.error, data.message); } return castSession(data); }, confirmSignup( did: Did, verificationCode: string, ): Promise { return xrpc("com.atproto.server.confirmSignup", { method: "POST", body: { did, verificationCode }, }); }, resendVerification(did: Did): Promise<{ success: boolean }> { return xrpc("com.atproto.server.resendVerification", { method: "POST", body: { did }, }); }, async createSession(identifier: string, password: string): Promise { const raw = await xrpc("com.atproto.server.createSession", { method: "POST", body: { identifier, password }, }); return castSession(raw); }, checkEmailVerified(identifier: string): Promise<{ verified: boolean }> { return xrpc("_checkEmailVerified", { method: "POST", body: { identifier }, }); }, async getSession(token: AccessToken): Promise { const raw = await xrpc("com.atproto.server.getSession", { token }); return castSession(raw); }, async refreshSession(refreshJwt: RefreshToken): Promise { const raw = await xrpc("com.atproto.server.refreshSession", { method: "POST", token: refreshJwt, }); return castSession(raw); }, async deleteSession(token: AccessToken): Promise { await xrpc("com.atproto.server.deleteSession", { method: "POST", token, }); }, listAppPasswords(token: AccessToken): Promise<{ passwords: AppPassword[] }> { return xrpc("com.atproto.server.listAppPasswords", { token }); }, createAppPassword( token: AccessToken, name: string, scopes?: string, ): Promise { return xrpc("com.atproto.server.createAppPassword", { method: "POST", token, body: { name, scopes }, }); }, async revokeAppPassword(token: AccessToken, name: string): Promise { await xrpc("com.atproto.server.revokeAppPassword", { method: "POST", token, body: { name }, }); }, getAccountInviteCodes( token: AccessToken, ): Promise<{ codes: InviteCodeInfo[] }> { return xrpc("com.atproto.server.getAccountInviteCodes", { token }); }, createInviteCode( token: AccessToken, useCount: number = 1, ): Promise<{ code: string }> { return xrpc("com.atproto.server.createInviteCode", { method: "POST", token, body: { useCount }, }); }, async requestPasswordReset(email: EmailAddress): Promise { await xrpc("com.atproto.server.requestPasswordReset", { method: "POST", body: { email }, }); }, async resetPassword(token: string, password: string): Promise { await xrpc("com.atproto.server.resetPassword", { method: "POST", body: { token, password }, }); }, requestEmailUpdate(token: AccessToken): Promise { return xrpc("com.atproto.server.requestEmailUpdate", { method: "POST", token, }); }, async updateEmail( token: AccessToken, email: string, emailToken?: string, ): Promise { await xrpc("com.atproto.server.updateEmail", { method: "POST", token, body: { email, token: emailToken }, }); }, async updateHandle(token: AccessToken, handle: Handle): Promise { await xrpc("com.atproto.identity.updateHandle", { method: "POST", token, body: { handle }, }); }, async requestAccountDelete(token: AccessToken): Promise { await xrpc("com.atproto.server.requestAccountDelete", { method: "POST", token, }); }, async deleteAccount( did: Did, password: string, deleteToken: string, ): Promise { await xrpc("com.atproto.server.deleteAccount", { method: "POST", body: { did, password, token: deleteToken }, }); }, describeServer(): Promise { return xrpc("com.atproto.server.describeServer"); }, listRepos(limit?: number): Promise { const params: Record = {}; if (limit) params.limit = String(limit); return xrpc("com.atproto.sync.listRepos", { params }); }, getNotificationPrefs(token: AccessToken): Promise { return xrpc("_account.getNotificationPrefs", { token }); }, updateNotificationPrefs(token: AccessToken, prefs: { preferredChannel?: string; discordId?: string; telegramUsername?: string; signalNumber?: string; }): Promise { return xrpc("_account.updateNotificationPrefs", { method: "POST", token, body: prefs, }); }, confirmChannelVerification( token: AccessToken, channel: string, identifier: string, code: string, ): Promise { return xrpc("_account.confirmChannelVerification", { method: "POST", token, body: { channel, identifier, code }, }); }, getNotificationHistory( token: AccessToken, ): Promise { return xrpc("_account.getNotificationHistory", { token }); }, getServerStats(token: AccessToken): Promise { return xrpc("_admin.getServerStats", { token }); }, getServerConfig(): Promise { return xrpc("_server.getConfig"); }, updateServerConfig( token: AccessToken, config: { serverName?: string; primaryColor?: string; primaryColorDark?: string; secondaryColor?: string; secondaryColorDark?: string; logoCid?: string; }, ): Promise { return xrpc("_admin.updateServerConfig", { method: "POST", token, body: config, }); }, async uploadBlob( token: AccessToken, file: File, ): Promise { const res = await fetch("/xrpc/com.atproto.repo.uploadBlob", { method: "POST", headers: { "Authorization": `Bearer ${token}`, "Content-Type": file.type, }, body: file, }); if (!res.ok) { const errData = await res.json().catch(() => ({ error: "Unknown", message: res.statusText, })); throw new ApiError(res.status, errData.error, errData.message); } return res.json(); }, async changePassword( token: AccessToken, currentPassword: string, newPassword: string, ): Promise { await xrpc("_account.changePassword", { method: "POST", token, body: { currentPassword, newPassword }, }); }, removePassword(token: AccessToken): Promise { return xrpc("_account.removePassword", { method: "POST", token, }); }, setPassword(token: AccessToken, newPassword: string): Promise { return xrpc("_account.setPassword", { method: "POST", token, body: { newPassword }, }); }, getPasswordStatus(token: AccessToken): Promise { return xrpc("_account.getPasswordStatus", { token }); }, getLegacyLoginPreference(token: AccessToken): Promise { return xrpc("_account.getLegacyLoginPreference", { token }); }, updateLegacyLoginPreference( token: AccessToken, allowLegacyLogin: boolean, ): Promise { return xrpc("_account.updateLegacyLoginPreference", { method: "POST", token, body: { allowLegacyLogin }, }); }, updateLocale( token: AccessToken, preferredLocale: string, ): Promise { return xrpc("_account.updateLocale", { method: "POST", token, body: { preferredLocale }, }); }, listSessions(token: AccessToken): Promise { return xrpc("_account.listSessions", { token }); }, async revokeSession(token: AccessToken, sessionId: string): Promise { await xrpc("_account.revokeSession", { method: "POST", token, body: { sessionId }, }); }, revokeAllSessions(token: AccessToken): Promise<{ revokedCount: number }> { return xrpc("_account.revokeAllSessions", { method: "POST", token, }); }, searchAccounts(token: AccessToken, options?: { handle?: string; cursor?: string; limit?: number; }): Promise { const params: Record = {}; if (options?.handle) params.handle = options.handle; if (options?.cursor) params.cursor = options.cursor; if (options?.limit) params.limit = String(options.limit); return xrpc("com.atproto.admin.searchAccounts", { token, params }); }, getInviteCodes(token: AccessToken, options?: { sort?: "recent" | "usage"; cursor?: string; limit?: number; }): Promise { const params: Record = {}; if (options?.sort) params.sort = options.sort; if (options?.cursor) params.cursor = options.cursor; if (options?.limit) params.limit = String(options.limit); return xrpc("com.atproto.admin.getInviteCodes", { token, params }); }, async disableInviteCodes( token: AccessToken, codes?: string[], accounts?: string[], ): Promise { await xrpc("com.atproto.admin.disableInviteCodes", { method: "POST", token, body: { codes, accounts }, }); }, getAccountInfo(token: AccessToken, did: Did): Promise { return xrpc("com.atproto.admin.getAccountInfo", { token, params: { did } }); }, async disableAccountInvites(token: AccessToken, account: Did): Promise { await xrpc("com.atproto.admin.disableAccountInvites", { method: "POST", token, body: { account }, }); }, async enableAccountInvites(token: AccessToken, account: Did): Promise { await xrpc("com.atproto.admin.enableAccountInvites", { method: "POST", token, body: { account }, }); }, async adminDeleteAccount(token: AccessToken, did: Did): Promise { await xrpc("com.atproto.admin.deleteAccount", { method: "POST", token, body: { did }, }); }, describeRepo(token: AccessToken, repo: Did): Promise { return xrpc("com.atproto.repo.describeRepo", { token, params: { repo }, }); }, listRecords(token: AccessToken, repo: Did, collection: Nsid, options?: { limit?: number; cursor?: string; reverse?: boolean; }): Promise { const params: Record = { repo, collection }; if (options?.limit) params.limit = String(options.limit); if (options?.cursor) params.cursor = options.cursor; if (options?.reverse) params.reverse = "true"; return xrpc("com.atproto.repo.listRecords", { token, params }); }, getRecord( token: AccessToken, repo: Did, collection: Nsid, rkey: Rkey, ): Promise { return xrpc("com.atproto.repo.getRecord", { token, params: { repo, collection, rkey }, }); }, createRecord( token: AccessToken, repo: Did, collection: Nsid, record: unknown, rkey?: Rkey, ): Promise { return xrpc("com.atproto.repo.createRecord", { method: "POST", token, body: { repo, collection, record, rkey }, }); }, putRecord( token: AccessToken, repo: Did, collection: Nsid, rkey: Rkey, record: unknown, ): Promise { return xrpc("com.atproto.repo.putRecord", { method: "POST", token, body: { repo, collection, rkey, record }, }); }, async deleteRecord( token: AccessToken, repo: Did, collection: Nsid, rkey: Rkey, ): Promise { await xrpc("com.atproto.repo.deleteRecord", { method: "POST", token, body: { repo, collection, rkey }, }); }, getTotpStatus(token: AccessToken): Promise { return xrpc("com.atproto.server.getTotpStatus", { token }); }, createTotpSecret(token: AccessToken): Promise { return xrpc("com.atproto.server.createTotpSecret", { method: "POST", token, }); }, enableTotp(token: AccessToken, code: string): Promise { return xrpc("com.atproto.server.enableTotp", { method: "POST", token, body: { code }, }); }, disableTotp( token: AccessToken, password: string, code: string, ): Promise { return xrpc("com.atproto.server.disableTotp", { method: "POST", token, body: { password, code }, }); }, regenerateBackupCodes( token: AccessToken, password: string, code: string, ): Promise { return xrpc("com.atproto.server.regenerateBackupCodes", { method: "POST", token, body: { password, code }, }); }, startPasskeyRegistration( token: AccessToken, friendlyName?: string, ): Promise { return xrpc("com.atproto.server.startPasskeyRegistration", { method: "POST", token, body: { friendlyName }, }); }, finishPasskeyRegistration( token: AccessToken, credential: unknown, friendlyName?: string, ): Promise { return xrpc("com.atproto.server.finishPasskeyRegistration", { method: "POST", token, body: { credential, friendlyName }, }); }, listPasskeys(token: AccessToken): Promise { return xrpc("com.atproto.server.listPasskeys", { token }); }, async deletePasskey(token: AccessToken, id: string): Promise { await xrpc("com.atproto.server.deletePasskey", { method: "POST", token, body: { id }, }); }, async updatePasskey( token: AccessToken, id: string, friendlyName: string, ): Promise { await xrpc("com.atproto.server.updatePasskey", { method: "POST", token, body: { id, friendlyName }, }); }, listTrustedDevices(token: AccessToken): Promise { return xrpc("_account.listTrustedDevices", { token }); }, revokeTrustedDevice( token: AccessToken, deviceId: string, ): Promise { return xrpc("_account.revokeTrustedDevice", { method: "POST", token, body: { deviceId }, }); }, updateTrustedDevice( token: AccessToken, deviceId: string, friendlyName: string, ): Promise { return xrpc("_account.updateTrustedDevice", { method: "POST", token, body: { deviceId, friendlyName }, }); }, getReauthStatus(token: AccessToken): Promise { return xrpc("_account.getReauthStatus", { token }); }, reauthPassword( token: AccessToken, password: string, ): Promise { return xrpc("_account.reauthPassword", { method: "POST", token, body: { password }, }); }, reauthTotp(token: AccessToken, code: string): Promise { return xrpc("_account.reauthTotp", { method: "POST", token, body: { code }, }); }, reauthPasskeyStart(token: AccessToken): Promise { return xrpc("_account.reauthPasskeyStart", { method: "POST", token, }); }, reauthPasskeyFinish( token: AccessToken, credential: unknown, ): Promise { return xrpc("_account.reauthPasskeyFinish", { method: "POST", token, body: { credential }, }); }, reserveSigningKey(did?: Did): Promise { return xrpc("com.atproto.server.reserveSigningKey", { method: "POST", body: { did }, }); }, getRecommendedDidCredentials( token: AccessToken, ): Promise { return xrpc("com.atproto.identity.getRecommendedDidCredentials", { token }); }, async activateAccount(token: AccessToken): Promise { await xrpc("com.atproto.server.activateAccount", { method: "POST", token, }); }, async createPasskeyAccount(params: { handle: Handle; email?: EmailAddress; inviteCode?: string; didType?: DidType; did?: Did; signingKey?: string; verificationChannel?: VerificationChannel; discordId?: string; telegramUsername?: string; signalNumber?: string; }, byodToken?: string): Promise { const url = `${API_BASE}/_account.createPasskeyAccount`; const headers: Record = { "Content-Type": "application/json", }; if (byodToken) { headers["Authorization"] = `Bearer ${byodToken}`; } const res = await fetch(url, { method: "POST", headers, body: JSON.stringify(params), }); if (!res.ok) { const errData = await res.json().catch(() => ({ error: "Unknown", message: res.statusText, })); throw new ApiError(res.status, errData.error, errData.message); } return res.json(); }, startPasskeyRegistrationForSetup( did: Did, setupToken: string, friendlyName?: string, ): Promise { return xrpc("_account.startPasskeyRegistrationForSetup", { method: "POST", body: { did, setupToken, friendlyName }, }); }, completePasskeySetup( did: Did, setupToken: string, passkeyCredential: unknown, passkeyFriendlyName?: string, ): Promise { return xrpc("_account.completePasskeySetup", { method: "POST", body: { did, setupToken, passkeyCredential, passkeyFriendlyName }, }); }, requestPasskeyRecovery(email: EmailAddress): Promise { return xrpc("_account.requestPasskeyRecovery", { method: "POST", body: { email }, }); }, recoverPasskeyAccount( did: Did, recoveryToken: string, newPassword: string, ): Promise { return xrpc("_account.recoverPasskeyAccount", { method: "POST", body: { did, recoveryToken, newPassword }, }); }, verifyMigrationEmail( token: string, email: EmailAddress, ): Promise { return xrpc("com.atproto.server.verifyMigrationEmail", { method: "POST", body: { token, email }, }); }, resendMigrationVerification( email: EmailAddress, ): Promise { return xrpc("com.atproto.server.resendMigrationVerification", { method: "POST", body: { email }, }); }, verifyToken( token: string, identifier: string, accessToken?: AccessToken, ): Promise { return xrpc("_account.verifyToken", { method: "POST", body: { token, identifier }, token: accessToken, }); }, getDidDocument(token: AccessToken): Promise { return xrpc("_account.getDidDocument", { token }); }, updateDidDocument( token: AccessToken, params: { verificationMethods?: VerificationMethod[]; alsoKnownAs?: string[]; serviceEndpoint?: string; }, ): Promise { return xrpc("_account.updateDidDocument", { method: "POST", token, body: params, }); }, async deactivateAccount( token: AccessToken, deleteAfter?: string, ): Promise { await xrpc("com.atproto.server.deactivateAccount", { method: "POST", token, body: { deleteAfter }, }); }, async getRepo(token: AccessToken, did: Did): Promise { const url = `${API_BASE}/com.atproto.sync.getRepo?did=${ encodeURIComponent(did) }`; const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` }, }); if (!res.ok) { const errData = await res.json().catch(() => ({ error: "Unknown", message: res.statusText, })); throw new ApiError(res.status, errData.error, errData.message); } return res.arrayBuffer(); }, listBackups(token: AccessToken): Promise { return xrpc("_backup.listBackups", { token }); }, async getBackup(token: AccessToken, id: string): Promise { const url = `${API_BASE}/_backup.getBackup?id=${encodeURIComponent(id)}`; const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` }, }); if (!res.ok) { const errData = await res.json().catch(() => ({ error: "Unknown", message: res.statusText, })); throw new ApiError(res.status, errData.error, errData.message); } return res.blob(); }, createBackup(token: AccessToken): Promise { return xrpc("_backup.createBackup", { method: "POST", token, }); }, async deleteBackup(token: AccessToken, id: string): Promise { await xrpc("_backup.deleteBackup", { method: "POST", token, params: { id }, }); }, setBackupEnabled( token: AccessToken, enabled: boolean, ): Promise { return xrpc("_backup.setEnabled", { method: "POST", token, body: { enabled }, }); }, async importRepo(token: AccessToken, car: Uint8Array): Promise { const url = `${API_BASE}/com.atproto.repo.importRepo`; const res = await fetch(url, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/vnd.ipld.car", }, body: car as unknown as BodyInit, }); if (!res.ok) { const errData = await res.json().catch(() => ({ error: "Unknown", message: res.statusText, })); throw new ApiError(res.status, errData.error, errData.message); } }, }; export const typedApi = { createSession( identifier: string, password: string, ): Promise> { return xrpcResult("com.atproto.server.createSession", { method: "POST", body: { identifier, password }, }).then((r) => r.ok ? ok(castSession(r.value)) : r); }, getSession(token: AccessToken): Promise> { return xrpcResult("com.atproto.server.getSession", { token }) .then((r) => r.ok ? ok(castSession(r.value)) : r); }, refreshSession(refreshJwt: RefreshToken): Promise> { return xrpcResult("com.atproto.server.refreshSession", { method: "POST", token: refreshJwt, }).then((r) => r.ok ? ok(castSession(r.value)) : r); }, describeServer(): Promise> { return xrpcResult("com.atproto.server.describeServer"); }, listAppPasswords( token: AccessToken, ): Promise> { return xrpcResult("com.atproto.server.listAppPasswords", { token }); }, createAppPassword( token: AccessToken, name: string, scopes?: string, ): Promise> { return xrpcResult("com.atproto.server.createAppPassword", { method: "POST", token, body: { name, scopes }, }); }, revokeAppPassword( token: AccessToken, name: string, ): Promise> { return xrpcResult("com.atproto.server.revokeAppPassword", { method: "POST", token, body: { name }, }); }, listSessions( token: AccessToken, ): Promise> { return xrpcResult("_account.listSessions", { token }); }, revokeSession( token: AccessToken, sessionId: string, ): Promise> { return xrpcResult("_account.revokeSession", { method: "POST", token, body: { sessionId }, }); }, getTotpStatus(token: AccessToken): Promise> { return xrpcResult("com.atproto.server.getTotpStatus", { token }); }, createTotpSecret(token: AccessToken): Promise> { return xrpcResult("com.atproto.server.createTotpSecret", { method: "POST", token, }); }, enableTotp( token: AccessToken, code: string, ): Promise> { return xrpcResult("com.atproto.server.enableTotp", { method: "POST", token, body: { code }, }); }, disableTotp( token: AccessToken, password: string, code: string, ): Promise> { return xrpcResult("com.atproto.server.disableTotp", { method: "POST", token, body: { password, code }, }); }, listPasskeys( token: AccessToken, ): Promise> { return xrpcResult("com.atproto.server.listPasskeys", { token }); }, deletePasskey( token: AccessToken, id: string, ): Promise> { return xrpcResult("com.atproto.server.deletePasskey", { method: "POST", token, body: { id }, }); }, listTrustedDevices( token: AccessToken, ): Promise> { return xrpcResult("_account.listTrustedDevices", { token }); }, getReauthStatus(token: AccessToken): Promise> { return xrpcResult("_account.getReauthStatus", { token }); }, getNotificationPrefs( token: AccessToken, ): Promise> { return xrpcResult("_account.getNotificationPrefs", { token }); }, updateHandle( token: AccessToken, handle: Handle, ): Promise> { return xrpcResult("com.atproto.identity.updateHandle", { method: "POST", token, body: { handle }, }); }, describeRepo( token: AccessToken, repo: Did, ): Promise> { return xrpcResult("com.atproto.repo.describeRepo", { token, params: { repo }, }); }, listRecords( token: AccessToken, repo: Did, collection: Nsid, options?: { limit?: number; cursor?: string; reverse?: boolean }, ): Promise> { const params: Record = { repo, collection }; if (options?.limit) params.limit = String(options.limit); if (options?.cursor) params.cursor = options.cursor; if (options?.reverse) params.reverse = "true"; return xrpcResult("com.atproto.repo.listRecords", { token, params }); }, getRecord( token: AccessToken, repo: Did, collection: Nsid, rkey: Rkey, ): Promise> { return xrpcResult("com.atproto.repo.getRecord", { token, params: { repo, collection, rkey }, }); }, deleteRecord( token: AccessToken, repo: Did, collection: Nsid, rkey: Rkey, ): Promise> { return xrpcResult("com.atproto.repo.deleteRecord", { method: "POST", token, body: { repo, collection, rkey }, }); }, searchAccounts( token: AccessToken, options?: { handle?: string; cursor?: string; limit?: number }, ): Promise> { const params: Record = {}; if (options?.handle) params.handle = options.handle; if (options?.cursor) params.cursor = options.cursor; if (options?.limit) params.limit = String(options.limit); return xrpcResult("com.atproto.admin.searchAccounts", { token, params }); }, getAccountInfo( token: AccessToken, did: Did, ): Promise> { return xrpcResult("com.atproto.admin.getAccountInfo", { token, params: { did }, }); }, getServerStats(token: AccessToken): Promise> { return xrpcResult("_admin.getServerStats", { token }); }, listBackups( token: AccessToken, ): Promise> { return xrpcResult("_backup.listBackups", { token }); }, createBackup( token: AccessToken, ): Promise> { return xrpcResult("_backup.createBackup", { method: "POST", token, }); }, getDidDocument(token: AccessToken): Promise> { return xrpcResult("_account.getDidDocument", { token }); }, deleteSession(token: AccessToken): Promise> { return xrpcResult("com.atproto.server.deleteSession", { method: "POST", token, }); }, revokeAllSessions( token: AccessToken, ): Promise> { return xrpcResult("_account.revokeAllSessions", { method: "POST", token, }); }, getAccountInviteCodes( token: AccessToken, ): Promise> { return xrpcResult("com.atproto.server.getAccountInviteCodes", { token }); }, createInviteCode( token: AccessToken, useCount: number = 1, ): Promise> { return xrpcResult("com.atproto.server.createInviteCode", { method: "POST", token, body: { useCount }, }); }, changePassword( token: AccessToken, currentPassword: string, newPassword: string, ): Promise> { return xrpcResult("_account.changePassword", { method: "POST", token, body: { currentPassword, newPassword }, }); }, getPasswordStatus( token: AccessToken, ): Promise> { return xrpcResult("_account.getPasswordStatus", { token }); }, getServerConfig(): Promise> { return xrpcResult("_server.getConfig"); }, getLegacyLoginPreference( token: AccessToken, ): Promise> { return xrpcResult("_account.getLegacyLoginPreference", { token }); }, updateLegacyLoginPreference( token: AccessToken, allowLegacyLogin: boolean, ): Promise> { return xrpcResult("_account.updateLegacyLoginPreference", { method: "POST", token, body: { allowLegacyLogin }, }); }, getNotificationHistory( token: AccessToken, ): Promise> { return xrpcResult("_account.getNotificationHistory", { token }); }, updateNotificationPrefs( token: AccessToken, prefs: { preferredChannel?: string; discordId?: string; telegramUsername?: string; signalNumber?: string; }, ): Promise> { return xrpcResult("_account.updateNotificationPrefs", { method: "POST", token, body: prefs, }); }, revokeTrustedDevice( token: AccessToken, deviceId: string, ): Promise> { return xrpcResult("_account.revokeTrustedDevice", { method: "POST", token, body: { deviceId }, }); }, updateTrustedDevice( token: AccessToken, deviceId: string, friendlyName: string, ): Promise> { return xrpcResult("_account.updateTrustedDevice", { method: "POST", token, body: { deviceId, friendlyName }, }); }, reauthPassword( token: AccessToken, password: string, ): Promise> { return xrpcResult("_account.reauthPassword", { method: "POST", token, body: { password }, }); }, reauthTotp( token: AccessToken, code: string, ): Promise> { return xrpcResult("_account.reauthTotp", { method: "POST", token, body: { code }, }); }, reauthPasskeyStart( token: AccessToken, ): Promise> { return xrpcResult("_account.reauthPasskeyStart", { method: "POST", token, }); }, reauthPasskeyFinish( token: AccessToken, credential: unknown, ): Promise> { return xrpcResult("_account.reauthPasskeyFinish", { method: "POST", token, body: { credential }, }); }, confirmSignup( did: Did, verificationCode: string, ): Promise> { return xrpcResult("com.atproto.server.confirmSignup", { method: "POST", body: { did, verificationCode }, }); }, resendVerification( did: Did, ): Promise> { return xrpcResult("com.atproto.server.resendVerification", { method: "POST", body: { did }, }); }, requestEmailUpdate( token: AccessToken, ): Promise> { return xrpcResult("com.atproto.server.requestEmailUpdate", { method: "POST", token, }); }, updateEmail( token: AccessToken, email: string, emailToken?: string, ): Promise> { return xrpcResult("com.atproto.server.updateEmail", { method: "POST", token, body: { email, token: emailToken }, }); }, requestAccountDelete(token: AccessToken): Promise> { return xrpcResult("com.atproto.server.requestAccountDelete", { method: "POST", token, }); }, deleteAccount( did: Did, password: string, deleteToken: string, ): Promise> { return xrpcResult("com.atproto.server.deleteAccount", { method: "POST", body: { did, password, token: deleteToken }, }); }, updateDidDocument( token: AccessToken, params: { verificationMethods?: VerificationMethod[]; alsoKnownAs?: string[]; serviceEndpoint?: string; }, ): Promise> { return xrpcResult("_account.updateDidDocument", { method: "POST", token, body: params, }); }, deactivateAccount( token: AccessToken, deleteAfter?: string, ): Promise> { return xrpcResult("com.atproto.server.deactivateAccount", { method: "POST", token, body: { deleteAfter }, }); }, activateAccount(token: AccessToken): Promise> { return xrpcResult("com.atproto.server.activateAccount", { method: "POST", token, }); }, setBackupEnabled( token: AccessToken, enabled: boolean, ): Promise> { return xrpcResult("_backup.setEnabled", { method: "POST", token, body: { enabled }, }); }, deleteBackup( token: AccessToken, id: string, ): Promise> { return xrpcResult("_backup.deleteBackup", { method: "POST", token, params: { id }, }); }, createRecord( token: AccessToken, repo: Did, collection: Nsid, record: unknown, rkey?: Rkey, ): Promise> { return xrpcResult("com.atproto.repo.createRecord", { method: "POST", token, body: { repo, collection, record, rkey }, }); }, putRecord( token: AccessToken, repo: Did, collection: Nsid, rkey: Rkey, record: unknown, ): Promise> { return xrpcResult("com.atproto.repo.putRecord", { method: "POST", token, body: { repo, collection, rkey, record }, }); }, getInviteCodes( token: AccessToken, options?: { sort?: "recent" | "usage"; cursor?: string; limit?: number }, ): Promise> { const params: Record = {}; if (options?.sort) params.sort = options.sort; if (options?.cursor) params.cursor = options.cursor; if (options?.limit) params.limit = String(options.limit); return xrpcResult("com.atproto.admin.getInviteCodes", { token, params }); }, disableAccountInvites( token: AccessToken, account: Did, ): Promise> { return xrpcResult("com.atproto.admin.disableAccountInvites", { method: "POST", token, body: { account }, }); }, enableAccountInvites( token: AccessToken, account: Did, ): Promise> { return xrpcResult("com.atproto.admin.enableAccountInvites", { method: "POST", token, body: { account }, }); }, adminDeleteAccount( token: AccessToken, did: Did, ): Promise> { return xrpcResult("com.atproto.admin.deleteAccount", { method: "POST", token, body: { did }, }); }, startPasskeyRegistration( token: AccessToken, friendlyName?: string, ): Promise> { return xrpcResult("com.atproto.server.startPasskeyRegistration", { method: "POST", token, body: { friendlyName }, }); }, finishPasskeyRegistration( token: AccessToken, credential: unknown, friendlyName?: string, ): Promise> { return xrpcResult("com.atproto.server.finishPasskeyRegistration", { method: "POST", token, body: { credential, friendlyName }, }); }, updatePasskey( token: AccessToken, id: string, friendlyName: string, ): Promise> { return xrpcResult("com.atproto.server.updatePasskey", { method: "POST", token, body: { id, friendlyName }, }); }, regenerateBackupCodes( token: AccessToken, password: string, code: string, ): Promise> { return xrpcResult("com.atproto.server.regenerateBackupCodes", { method: "POST", token, body: { password, code }, }); }, updateLocale( token: AccessToken, preferredLocale: string, ): Promise> { return xrpcResult("_account.updateLocale", { method: "POST", token, body: { preferredLocale }, }); }, confirmChannelVerification( token: AccessToken, channel: string, identifier: string, code: string, ): Promise> { return xrpcResult("_account.confirmChannelVerification", { method: "POST", token, body: { channel, identifier, code }, }); }, removePassword( token: AccessToken, ): Promise> { return xrpcResult("_account.removePassword", { method: "POST", token, }); }, };