import { ok, err, type Result } from './types/result' import type { Did, Handle, AccessToken, RefreshToken, Cid, Rkey, AtUri, Nsid, ISODateString, EmailAddress, InviteCode as InviteCodeBrand, } from './types/branded' import { unsafeAsDid, unsafeAsHandle, unsafeAsAccessToken, unsafeAsRefreshToken, unsafeAsCid, unsafeAsISODate, unsafeAsEmail, unsafeAsInviteCode, } from './types/branded' import type { Session, DidDocument, AppPassword, CreatedAppPassword, InviteCodeInfo, ServerDescription, NotificationPrefs, NotificationHistoryResponse, ServerStats, ServerConfig, UploadBlobResponse, ListSessionsResponse, SearchAccountsResponse, GetInviteCodesResponse, AccountInfo, RepoDescription, ListRecordsResponse, RecordResponse, CreateRecordResponse, TotpStatus, TotpSecret, EnableTotpResponse, RegenerateBackupCodesResponse, ListPasskeysResponse, StartPasskeyRegistrationResponse, FinishPasskeyRegistrationResponse, ListTrustedDevicesResponse, ReauthStatus, ReauthResponse, ReauthPasskeyStartResponse, ReserveSigningKeyResponse, RecommendedDidCredentials, PasskeyAccountCreateResponse, CompletePasskeySetupResponse, VerifyTokenResponse, ListBackupsResponse, CreateBackupResponse, SetBackupEnabledResponse, EmailUpdateResponse, LegacyLoginPreference, UpdateLegacyLoginResponse, UpdateLocaleResponse, PasswordStatus, SuccessResponse, CheckEmailVerifiedResponse, VerifyMigrationEmailResponse, ResendMigrationVerificationResponse, ListReposResponse, VerificationChannel, DidType, ApiErrorCode, VerificationMethod as VerificationMethodType, CreateAccountParams, CreateAccountResult, ConfirmSignupResult, } from './types/api' 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 { Session, DidDocument, AppPassword, InviteCodeInfo as InviteCode } export type { VerificationChannel, DidType, CreateAccountParams, CreateAccountResult, ConfirmSignupResult } 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, }) }, 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, }) 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, }) }, }