diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml
deleted file mode 100644
index 133981a0..00000000
--- a/.github/workflows/detect-breaking-changes.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-name: CI
-on:
- pull_request:
- branches:
- - main
- - next
-
-jobs:
- detect_breaking_changes:
- runs-on: 'ubuntu-latest'
- name: detect-breaking-changes
- if: github.repository == 'beeper/desktop-api-js'
- steps:
- - name: Calculate fetch-depth
- run: |
- echo "FETCH_DEPTH=$(expr ${{ github.event.pull_request.commits }} + 1)" >> $GITHUB_ENV
-
- - uses: actions/checkout@v6
- with:
- # Ensure we can check out the pull request base in the script below.
- fetch-depth: ${{ env.FETCH_DEPTH }}
-
- - name: Set up Node
- uses: actions/setup-node@v3
- with:
- node-version: '20'
- - name: Install dependencies
- run: |
- yarn install
-
- - name: Detect breaking changes
- run: |
- # Try to check out previous versions of the breaking change detection script. This ensures that
- # we still detect breaking changes when entire files and their tests are removed.
- git checkout "${{ github.event.pull_request.base.sha }}" -- ./scripts/detect-breaking-changes 2>/dev/null || true
- ./scripts/detect-breaking-changes ${{ github.event.pull_request.base.sha }}
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 1ed3fbdc..32dbe0e2 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "4.8.0"
+ ".": "5.0.0"
}
diff --git a/.stats.yml b/.stats.yml
index ec755712..2dd3fee6 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 23
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/beeper/beeper-desktop-api-356444646dafe352d3ef7c2e01aedf030197a5519b41cf2c3fd8be2571456b43.yml
-openapi_spec_hash: 4840f003552e8b48eb8e689b59a819ef
-config_hash: 05ebdec072113f63395372504da98192
+configured_endpoints: 30
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/beeper/beeper-desktop-api-c08c14bb754b4cb0e02b21fabb680469368286be339dec0aaa8c69d04a1f021a.yml
+openapi_spec_hash: a10246aaf7cdc33b682fc245bd5f893b
+config_hash: 72f9d43b9b51a5da912e9f3730e53ae2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 10169ccf..4932b1a8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
# Changelog
+## 5.0.0 (2026-05-07)
+
+Full Changelog: [v4.8.0...v5.0.0](https://github.com/beeper/desktop-api-js/compare/v4.8.0...v5.0.0)
+
+### Features
+
+* **api:** api update ([275b76c](https://github.com/beeper/desktop-api-js/commit/275b76c5c9dcfe29b20eccc61458e01f1367983e))
+* **api:** api update ([5105024](https://github.com/beeper/desktop-api-js/commit/5105024706e94083f476f063b20930961adc6775))
+
+
+### Bug Fixes
+
+* **mcp:** remove Stainless sandbox execution mode ([8211c14](https://github.com/beeper/desktop-api-js/commit/8211c149c3934e7d6c0d6edc820b9890b0c4f8f6))
+
## 4.8.0 (2026-05-01)
Full Changelog: [v4.7.1...v4.8.0](https://github.com/beeper/desktop-api-js/compare/v4.7.1...v4.8.0)
diff --git a/README.md b/README.md
index 538d0074..86d6f1f2 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ const client = new BeeperDesktop({
});
const page = await client.chats.search({
+ accountIDs: ['matrix', 'discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'],
includeMuted: true,
limit: 3,
type: 'single',
@@ -173,9 +174,9 @@ async function fetchAllMessages(params) {
const allMessages = [];
// Automatically fetches more pages as needed.
for await (const message of client.messages.search({
- accountIDs: ['local-telegram_ba_QFrb5lrLPhO3OT5MFBeTWv0x4BI'],
+ accountIDs: ['discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'],
limit: 10,
- query: 'deployment',
+ query: 'oauth',
})) {
allMessages.push(message);
}
@@ -187,9 +188,9 @@ Alternatively, you can request a single page at a time:
```ts
let page = await client.messages.search({
- accountIDs: ['local-telegram_ba_QFrb5lrLPhO3OT5MFBeTWv0x4BI'],
+ accountIDs: ['discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'],
limit: 10,
- query: 'deployment',
+ query: 'oauth',
});
for (const message of page.items) {
console.log(message);
@@ -220,7 +221,11 @@ const client = createClient({
// ... then make API calls as usual.
const accounts = await client.accounts.list();
-const chat = await client.chats.create({ accountID: 'accountID' });
+const chat = await client.chats.create({
+ accountID: 'accountID',
+ participantIDs: ['string'],
+ type: 'single',
+});
```
Each API resource has two versions, the full resource (e.g., `Accounts`) which includes all subresources, and the base resource (e.g., `BaseAccounts`) which does not.
diff --git a/api.md b/api.md
index 4cdfd4f7..bde6c07e 100644
--- a/api.md
+++ b/api.md
@@ -49,14 +49,20 @@ Types:
- Chat
- ChatCreateResponse
- ChatListResponse
+- ChatStartResponse
Methods:
- client.chats.create({ ...params }) -> ChatCreateResponse
- client.chats.retrieve(chatID, { ...params }) -> Chat
+- client.chats.update(chatID, { ...params }) -> Chat
- client.chats.list({ ...params }) -> ChatListResponsesCursorNoLimit
- client.chats.archive(chatID, { ...params }) -> void
+- client.chats.markRead(chatID, { ...params }) -> Chat
+- client.chats.markUnread(chatID, { ...params }) -> Chat
+- client.chats.notifyAnyway(chatID) -> Chat
- client.chats.search({ ...params }) -> ChatsCursorSearch
+- client.chats.start({ ...params }) -> ChatStartResponse
## Reminders
@@ -76,7 +82,7 @@ Types:
Methods:
-- client.chats.messages.reactions.delete(messageID, { ...params }) -> ReactionDeleteResponse
+- client.chats.messages.reactions.delete(reactionKey, { ...params }) -> ReactionDeleteResponse
- client.chats.messages.reactions.add(messageID, { ...params }) -> ReactionAddResponse
# Messages
@@ -88,8 +94,10 @@ Types:
Methods:
+- client.messages.retrieve(messageID, { ...params }) -> Message
- client.messages.update(messageID, { ...params }) -> MessageUpdateResponse
- client.messages.list(chatID, { ...params }) -> MessagesCursorNoLimit
+- client.messages.delete(messageID, { ...params }) -> void
- client.messages.search({ ...params }) -> MessagesCursorSearch
- client.messages.send(chatID, { ...params }) -> MessageSendResponse
diff --git a/package.json b/package.json
index d1a952d9..0943a761 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@beeper/desktop-api",
- "version": "4.8.0",
+ "version": "5.0.0",
"description": "The official TypeScript library for the Beeper Desktop API",
"author": "Beeper Desktop ",
"types": "dist/index.d.ts",
diff --git a/packages/mcp-server/manifest.json b/packages/mcp-server/manifest.json
index da7ba56b..d59e1349 100644
--- a/packages/mcp-server/manifest.json
+++ b/packages/mcp-server/manifest.json
@@ -1,7 +1,7 @@
{
"dxt_version": "0.2",
"name": "@beeper/desktop-mcp",
- "version": "4.8.0",
+ "version": "5.0.0",
"description": "The official MCP Server for the Beeper Desktop API",
"author": {
"name": "Beeper Desktop",
diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json
index 2f3b8f5b..67c9e044 100644
--- a/packages/mcp-server/package.json
+++ b/packages/mcp-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@beeper/desktop-mcp",
- "version": "4.8.0",
+ "version": "5.0.0",
"description": "The official MCP Server for the Beeper Desktop API",
"author": "Beeper Desktop ",
"types": "dist/index.d.ts",
diff --git a/packages/mcp-server/src/code-tool-worker.ts b/packages/mcp-server/src/code-tool-worker.ts
index cf27caeb..b7ad928e 100644
--- a/packages/mcp-server/src/code-tool-worker.ts
+++ b/packages/mcp-server/src/code-tool-worker.ts
@@ -116,13 +116,20 @@ const fuse = new Fuse(
'client.chats.archive',
'client.chats.create',
'client.chats.list',
+ 'client.chats.markRead',
+ 'client.chats.markUnread',
+ 'client.chats.notifyAnyway',
'client.chats.retrieve',
'client.chats.search',
+ 'client.chats.start',
+ 'client.chats.update',
'client.chats.reminders.create',
'client.chats.reminders.delete',
'client.chats.messages.reactions.add',
'client.chats.messages.reactions.delete',
+ 'client.messages.delete',
'client.messages.list',
+ 'client.messages.retrieve',
'client.messages.search',
'client.messages.send',
'client.messages.update',
diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts
index ea89629f..88e2dd35 100644
--- a/packages/mcp-server/src/code-tool.ts
+++ b/packages/mcp-server/src/code-tool.ts
@@ -1,17 +1,8 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-import {
- ContentBlock,
- McpRequestContext,
- McpTool,
- Metadata,
- ToolCallResult,
- asErrorResult,
- asTextContentResult,
-} from './types';
+import { ContentBlock, McpRequestContext, McpTool, Metadata, ToolCallResult, asErrorResult } from './types';
import { Tool } from '@modelcontextprotocol/sdk/types.js';
-import { readEnv, requireValue } from './util';
-import { WorkerInput, WorkerOutput } from './code-tool-types';
+import { WorkerOutput } from './code-tool-types';
import { getLogger } from './logger';
import { SdkMethod } from './methods';
import { McpCodeExecutionMode } from './options';
@@ -26,6 +17,7 @@ For example:
\`\`\`
async function run(client) {
const page = await client.chats.search({
+ accountIDs: ['matrix', 'discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'],
includeMuted: true,
limit: 3,
type: 'single',
@@ -54,8 +46,7 @@ Always type dynamic key-value stores explicitly as Record
* @param blockedMethods - The methods to block for code execution. Blocking is done by simple string
* matching, so it is not secure against obfuscation. For stronger security, block in the downstream API
* with limited API keys.
- * @param codeExecutionMode - Whether to execute code in a local Deno environment or in a remote
- * sandbox environment hosted by Stainless.
+ * @param codeExecutionMode - Whether to execute code in a local Deno environment.
*/
export function codeTool({
blockedMethods,
@@ -113,13 +104,8 @@ export function codeTool({
let result: ToolCallResult;
const startTime = Date.now();
- if (codeExecutionMode === 'local') {
- logger.debug('Executing code in local Deno environment');
- result = await localDenoHandler({ reqContext, args });
- } else {
- logger.debug('Executing code in remote Stainless environment');
- result = await remoteStainlessHandler({ reqContext, args });
- }
+ logger.debug('Executing code in local Deno environment');
+ result = await localDenoHandler({ reqContext, args });
logger.info(
{
@@ -136,71 +122,6 @@ export function codeTool({
return { metadata, tool, handler };
}
-const remoteStainlessHandler = async ({
- reqContext,
- args,
-}: {
- reqContext: McpRequestContext;
- args: any;
-}): Promise => {
- const code = args.code as string;
- const intent = args.intent as string | undefined;
- const client = reqContext.client;
-
- const codeModeEndpoint = readEnv('CODE_MODE_ENDPOINT_URL') ?? 'https://api.stainless.com/api/ai/code-tool';
-
- const localClientEnvs = {
- BEEPER_ACCESS_TOKEN: requireValue(
- readEnv('BEEPER_ACCESS_TOKEN') ?? client.accessToken,
- 'set BEEPER_ACCESS_TOKEN environment variable or provide accessToken client option',
- ),
- BEEPER_BASE_URL: readEnv('BEEPER_BASE_URL') ?? client.baseURL ?? undefined,
- };
- // Merge any upstream client envs from the request header, with upstream values taking precedence.
- const mergedClientEnvs = { ...localClientEnvs, ...reqContext.upstreamClientEnvs };
-
- // Setting a Stainless API key authenticates requests to the code tool endpoint.
- const res = await fetch(codeModeEndpoint, {
- method: 'POST',
- headers: {
- ...(reqContext.stainlessApiKey && { Authorization: reqContext.stainlessApiKey }),
- 'Content-Type': 'application/json',
- 'x-stainless-mcp-client-envs': JSON.stringify(mergedClientEnvs),
- },
- body: JSON.stringify({
- project_name: 'beeper-desktop-api',
- code,
- intent,
- client_opts: {},
- } satisfies WorkerInput),
- });
-
- if (!res.ok) {
- if (res.status === 404 && !reqContext.stainlessApiKey) {
- throw new Error(
- 'Could not access code tool for this project. You may need to provide a Stainless API key via the STAINLESS_API_KEY environment variable, the --stainless-api-key flag, or the x-stainless-api-key HTTP header.',
- );
- }
- throw new Error(
- `${res.status}: ${
- res.statusText
- } error when trying to contact Code Tool server. Details: ${await res.text()}`,
- );
- }
-
- const { is_error, result, log_lines, err_lines } = (await res.json()) as WorkerOutput;
- const hasLogs = log_lines.length > 0 || err_lines.length > 0;
- const output = {
- result,
- ...(log_lines.length > 0 && { log_lines }),
- ...(err_lines.length > 0 && { err_lines }),
- };
- if (is_error) {
- return asErrorResult(typeof result === 'string' && !hasLogs ? result : JSON.stringify(output, null, 2));
- }
- return asTextContentResult(output);
-};
-
const localDenoHandler = async ({
reqContext,
args,
diff --git a/packages/mcp-server/src/local-docs-search.ts b/packages/mcp-server/src/local-docs-search.ts
index 416d6964..e2d61606 100644
--- a/packages/mcp-server/src/local-docs-search.ts
+++ b/packages/mcp-server/src/local-docs-search.ts
@@ -56,7 +56,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
httpMethod: 'post',
summary: 'Focus Beeper Desktop app',
description:
- 'Focus Beeper Desktop and optionally navigate to a specific chat, message, or pre-fill draft text and attachment.',
+ 'Focus Beeper Desktop and optionally navigate to a specific chat, message, or pre-fill plain text and an image path.',
stainlessPath: '(resource) $client > (method) focus',
qualified: 'client.focus',
params: [
@@ -67,7 +67,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
],
response: '{ success: boolean; }',
markdown:
- "## focus\n\n`client.focus(chatID?: string, draftAttachmentPath?: string, draftText?: string, messageID?: string): { success: boolean; }`\n\n**post** `/v1/focus`\n\nFocus Beeper Desktop and optionally navigate to a specific chat, message, or pre-fill draft text and attachment.\n\n### Parameters\n\n- `chatID?: string`\n Optional Beeper chat ID (or local chat ID) to focus after opening the app. If omitted, only opens/focuses the app.\n\n- `draftAttachmentPath?: string`\n Optional draft attachment path to populate in the message input field.\n\n- `draftText?: string`\n Optional draft text to populate in the message input field.\n\n- `messageID?: string`\n Optional message ID. Jumps to that message in the chat when opening.\n\n### Returns\n\n- `{ success: boolean; }`\n Response indicating successful app focus action.\n\n - `success: boolean`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.focus();\n\nconsole.log(response);\n```",
+ "## focus\n\n`client.focus(chatID?: string, draftAttachmentPath?: string, draftText?: string, messageID?: string): { success: boolean; }`\n\n**post** `/v1/focus`\n\nFocus Beeper Desktop and optionally navigate to a specific chat, message, or pre-fill plain text and an image path.\n\n### Parameters\n\n- `chatID?: string`\n Optional Beeper chat ID (or local chat ID) to focus after opening the app. If omitted, only opens/focuses the app.\n\n- `draftAttachmentPath?: string`\n Optional image path to populate in the message input field.\n\n- `draftText?: string`\n Optional plain text to populate in the message input field.\n\n- `messageID?: string`\n Optional message ID. Jumps to that message in the chat when opening.\n\n### Returns\n\n- `{ success: boolean; }`\n Response indicating successful app focus action.\n\n - `success: boolean`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.focus();\n\nconsole.log(response);\n```",
perLanguage: {
typescript: {
method: 'client.focus',
@@ -112,7 +112,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
response:
'{ results: { chats: object[]; in_groups: object[]; messages: { chats: object; hasMore: boolean; items: message[]; newestCursor: string; oldestCursor: string; }; }; }',
markdown:
- "## search\n\n`client.search(query: string): { results: object; }`\n\n**get** `/v1/search`\n\nReturns matching chats, participant name matches in groups, and the first page of messages in one call. Paginate messages via search-messages. Paginate chats via search-chats.\n\n### Parameters\n\n- `query: string`\n User-typed search text. Literal word matching (non-semantic).\n\n### Returns\n\n- `{ results: { chats: object[]; in_groups: object[]; messages: { chats: object; hasMore: boolean; items: message[]; newestCursor: string; oldestCursor: string; }; }; }`\n\n - `results: { chats: { id: string; accountID: string; participants: { hasMore: boolean; items: user[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; isArchived?: boolean; isMuted?: boolean; isPinned?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; }[]; in_groups: { id: string; accountID: string; participants: { hasMore: boolean; items: user[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; isArchived?: boolean; isMuted?: boolean; isPinned?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; }[]; messages: { chats: object; hasMore: boolean; items: { id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: attachment[]; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; reactions?: reaction[]; senderName?: string; text?: string; type?: string; }[]; newestCursor: string; oldestCursor: string; }; }`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.search({ query: 'x' });\n\nconsole.log(response);\n```",
+ "## search\n\n`client.search(query: string): { results: object; }`\n\n**get** `/v1/search`\n\nReturns matching chats, participant name matches in groups, and the first page of messages in one call. Paginate messages via search-messages. Paginate chats via search-chats.\n\n### Parameters\n\n- `query: string`\n User-typed search text. Literal word matching (non-semantic).\n\n### Returns\n\n- `{ results: { chats: object[]; in_groups: object[]; messages: { chats: object; hasMore: boolean; items: message[]; newestCursor: string; oldestCursor: string; }; }; }`\n\n - `results: { chats: { id: string; accountID: string; network: string; participants: { hasMore: boolean; items: user[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: object; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: object; participantActions?: object; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: object; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }; description?: string; draft?: { text: string; attachments?: object; }; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }; snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }; unreadMentionsCount?: number; }[]; in_groups: { id: string; accountID: string; network: string; participants: { hasMore: boolean; items: user[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: object; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: object; participantActions?: object; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: object; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }; description?: string; draft?: { text: string; attachments?: object; }; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }; snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }; unreadMentionsCount?: number; }[]; messages: { chats: object; hasMore: boolean; items: { id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: attachment[]; editedTimestamp?: string; isDeleted?: boolean; isHidden?: boolean; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; links?: object[]; mentions?: string[]; reactions?: reaction[]; seen?: boolean | string | object; senderName?: string; sendStatus?: object; text?: string; type?: string; }[]; newestCursor: string; oldestCursor: string; }; }`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.search({ query: 'x' });\n\nconsole.log(response);\n```",
perLanguage: {
typescript: {
method: 'client.search',
@@ -148,15 +148,15 @@ const EMBEDDED_METHODS: MethodEntry[] = [
name: 'list',
endpoint: '/v1/accounts',
httpMethod: 'get',
- summary: 'List connected accounts',
+ summary: 'List Chat Accounts',
description:
- 'Lists chat accounts across networks (WhatsApp, Telegram, Twitter/X, etc.) actively connected to this Beeper Desktop instance',
+ 'List Chat Accounts connected to this Beeper Desktop instance, including bridge metadata and network identity.',
stainlessPath: '(resource) accounts > (method) list',
qualified: 'client.accounts.list',
response:
"{ accountID: string; bridge: { id: string; provider: 'cloud' | 'self-hosted' | 'local' | 'platform-sdk'; type: string; }; user: object; network?: string; }[]",
markdown:
- "## list\n\n`client.accounts.list(): object[]`\n\n**get** `/v1/accounts`\n\nLists chat accounts across networks (WhatsApp, Telegram, Twitter/X, etc.) actively connected to this Beeper Desktop instance\n\n### Returns\n\n- `{ accountID: string; bridge: { id: string; provider: 'cloud' | 'self-hosted' | 'local' | 'platform-sdk'; type: string; }; user: object; network?: string; }[]`\n Accounts configured on this device. Includes accountID, bridge metadata, optional network name, and user identity.\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst accounts = await client.accounts.list();\n\nconsole.log(accounts);\n```",
+ "## list\n\n`client.accounts.list(): object[]`\n\n**get** `/v1/accounts`\n\nList Chat Accounts connected to this Beeper Desktop instance, including bridge metadata and network identity.\n\n### Returns\n\n- `{ accountID: string; bridge: { id: string; provider: 'cloud' | 'self-hosted' | 'local' | 'platform-sdk'; type: string; }; user: object; network?: string; }[]`\n Accounts configured on this device. Includes accountID, bridge metadata, optional network name, and user identity.\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst accounts = await client.accounts.list();\n\nconsole.log(accounts);\n```",
perLanguage: {
typescript: {
method: 'client.accounts.list',
@@ -295,9 +295,9 @@ const EMBEDDED_METHODS: MethodEntry[] = [
qualified: 'client.chats.retrieve',
params: ['chatID: string;', 'maxParticipantCount?: number;'],
response:
- "{ id: string; accountID: string; participants: { hasMore: boolean; items: object[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; isArchived?: boolean; isMuted?: boolean; isPinned?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; }",
+ "{ id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }",
markdown:
- "## retrieve\n\n`client.chats.retrieve(chatID: string, maxParticipantCount?: number): { id: string; accountID: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; isArchived?: boolean; isMuted?: boolean; isPinned?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; }`\n\n**get** `/v1/chats/{chatID}`\n\nRetrieve chat details including metadata, participants, and latest message\n\n### Parameters\n\n- `chatID: string`\n Unique identifier of the chat.\n\n- `maxParticipantCount?: number`\n Maximum number of participants to return. Use -1 for all; otherwise 0–500. Defaults to all (-1).\n\n### Returns\n\n- `{ id: string; accountID: string; participants: { hasMore: boolean; items: object[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; isArchived?: boolean; isMuted?: boolean; isPinned?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; }`\n\n - `id: string`\n - `accountID: string`\n - `participants: { hasMore: boolean; items: { id: string; cannotMessage?: boolean; email?: string; fullName?: string; imgURL?: string; isSelf?: boolean; phoneNumber?: string; username?: string; }[]; total: number; }`\n - `title: string`\n - `type: 'single' | 'group'`\n - `unreadCount: number`\n - `isArchived?: boolean`\n - `isMuted?: boolean`\n - `isPinned?: boolean`\n - `lastActivity?: string`\n - `lastReadMessageSortKey?: string`\n - `localChatID?: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst chat = await client.chats.retrieve('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nconsole.log(chat);\n```",
+ "## retrieve\n\n`client.chats.retrieve(chatID: string, maxParticipantCount?: number): { id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }`\n\n**get** `/v1/chats/{chatID}`\n\nRetrieve chat details including metadata, participants, and latest message\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `maxParticipantCount?: number`\n Maximum number of participants to return. Use -1 for all; otherwise 0-500. Defaults to 100. List and search endpoints return up to 20 participants per chat.\n\n### Returns\n\n- `{ id: string; accountID: string; network: string; participants: { hasMore: boolean; items: object[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: { omitEmptyTimer?: boolean; timers?: number[]; types?: 'afterRead' | 'afterSend'[]; }; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: { acceptWithButton?: -2 | -1 | 0 | 1 | 2; acceptWithMessage?: -2 | -1 | 0 | 1 | 2; }; participantActions?: { ban?: -2 | -1 | 0 | 1 | 2; invite?: -2 | -1 | 0 | 1 | 2; kick?: -2 | -1 | 0 | 1 | 2; leave?: -2 | -1 | 0 | 1 | 2; revokeInvite?: -2 | -1 | 0 | 1 | 2; }; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: { avatar?: object; description?: object; disappearingTimer?: object; title?: object; }; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }; description?: string; draft?: { text: string; attachments?: object; }; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }; snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }; unreadMentionsCount?: number; }`\n\n - `id: string`\n - `accountID: string`\n - `network: string`\n - `participants: { hasMore: boolean; items: { id: string; cannotMessage?: boolean; email?: string; fullName?: string; imgURL?: string; isSelf?: boolean; phoneNumber?: string; username?: string; }[]; total: number; }`\n - `title: string`\n - `type: 'single' | 'group'`\n - `unreadCount: number`\n - `capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: { omitEmptyTimer?: boolean; timers?: number[]; types?: 'afterRead' | 'afterSend'[]; }; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: { acceptWithButton?: -2 | -1 | 0 | 1 | 2; acceptWithMessage?: -2 | -1 | 0 | 1 | 2; }; participantActions?: { ban?: -2 | -1 | 0 | 1 | 2; invite?: -2 | -1 | 0 | 1 | 2; kick?: -2 | -1 | 0 | 1 | 2; leave?: -2 | -1 | 0 | 1 | 2; revokeInvite?: -2 | -1 | 0 | 1 | 2; }; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: { avatar?: { level: -2 | -1 | 0 | 1 | 2; }; description?: { level: -2 | -1 | 0 | 1 | 2; }; disappearingTimer?: { level: -2 | -1 | 0 | 1 | 2; }; title?: { level: -2 | -1 | 0 | 1 | 2; }; }; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }`\n - `description?: string`\n - `draft?: { text: string; attachments?: object; }`\n - `imgURL?: string`\n - `isArchived?: boolean`\n - `isLowPriority?: boolean`\n - `isMarkedUnread?: boolean`\n - `isMuted?: boolean`\n - `isPinned?: boolean`\n - `isReadOnly?: boolean`\n - `lastActivity?: string`\n - `lastReadMessageSortKey?: string`\n - `localChatID?: string`\n - `messageExpirySeconds?: number`\n - `reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }`\n - `snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }`\n - `unreadMentionsCount?: number`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst chat = await client.chats.retrieve('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nconsole.log(chat);\n```",
perLanguage: {
typescript: {
method: 'client.chats.retrieve',
@@ -322,7 +322,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
php: {
method: 'chats->retrieve',
example:
- "chats->retrieve(\n '!NCdzlIaMjZUmvmvyHU:beeper.com', maxParticipantCount: 50\n);\n\nvar_dump($chat);",
+ "chats->retrieve(\n '!NCdzlIaMjZUmvmvyHU:beeper.com', maxParticipantCount: 100\n);\n\nvar_dump($chat);",
},
http: {
example:
@@ -334,53 +334,101 @@ const EMBEDDED_METHODS: MethodEntry[] = [
name: 'create',
endpoint: '/v1/chats',
httpMethod: 'post',
- summary: 'Create or start a chat',
- description:
- 'Create a direct or group chat with mode="create", or use mode="start" to resolve a contact and open a direct chat.',
+ summary: 'Create a chat',
+ description: 'Create a direct or group chat from participant IDs. Returns the created chat.',
stainlessPath: '(resource) chats > (method) create',
qualified: 'client.chats.create',
params: [
'accountID: string;',
- 'allowInvite?: boolean;',
+ 'participantIDs: string[];',
+ "type: 'single' | 'group';",
'messageText?: string;',
- "mode?: 'start' | 'create';",
- 'participantIDs?: string[];',
'title?: string;',
- "type?: 'single' | 'group';",
- 'user?: { id?: string; email?: string; fullName?: string; phoneNumber?: string; username?: string; };',
],
- response: "{ chatID: string; status?: 'existing' | 'created'; }",
+ response:
+ "{ id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }",
markdown:
- "## create\n\n`client.chats.create(accountID: string, allowInvite?: boolean, messageText?: string, mode?: 'start' | 'create', participantIDs?: string[], title?: string, type?: 'single' | 'group', user?: { id?: string; email?: string; fullName?: string; phoneNumber?: string; username?: string; }): { chatID: string; status?: 'existing' | 'created'; }`\n\n**post** `/v1/chats`\n\nCreate a direct or group chat with mode=\"create\", or use mode=\"start\" to resolve a contact and open a direct chat.\n\n### Parameters\n\n- `accountID: string`\n Account to create or start the chat on.\n\n- `allowInvite?: boolean`\n Only used for mode='start'. Whether invite-based DM creation is allowed when required by the platform.\n\n- `messageText?: string`\n Optional first message content if the platform requires it to create the chat.\n\n- `mode?: 'start' | 'create'`\n Operation mode. Use 'start' to resolve a user/contact and start a direct chat. Omit or set 'create' to create a chat directly.\n\n- `participantIDs?: string[]`\n Required for create mode. Provide exactly one user ID for 'single' chats and one or more for 'group' chats.\n\n- `title?: string`\n Optional title for group chats; ignored for single chats on most networks.\n\n- `type?: 'single' | 'group'`\n Required for create mode. 'single' creates a direct message chat; 'group' creates a group chat.\n\n- `user?: { id?: string; email?: string; fullName?: string; phoneNumber?: string; username?: string; }`\n Required for mode='start'. Merged user-like contact payload used to resolve the best identifier.\n - `id?: string`\n Known user ID when available.\n - `email?: string`\n Email candidate.\n - `fullName?: string`\n Display name hint used for ranking only.\n - `phoneNumber?: string`\n Phone number candidate (E.164 preferred).\n - `username?: string`\n Username/handle candidate.\n\n### Returns\n\n- `{ chatID: string; status?: 'existing' | 'created'; }`\n\n - `chatID: string`\n - `status?: 'existing' | 'created'`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst chat = await client.chats.create({ accountID: 'accountID' });\n\nconsole.log(chat);\n```",
+ "## create\n\n`client.chats.create(accountID: string, participantIDs: string[], type: 'single' | 'group', messageText?: string, title?: string): object`\n\n**post** `/v1/chats`\n\nCreate a direct or group chat from participant IDs. Returns the created chat.\n\n### Parameters\n\n- `accountID: string`\n Account to create or start the chat on.\n\n- `participantIDs: string[]`\n User IDs to include in the new chat.\n\n- `type: 'single' | 'group'`\n 'single' requires exactly one participantID; 'group' supports multiple participants and optional title.\n\n- `messageText?: string`\n Optional first message content if the platform requires it to create the chat.\n\n- `title?: string`\n Optional title for group chats; ignored for single chats on most networks.\n\n### Returns\n\n- `{ id: string; accountID: string; network: string; participants: { hasMore: boolean; items: user[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: object; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: object; participantActions?: object; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: object; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }; description?: string; draft?: { text: string; attachments?: object; }; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }; snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }; unreadMentionsCount?: number; }`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst chat = await client.chats.create({\n accountID: 'accountID',\n participantIDs: ['string'],\n type: 'single',\n});\n\nconsole.log(chat);\n```",
perLanguage: {
typescript: {
method: 'client.chats.create',
example:
- "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst chat = await client.chats.create({ accountID: 'accountID' });\n\nconsole.log(chat.chatID);",
+ "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst chat = await client.chats.create({\n accountID: 'accountID',\n participantIDs: ['string'],\n type: 'single',\n});\n\nconsole.log(chat);",
},
python: {
method: 'chats.create',
example:
- 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nchat = client.chats.create(\n account_id="accountID",\n)\nprint(chat.chat_id)',
+ 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nchat = client.chats.create(\n account_id="accountID",\n participant_ids=["string"],\n type="single",\n)\nprint(chat)',
},
go: {
method: 'client.Chats.New',
example:
- 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\tchat, err := client.Chats.New(context.TODO(), beeperdesktopapi.ChatNewParams{\n\t\tAccountID: "accountID",\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", chat.ChatID)\n}\n',
+ 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\tchat, err := client.Chats.New(context.TODO(), beeperdesktopapi.ChatNewParams{\n\t\tAccountID: "accountID",\n\t\tParticipantIDs: []string{"string"},\n\t\tType: beeperdesktopapi.ChatNewParamsTypeSingle,\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", chat)\n}\n',
},
cli: {
method: 'chats create',
example:
- "beeper-desktop-cli chats create \\\n --access-token 'My Access Token' \\\n --account-id accountID",
+ "beeper-desktop-cli chats create \\\n --access-token 'My Access Token' \\\n --account-id accountID \\\n --participant-id string \\\n --type single",
},
php: {
method: 'chats->create',
example:
- "chats->create(\n accountID: 'accountID',\n allowInvite: true,\n messageText: 'messageText',\n mode: 'start',\n participantIDs: ['string'],\n title: 'title',\n type: 'single',\n user: [\n 'id' => 'id',\n 'email' => 'email',\n 'fullName' => 'fullName',\n 'phoneNumber' => 'phoneNumber',\n 'username' => 'username',\n ],\n);\n\nvar_dump($chat);",
+ "chats->create(\n accountID: 'accountID',\n participantIDs: ['string'],\n type: 'single',\n messageText: 'messageText',\n title: 'title',\n);\n\nvar_dump($chat);",
+ },
+ http: {
+ example:
+ 'curl http://localhost:23373/v1/chats \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN" \\\n -d \'{\n "accountID": "accountID",\n "participantIDs": [\n "string"\n ],\n "type": "single"\n }\'',
+ },
+ },
+ },
+ {
+ name: 'start',
+ endpoint: '/v1/chats/start',
+ httpMethod: 'post',
+ summary: 'Start a direct chat',
+ description:
+ 'Resolve a user/contact and open a direct chat. Reuses and returns an existing direct chat when one is found. Available in Beeper Desktop v4.2.808+.',
+ stainlessPath: '(resource) chats > (method) start',
+ qualified: 'client.chats.start',
+ params: [
+ 'accountID: string;',
+ 'user: { id?: string; email?: string; fullName?: string; phoneNumber?: string; username?: string; };',
+ 'allowInvite?: boolean;',
+ 'messageText?: string;',
+ ],
+ response:
+ "{ id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }",
+ markdown:
+ "## start\n\n`client.chats.start(accountID: string, user: { id?: string; email?: string; fullName?: string; phoneNumber?: string; username?: string; }, allowInvite?: boolean, messageText?: string): object`\n\n**post** `/v1/chats/start`\n\nResolve a user/contact and open a direct chat. Reuses and returns an existing direct chat when one is found. Available in Beeper Desktop v4.2.808+.\n\n### Parameters\n\n- `accountID: string`\n Account to create or start the chat on.\n\n- `user: { id?: string; email?: string; fullName?: string; phoneNumber?: string; username?: string; }`\n Merged user-like contact payload used to resolve the best identifier.\n - `id?: string`\n Known user ID when available.\n - `email?: string`\n Email candidate.\n - `fullName?: string`\n Display name hint used for ranking only.\n - `phoneNumber?: string`\n Phone number candidate (E.164 preferred).\n - `username?: string`\n Username/handle candidate.\n\n- `allowInvite?: boolean`\n Whether invite-based DM creation is allowed when required by the platform.\n\n- `messageText?: string`\n Optional first message content if the platform requires it to create the chat.\n\n### Returns\n\n- `{ id: string; accountID: string; network: string; participants: { hasMore: boolean; items: user[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: object; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: object; participantActions?: object; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: object; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }; description?: string; draft?: { text: string; attachments?: object; }; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }; snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }; unreadMentionsCount?: number; }`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.chats.start({\n accountID: 'accountID',\n user: {},\n});\n\nconsole.log(response);\n```",
+ perLanguage: {
+ typescript: {
+ method: 'client.chats.start',
+ example:
+ "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst response = await client.chats.start({\n accountID: 'accountID',\n user: {},\n});\n\nconsole.log(response);",
+ },
+ python: {
+ method: 'chats.start',
+ example:
+ 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nresponse = client.chats.start(\n account_id="accountID",\n user={},\n)\nprint(response)',
+ },
+ go: {
+ method: 'client.Chats.Start',
+ example:
+ 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\tresponse, err := client.Chats.Start(context.TODO(), beeperdesktopapi.ChatStartParams{\n\t\tAccountID: "accountID",\n\t\tUser: beeperdesktopapi.ChatStartParamsUser{},\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", response)\n}\n',
+ },
+ cli: {
+ method: 'chats start',
+ example:
+ "beeper-desktop-cli chats start \\\n --access-token 'My Access Token' \\\n --account-id accountID \\\n --user '{}'",
+ },
+ php: {
+ method: 'chats->start',
+ example:
+ "chats->start(\n accountID: 'accountID',\n user: [\n 'id' => 'id',\n 'email' => 'email',\n 'fullName' => 'fullName',\n 'phoneNumber' => 'phoneNumber',\n 'username' => 'username',\n ],\n allowInvite: true,\n messageText: 'messageText',\n);\n\nvar_dump($response);",
},
http: {
example:
- 'curl http://localhost:23373/v1/chats \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN" \\\n -d \'{\n "accountID": "accountID"\n }\'',
+ 'curl http://localhost:23373/v1/chats/start \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN" \\\n -d \'{\n "accountID": "accountID",\n "user": {}\n }\'',
},
},
},
@@ -395,9 +443,9 @@ const EMBEDDED_METHODS: MethodEntry[] = [
qualified: 'client.chats.list',
params: ['accountIDs?: string[];', 'cursor?: string;', "direction?: 'after' | 'before';"],
response:
- "{ id: string; accountID: string; participants: { hasMore: boolean; items: user[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; isArchived?: boolean; isMuted?: boolean; isPinned?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; }",
+ "{ id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }",
markdown:
- "## list\n\n`client.chats.list(accountIDs?: string[], cursor?: string, direction?: 'after' | 'before'): object`\n\n**get** `/v1/chats`\n\nList all chats sorted by last activity (most recent first). Combines all accounts into a single paginated list.\n\n### Parameters\n\n- `accountIDs?: string[]`\n Limit to specific account IDs. If omitted, fetches from all accounts.\n\n- `cursor?: string`\n Opaque pagination cursor; do not inspect. Use together with 'direction'.\n\n- `direction?: 'after' | 'before'`\n Pagination direction used with 'cursor': 'before' fetches older results, 'after' fetches newer results. Defaults to 'before' when only 'cursor' is provided.\n\n### Returns\n\n- `{ id: string; accountID: string; participants: { hasMore: boolean; items: user[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; isArchived?: boolean; isMuted?: boolean; isPinned?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; }`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\n// Automatically fetches more pages as needed.\nfor await (const chatListResponse of client.chats.list()) {\n console.log(chatListResponse);\n}\n```",
+ "## list\n\n`client.chats.list(accountIDs?: string[], cursor?: string, direction?: 'after' | 'before'): object`\n\n**get** `/v1/chats`\n\nList all chats sorted by last activity (most recent first). Combines all accounts into a single paginated list.\n\n### Parameters\n\n- `accountIDs?: string[]`\n Limit to specific account IDs. If omitted, fetches from all accounts.\n\n- `cursor?: string`\n Opaque pagination cursor; do not inspect. Use together with 'direction'.\n\n- `direction?: 'after' | 'before'`\n Pagination direction used with 'cursor': 'before' fetches older results, 'after' fetches newer results. Defaults to 'before' when only 'cursor' is provided.\n\n### Returns\n\n- `{ id: string; accountID: string; network: string; participants: { hasMore: boolean; items: user[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: object; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: object; participantActions?: object; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: object; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }; description?: string; draft?: { text: string; attachments?: object; }; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }; snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }; unreadMentionsCount?: number; }`\n Chat with optional last message preview.\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\n// Automatically fetches more pages as needed.\nfor await (const chatListResponse of client.chats.list()) {\n console.log(chatListResponse);\n}\n```",
perLanguage: {
typescript: {
method: 'client.chats.list',
@@ -421,7 +469,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
php: {
method: 'chats->list',
example:
- "chats->list(\n accountIDs: [\n 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc',\n 'local-instagram_ba_eRfQMmnSNy_p7Ih7HL7RduRpKFU',\n ],\n cursor: '1725489123456|c29tZUltc2dQYWdl',\n direction: 'before',\n);\n\nvar_dump($page);",
+ "chats->list(\n accountIDs: [\n 'matrix', 'discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'\n ],\n cursor: '1725489123456|c29tZUltc2dQYWdl',\n direction: 'before',\n);\n\nvar_dump($page);",
},
http: {
example:
@@ -452,9 +500,9 @@ const EMBEDDED_METHODS: MethodEntry[] = [
'unreadOnly?: boolean;',
],
response:
- "{ id: string; accountID: string; participants: { hasMore: boolean; items: object[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; isArchived?: boolean; isMuted?: boolean; isPinned?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; }",
+ "{ id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }",
markdown:
- "## search\n\n`client.chats.search(accountIDs?: string[], cursor?: string, direction?: 'after' | 'before', inbox?: 'primary' | 'low-priority' | 'archive', includeMuted?: boolean, lastActivityAfter?: string, lastActivityBefore?: string, limit?: number, query?: string, scope?: 'titles' | 'participants', type?: 'single' | 'group' | 'any', unreadOnly?: boolean): { id: string; accountID: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; isArchived?: boolean; isMuted?: boolean; isPinned?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; }`\n\n**get** `/v1/chats/search`\n\nSearch chats by title, network, or participant names.\n\n### Parameters\n\n- `accountIDs?: string[]`\n Provide an array of account IDs to filter chats from specific messaging accounts only\n\n- `cursor?: string`\n Opaque pagination cursor; do not inspect. Use together with 'direction'.\n\n- `direction?: 'after' | 'before'`\n Pagination direction used with 'cursor': 'before' fetches older results, 'after' fetches newer results. Defaults to 'before' when only 'cursor' is provided.\n\n- `inbox?: 'primary' | 'low-priority' | 'archive'`\n Filter by inbox type: \"primary\" (non-archived, non-low-priority), \"low-priority\", or \"archive\". If not specified, shows all chats.\n\n- `includeMuted?: boolean`\n Include chats marked as Muted by the user, which are usually less important. Default: true. Set to false if the user wants a more refined search.\n\n- `lastActivityAfter?: string`\n Provide an ISO datetime string to only retrieve chats with last activity after this time\n\n- `lastActivityBefore?: string`\n Provide an ISO datetime string to only retrieve chats with last activity before this time\n\n- `limit?: number`\n Set the maximum number of chats to retrieve. Valid range: 1-200, default is 50\n\n- `query?: string`\n Literal token search (non-semantic). Use single words users type (e.g., \"dinner\"). When multiple words provided, ALL must match. Case-insensitive.\n\n- `scope?: 'titles' | 'participants'`\n Search scope: 'titles' matches title + network; 'participants' matches participant names.\n\n- `type?: 'single' | 'group' | 'any'`\n Specify the type of chats to retrieve: use \"single\" for direct messages, \"group\" for group chats, or \"any\" to get all types\n\n- `unreadOnly?: boolean`\n Set to true to only retrieve chats that have unread messages\n\n### Returns\n\n- `{ id: string; accountID: string; participants: { hasMore: boolean; items: object[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; isArchived?: boolean; isMuted?: boolean; isPinned?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; }`\n\n - `id: string`\n - `accountID: string`\n - `participants: { hasMore: boolean; items: { id: string; cannotMessage?: boolean; email?: string; fullName?: string; imgURL?: string; isSelf?: boolean; phoneNumber?: string; username?: string; }[]; total: number; }`\n - `title: string`\n - `type: 'single' | 'group'`\n - `unreadCount: number`\n - `isArchived?: boolean`\n - `isMuted?: boolean`\n - `isPinned?: boolean`\n - `lastActivity?: string`\n - `lastReadMessageSortKey?: string`\n - `localChatID?: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\n// Automatically fetches more pages as needed.\nfor await (const chat of client.chats.search()) {\n console.log(chat);\n}\n```",
+ "## search\n\n`client.chats.search(accountIDs?: string[], cursor?: string, direction?: 'after' | 'before', inbox?: 'primary' | 'low-priority' | 'archive', includeMuted?: boolean, lastActivityAfter?: string, lastActivityBefore?: string, limit?: number, query?: string, scope?: 'titles' | 'participants', type?: 'single' | 'group' | 'any', unreadOnly?: boolean): { id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }`\n\n**get** `/v1/chats/search`\n\nSearch chats by title, network, or participant names.\n\n### Parameters\n\n- `accountIDs?: string[]`\n Provide an array of account IDs to filter chats from specific messaging accounts only\n\n- `cursor?: string`\n Opaque pagination cursor; do not inspect. Use together with 'direction'.\n\n- `direction?: 'after' | 'before'`\n Pagination direction used with 'cursor': 'before' fetches older results, 'after' fetches newer results. Defaults to 'before' when only 'cursor' is provided.\n\n- `inbox?: 'primary' | 'low-priority' | 'archive'`\n Filter by inbox type: \"primary\" (non-archived, non-low-priority), \"low-priority\", or \"archive\". If not specified, shows all chats.\n\n- `includeMuted?: boolean`\n Include chats marked as Muted by the user, which are usually less important. Default: true. Set to false if the user wants a more refined search.\n\n- `lastActivityAfter?: string`\n Provide an ISO datetime string to only retrieve chats with last activity after this time\n\n- `lastActivityBefore?: string`\n Provide an ISO datetime string to only retrieve chats with last activity before this time\n\n- `limit?: number`\n Set the maximum number of chats to retrieve. Valid range: 1-200, default is 50\n\n- `query?: string`\n Literal token search (non-semantic). Use single words users type (e.g., \"dinner\"). When multiple words provided, ALL must match. Case-insensitive.\n\n- `scope?: 'titles' | 'participants'`\n Search scope: 'titles' matches title + network; 'participants' matches participant names.\n\n- `type?: 'single' | 'group' | 'any'`\n Specify the type of chats to retrieve: use \"single\" for direct messages, \"group\" for group chats, or \"any\" to get all types\n\n- `unreadOnly?: boolean`\n Set to true to only retrieve chats that have unread messages\n\n### Returns\n\n- `{ id: string; accountID: string; network: string; participants: { hasMore: boolean; items: object[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: { omitEmptyTimer?: boolean; timers?: number[]; types?: 'afterRead' | 'afterSend'[]; }; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: { acceptWithButton?: -2 | -1 | 0 | 1 | 2; acceptWithMessage?: -2 | -1 | 0 | 1 | 2; }; participantActions?: { ban?: -2 | -1 | 0 | 1 | 2; invite?: -2 | -1 | 0 | 1 | 2; kick?: -2 | -1 | 0 | 1 | 2; leave?: -2 | -1 | 0 | 1 | 2; revokeInvite?: -2 | -1 | 0 | 1 | 2; }; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: { avatar?: object; description?: object; disappearingTimer?: object; title?: object; }; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }; description?: string; draft?: { text: string; attachments?: object; }; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }; snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }; unreadMentionsCount?: number; }`\n\n - `id: string`\n - `accountID: string`\n - `network: string`\n - `participants: { hasMore: boolean; items: { id: string; cannotMessage?: boolean; email?: string; fullName?: string; imgURL?: string; isSelf?: boolean; phoneNumber?: string; username?: string; }[]; total: number; }`\n - `title: string`\n - `type: 'single' | 'group'`\n - `unreadCount: number`\n - `capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: { omitEmptyTimer?: boolean; timers?: number[]; types?: 'afterRead' | 'afterSend'[]; }; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: { acceptWithButton?: -2 | -1 | 0 | 1 | 2; acceptWithMessage?: -2 | -1 | 0 | 1 | 2; }; participantActions?: { ban?: -2 | -1 | 0 | 1 | 2; invite?: -2 | -1 | 0 | 1 | 2; kick?: -2 | -1 | 0 | 1 | 2; leave?: -2 | -1 | 0 | 1 | 2; revokeInvite?: -2 | -1 | 0 | 1 | 2; }; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: { avatar?: { level: -2 | -1 | 0 | 1 | 2; }; description?: { level: -2 | -1 | 0 | 1 | 2; }; disappearingTimer?: { level: -2 | -1 | 0 | 1 | 2; }; title?: { level: -2 | -1 | 0 | 1 | 2; }; }; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }`\n - `description?: string`\n - `draft?: { text: string; attachments?: object; }`\n - `imgURL?: string`\n - `isArchived?: boolean`\n - `isLowPriority?: boolean`\n - `isMarkedUnread?: boolean`\n - `isMuted?: boolean`\n - `isPinned?: boolean`\n - `isReadOnly?: boolean`\n - `lastActivity?: string`\n - `lastReadMessageSortKey?: string`\n - `localChatID?: string`\n - `messageExpirySeconds?: number`\n - `reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }`\n - `snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }`\n - `unreadMentionsCount?: number`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\n// Automatically fetches more pages as needed.\nfor await (const chat of client.chats.search()) {\n console.log(chat);\n}\n```",
perLanguage: {
typescript: {
method: 'client.chats.search',
@@ -478,7 +526,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
php: {
method: 'chats->search',
example:
- "chats->search(\n accountIDs: [\n 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc',\n 'local-telegram_ba_QFrb5lrLPhO3OT5MFBeTWv0x4BI',\n ],\n cursor: '1725489123456|c29tZUltc2dQYWdl',\n direction: 'before',\n inbox: 'primary',\n includeMuted: true,\n lastActivityAfter: new \\DateTimeImmutable('2019-12-27T18:11:19.117Z'),\n lastActivityBefore: new \\DateTimeImmutable('2019-12-27T18:11:19.117Z'),\n limit: 1,\n query: 'x',\n scope: 'titles',\n type: 'single',\n unreadOnly: true,\n);\n\nvar_dump($page);",
+ "chats->search(\n accountIDs: [\n 'matrix', 'discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'\n ],\n cursor: '1725489123456|c29tZUltc2dQYWdl',\n direction: 'before',\n inbox: 'primary',\n includeMuted: true,\n lastActivityAfter: new \\DateTimeImmutable('2019-12-27T18:11:19.117Z'),\n lastActivityBefore: new \\DateTimeImmutable('2019-12-27T18:11:19.117Z'),\n limit: 1,\n query: 'x',\n scope: 'titles',\n type: 'single',\n unreadOnly: true,\n);\n\nvar_dump($page);",
},
http: {
example:
@@ -497,7 +545,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
qualified: 'client.chats.archive',
params: ['chatID: string;', 'archived?: boolean;'],
markdown:
- "## archive\n\n`client.chats.archive(chatID: string, archived?: boolean): void`\n\n**post** `/v1/chats/{chatID}/archive`\n\nArchive or unarchive a chat. Set archived=true to move to archive, archived=false to move back to inbox\n\n### Parameters\n\n- `chatID: string`\n Unique identifier of the chat.\n\n- `archived?: boolean`\n True to archive, false to unarchive\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nawait client.chats.archive('!NCdzlIaMjZUmvmvyHU:beeper.com')\n```",
+ "## archive\n\n`client.chats.archive(chatID: string, archived?: boolean): void`\n\n**post** `/v1/chats/{chatID}/archive`\n\nArchive or unarchive a chat. Set archived=true to move to archive, archived=false to move back to inbox\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `archived?: boolean`\n True to archive, false to unarchive\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nawait client.chats.archive('!NCdzlIaMjZUmvmvyHU:beeper.com')\n```",
perLanguage: {
typescript: {
method: 'client.chats.archive',
@@ -530,6 +578,199 @@ const EMBEDDED_METHODS: MethodEntry[] = [
},
},
},
+ {
+ name: 'update',
+ endpoint: '/v1/chats/{chatID}',
+ httpMethod: 'patch',
+ summary: 'Update chat',
+ description:
+ 'Update supported chat fields. Non-empty draft objects are accepted only when the current draft is empty. Send draft=null to clear the draft before setting new draft text or attachments.',
+ stainlessPath: '(resource) chats > (method) update',
+ qualified: 'client.chats.update',
+ params: [
+ 'chatID: string;',
+ 'description?: string;',
+ 'draft?: { text: string; attachments?: object; };',
+ 'imgURL?: string;',
+ 'isArchived?: boolean;',
+ 'isLowPriority?: boolean;',
+ 'isMuted?: boolean;',
+ 'isPinned?: boolean;',
+ 'messageExpirySeconds?: number;',
+ 'title?: string;',
+ ],
+ response:
+ "{ id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }",
+ markdown:
+ "## update\n\n`client.chats.update(chatID: string, description?: string, draft?: { text: string; attachments?: object; }, imgURL?: string, isArchived?: boolean, isLowPriority?: boolean, isMuted?: boolean, isPinned?: boolean, messageExpirySeconds?: number, title?: string): { id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }`\n\n**patch** `/v1/chats/{chatID}`\n\nUpdate supported chat fields. Non-empty draft objects are accepted only when the current draft is empty. Send draft=null to clear the draft before setting new draft text or attachments.\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `description?: string`\n Group chat description/topic. Support depends on the chat account and chat permissions.\n\n- `draft?: { text: string; attachments?: object; }`\n Draft object to set or clear. Non-empty drafts are only accepted when the current draft is empty. Send draft=null to clear text and attachments together before setting a new draft.\n - `text: string`\n Draft text. Plain text and Markdown are converted to Matrix HTML with the same rules used by send and edit.\n - `attachments?: object`\n Draft attachments keyed by attachment ID. Each attachment must reference an uploadID returned by the upload file endpoint.\n\n- `imgURL?: string`\n Local filesystem path to a group chat avatar image. Support depends on the chat account and chat permissions.\n\n- `isArchived?: boolean`\n Archive or unarchive the chat.\n\n- `isLowPriority?: boolean`\n Mark or unmark the chat as low priority when supported by the account.\n\n- `isMuted?: boolean`\n Mute or unmute the chat.\n\n- `isPinned?: boolean`\n Pin or unpin the chat when supported by the account.\n\n- `messageExpirySeconds?: number`\n Disappearing-message timer in seconds, or null to clear when supported.\n\n- `title?: string`\n Custom chat title. Support depends on the chat account and chat permissions.\n\n### Returns\n\n- `{ id: string; accountID: string; network: string; participants: { hasMore: boolean; items: object[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: { omitEmptyTimer?: boolean; timers?: number[]; types?: 'afterRead' | 'afterSend'[]; }; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: { acceptWithButton?: -2 | -1 | 0 | 1 | 2; acceptWithMessage?: -2 | -1 | 0 | 1 | 2; }; participantActions?: { ban?: -2 | -1 | 0 | 1 | 2; invite?: -2 | -1 | 0 | 1 | 2; kick?: -2 | -1 | 0 | 1 | 2; leave?: -2 | -1 | 0 | 1 | 2; revokeInvite?: -2 | -1 | 0 | 1 | 2; }; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: { avatar?: object; description?: object; disappearingTimer?: object; title?: object; }; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }; description?: string; draft?: { text: string; attachments?: object; }; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }; snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }; unreadMentionsCount?: number; }`\n\n - `id: string`\n - `accountID: string`\n - `network: string`\n - `participants: { hasMore: boolean; items: { id: string; cannotMessage?: boolean; email?: string; fullName?: string; imgURL?: string; isSelf?: boolean; phoneNumber?: string; username?: string; }[]; total: number; }`\n - `title: string`\n - `type: 'single' | 'group'`\n - `unreadCount: number`\n - `capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: { omitEmptyTimer?: boolean; timers?: number[]; types?: 'afterRead' | 'afterSend'[]; }; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: { acceptWithButton?: -2 | -1 | 0 | 1 | 2; acceptWithMessage?: -2 | -1 | 0 | 1 | 2; }; participantActions?: { ban?: -2 | -1 | 0 | 1 | 2; invite?: -2 | -1 | 0 | 1 | 2; kick?: -2 | -1 | 0 | 1 | 2; leave?: -2 | -1 | 0 | 1 | 2; revokeInvite?: -2 | -1 | 0 | 1 | 2; }; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: { avatar?: { level: -2 | -1 | 0 | 1 | 2; }; description?: { level: -2 | -1 | 0 | 1 | 2; }; disappearingTimer?: { level: -2 | -1 | 0 | 1 | 2; }; title?: { level: -2 | -1 | 0 | 1 | 2; }; }; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }`\n - `description?: string`\n - `draft?: { text: string; attachments?: object; }`\n - `imgURL?: string`\n - `isArchived?: boolean`\n - `isLowPriority?: boolean`\n - `isMarkedUnread?: boolean`\n - `isMuted?: boolean`\n - `isPinned?: boolean`\n - `isReadOnly?: boolean`\n - `lastActivity?: string`\n - `lastReadMessageSortKey?: string`\n - `localChatID?: string`\n - `messageExpirySeconds?: number`\n - `reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }`\n - `snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }`\n - `unreadMentionsCount?: number`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst chat = await client.chats.update('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nconsole.log(chat);\n```",
+ perLanguage: {
+ typescript: {
+ method: 'client.chats.update',
+ example:
+ "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst chat = await client.chats.update('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nconsole.log(chat.id);",
+ },
+ python: {
+ method: 'chats.update',
+ example:
+ 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nchat = client.chats.update(\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n)\nprint(chat.id)',
+ },
+ go: {
+ method: 'client.Chats.Update',
+ example:
+ 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\tchat, err := client.Chats.Update(\n\t\tcontext.TODO(),\n\t\t"!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\tbeeperdesktopapi.ChatUpdateParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", chat.ID)\n}\n',
+ },
+ cli: {
+ method: 'chats update',
+ example:
+ "beeper-desktop-cli chats update \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com'",
+ },
+ php: {
+ method: 'chats->update',
+ example:
+ "chats->update(\n '!NCdzlIaMjZUmvmvyHU:beeper.com',\n description: 'description',\n draft: [\n 'text' => 'text',\n 'attachments' => [\n 'foo' => [\n 'uploadID' => 'uploadID',\n 'id' => 'id',\n 'duration' => 0,\n 'fileName' => 'fileName',\n 'mimeType' => 'mimeType',\n 'size' => ['height' => 0, 'width' => 0],\n 'type' => 'image',\n ],\n ],\n ],\n imgURL: 'imgURL',\n isArchived: true,\n isLowPriority: true,\n isMuted: true,\n isPinned: true,\n messageExpirySeconds: 0,\n title: 'title',\n);\n\nvar_dump($chat);",
+ },
+ http: {
+ example:
+ 'curl http://localhost:23373/v1/chats/$CHAT_ID \\\n -X PATCH \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN"',
+ },
+ },
+ },
+ {
+ name: 'mark_read',
+ endpoint: '/v1/chats/{chatID}/read',
+ httpMethod: 'post',
+ summary: 'Mark a chat as read',
+ description: 'Mark a chat as read, optionally through a specific message ID.',
+ stainlessPath: '(resource) chats > (method) mark_read',
+ qualified: 'client.chats.markRead',
+ params: ['chatID: string;', 'messageID?: string;'],
+ response:
+ "{ id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }",
+ markdown:
+ "## mark_read\n\n`client.chats.markRead(chatID: string, messageID?: string): { id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }`\n\n**post** `/v1/chats/{chatID}/read`\n\nMark a chat as read, optionally through a specific message ID.\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `messageID?: string`\n Optional message ID to mark read through.\n\n### Returns\n\n- `{ id: string; accountID: string; network: string; participants: { hasMore: boolean; items: object[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: { omitEmptyTimer?: boolean; timers?: number[]; types?: 'afterRead' | 'afterSend'[]; }; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: { acceptWithButton?: -2 | -1 | 0 | 1 | 2; acceptWithMessage?: -2 | -1 | 0 | 1 | 2; }; participantActions?: { ban?: -2 | -1 | 0 | 1 | 2; invite?: -2 | -1 | 0 | 1 | 2; kick?: -2 | -1 | 0 | 1 | 2; leave?: -2 | -1 | 0 | 1 | 2; revokeInvite?: -2 | -1 | 0 | 1 | 2; }; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: { avatar?: object; description?: object; disappearingTimer?: object; title?: object; }; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }; description?: string; draft?: { text: string; attachments?: object; }; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }; snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }; unreadMentionsCount?: number; }`\n\n - `id: string`\n - `accountID: string`\n - `network: string`\n - `participants: { hasMore: boolean; items: { id: string; cannotMessage?: boolean; email?: string; fullName?: string; imgURL?: string; isSelf?: boolean; phoneNumber?: string; username?: string; }[]; total: number; }`\n - `title: string`\n - `type: 'single' | 'group'`\n - `unreadCount: number`\n - `capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: { omitEmptyTimer?: boolean; timers?: number[]; types?: 'afterRead' | 'afterSend'[]; }; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: { acceptWithButton?: -2 | -1 | 0 | 1 | 2; acceptWithMessage?: -2 | -1 | 0 | 1 | 2; }; participantActions?: { ban?: -2 | -1 | 0 | 1 | 2; invite?: -2 | -1 | 0 | 1 | 2; kick?: -2 | -1 | 0 | 1 | 2; leave?: -2 | -1 | 0 | 1 | 2; revokeInvite?: -2 | -1 | 0 | 1 | 2; }; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: { avatar?: { level: -2 | -1 | 0 | 1 | 2; }; description?: { level: -2 | -1 | 0 | 1 | 2; }; disappearingTimer?: { level: -2 | -1 | 0 | 1 | 2; }; title?: { level: -2 | -1 | 0 | 1 | 2; }; }; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }`\n - `description?: string`\n - `draft?: { text: string; attachments?: object; }`\n - `imgURL?: string`\n - `isArchived?: boolean`\n - `isLowPriority?: boolean`\n - `isMarkedUnread?: boolean`\n - `isMuted?: boolean`\n - `isPinned?: boolean`\n - `isReadOnly?: boolean`\n - `lastActivity?: string`\n - `lastReadMessageSortKey?: string`\n - `localChatID?: string`\n - `messageExpirySeconds?: number`\n - `reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }`\n - `snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }`\n - `unreadMentionsCount?: number`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst chat = await client.chats.markRead('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nconsole.log(chat);\n```",
+ perLanguage: {
+ typescript: {
+ method: 'client.chats.markRead',
+ example:
+ "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst chat = await client.chats.markRead('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nconsole.log(chat.id);",
+ },
+ python: {
+ method: 'chats.mark_read',
+ example:
+ 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nchat = client.chats.mark_read(\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n)\nprint(chat.id)',
+ },
+ go: {
+ method: 'client.Chats.MarkRead',
+ example:
+ 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\tchat, err := client.Chats.MarkRead(\n\t\tcontext.TODO(),\n\t\t"!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\tbeeperdesktopapi.ChatMarkReadParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", chat.ID)\n}\n',
+ },
+ cli: {
+ method: 'chats mark_read',
+ example:
+ "beeper-desktop-cli chats mark-read \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com'",
+ },
+ php: {
+ method: 'chats->markRead',
+ example:
+ "chats->markRead(\n '!NCdzlIaMjZUmvmvyHU:beeper.com', messageID: '1343993'\n);\n\nvar_dump($chat);",
+ },
+ http: {
+ example:
+ 'curl http://localhost:23373/v1/chats/$CHAT_ID/read \\\n -X POST \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN"',
+ },
+ },
+ },
+ {
+ name: 'mark_unread',
+ endpoint: '/v1/chats/{chatID}/unread',
+ httpMethod: 'post',
+ summary: 'Mark a chat as unread',
+ description: 'Mark a chat as unread, optionally from a specific message ID.',
+ stainlessPath: '(resource) chats > (method) mark_unread',
+ qualified: 'client.chats.markUnread',
+ params: ['chatID: string;', 'messageID?: string;'],
+ response:
+ "{ id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }",
+ markdown:
+ "## mark_unread\n\n`client.chats.markUnread(chatID: string, messageID?: string): { id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }`\n\n**post** `/v1/chats/{chatID}/unread`\n\nMark a chat as unread, optionally from a specific message ID.\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `messageID?: string`\n Optional message ID to mark unread from.\n\n### Returns\n\n- `{ id: string; accountID: string; network: string; participants: { hasMore: boolean; items: object[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: { omitEmptyTimer?: boolean; timers?: number[]; types?: 'afterRead' | 'afterSend'[]; }; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: { acceptWithButton?: -2 | -1 | 0 | 1 | 2; acceptWithMessage?: -2 | -1 | 0 | 1 | 2; }; participantActions?: { ban?: -2 | -1 | 0 | 1 | 2; invite?: -2 | -1 | 0 | 1 | 2; kick?: -2 | -1 | 0 | 1 | 2; leave?: -2 | -1 | 0 | 1 | 2; revokeInvite?: -2 | -1 | 0 | 1 | 2; }; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: { avatar?: object; description?: object; disappearingTimer?: object; title?: object; }; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }; description?: string; draft?: { text: string; attachments?: object; }; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }; snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }; unreadMentionsCount?: number; }`\n\n - `id: string`\n - `accountID: string`\n - `network: string`\n - `participants: { hasMore: boolean; items: { id: string; cannotMessage?: boolean; email?: string; fullName?: string; imgURL?: string; isSelf?: boolean; phoneNumber?: string; username?: string; }[]; total: number; }`\n - `title: string`\n - `type: 'single' | 'group'`\n - `unreadCount: number`\n - `capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: { omitEmptyTimer?: boolean; timers?: number[]; types?: 'afterRead' | 'afterSend'[]; }; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: { acceptWithButton?: -2 | -1 | 0 | 1 | 2; acceptWithMessage?: -2 | -1 | 0 | 1 | 2; }; participantActions?: { ban?: -2 | -1 | 0 | 1 | 2; invite?: -2 | -1 | 0 | 1 | 2; kick?: -2 | -1 | 0 | 1 | 2; leave?: -2 | -1 | 0 | 1 | 2; revokeInvite?: -2 | -1 | 0 | 1 | 2; }; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: { avatar?: { level: -2 | -1 | 0 | 1 | 2; }; description?: { level: -2 | -1 | 0 | 1 | 2; }; disappearingTimer?: { level: -2 | -1 | 0 | 1 | 2; }; title?: { level: -2 | -1 | 0 | 1 | 2; }; }; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }`\n - `description?: string`\n - `draft?: { text: string; attachments?: object; }`\n - `imgURL?: string`\n - `isArchived?: boolean`\n - `isLowPriority?: boolean`\n - `isMarkedUnread?: boolean`\n - `isMuted?: boolean`\n - `isPinned?: boolean`\n - `isReadOnly?: boolean`\n - `lastActivity?: string`\n - `lastReadMessageSortKey?: string`\n - `localChatID?: string`\n - `messageExpirySeconds?: number`\n - `reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }`\n - `snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }`\n - `unreadMentionsCount?: number`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst chat = await client.chats.markUnread('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nconsole.log(chat);\n```",
+ perLanguage: {
+ typescript: {
+ method: 'client.chats.markUnread',
+ example:
+ "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst chat = await client.chats.markUnread('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nconsole.log(chat.id);",
+ },
+ python: {
+ method: 'chats.mark_unread',
+ example:
+ 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nchat = client.chats.mark_unread(\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n)\nprint(chat.id)',
+ },
+ go: {
+ method: 'client.Chats.MarkUnread',
+ example:
+ 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\tchat, err := client.Chats.MarkUnread(\n\t\tcontext.TODO(),\n\t\t"!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\tbeeperdesktopapi.ChatMarkUnreadParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", chat.ID)\n}\n',
+ },
+ cli: {
+ method: 'chats mark_unread',
+ example:
+ "beeper-desktop-cli chats mark-unread \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com'",
+ },
+ php: {
+ method: 'chats->markUnread',
+ example:
+ "chats->markUnread(\n '!NCdzlIaMjZUmvmvyHU:beeper.com', messageID: '1343993'\n);\n\nvar_dump($chat);",
+ },
+ http: {
+ example:
+ 'curl http://localhost:23373/v1/chats/$CHAT_ID/unread \\\n -X POST \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN"',
+ },
+ },
+ },
+ {
+ name: 'notify_anyway',
+ endpoint: '/v1/chats/{chatID}/notify-anyway',
+ httpMethod: 'post',
+ summary: 'Notify anyway',
+ description:
+ 'Force a delivery notification when supported by the underlying network. Currently intended for iMessage on macOS; unsupported networks return an error.',
+ stainlessPath: '(resource) chats > (method) notify_anyway',
+ qualified: 'client.chats.notifyAnyway',
+ params: ['chatID: string;'],
+ response:
+ "{ id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }",
+ markdown:
+ "## notify_anyway\n\n`client.chats.notifyAnyway(chatID: string): { id: string; accountID: string; network: string; participants: object; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: object; description?: string; draft?: object; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: object; snooze?: object; unreadMentionsCount?: number; }`\n\n**post** `/v1/chats/{chatID}/notify-anyway`\n\nForce a delivery notification when supported by the underlying network. Currently intended for iMessage on macOS; unsupported networks return an error.\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n### Returns\n\n- `{ id: string; accountID: string; network: string; participants: { hasMore: boolean; items: object[]; total: number; }; title: string; type: 'single' | 'group'; unreadCount: number; capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: { omitEmptyTimer?: boolean; timers?: number[]; types?: 'afterRead' | 'afterSend'[]; }; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: { acceptWithButton?: -2 | -1 | 0 | 1 | 2; acceptWithMessage?: -2 | -1 | 0 | 1 | 2; }; participantActions?: { ban?: -2 | -1 | 0 | 1 | 2; invite?: -2 | -1 | 0 | 1 | 2; kick?: -2 | -1 | 0 | 1 | 2; leave?: -2 | -1 | 0 | 1 | 2; revokeInvite?: -2 | -1 | 0 | 1 | 2; }; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: { avatar?: object; description?: object; disappearingTimer?: object; title?: object; }; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }; description?: string; draft?: { text: string; attachments?: object; }; imgURL?: string; isArchived?: boolean; isLowPriority?: boolean; isMarkedUnread?: boolean; isMuted?: boolean; isPinned?: boolean; isReadOnly?: boolean; lastActivity?: string; lastReadMessageSortKey?: string; localChatID?: string; messageExpirySeconds?: number; reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }; snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }; unreadMentionsCount?: number; }`\n\n - `id: string`\n - `accountID: string`\n - `network: string`\n - `participants: { hasMore: boolean; items: { id: string; cannotMessage?: boolean; email?: string; fullName?: string; imgURL?: string; isSelf?: boolean; phoneNumber?: string; username?: string; }[]; total: number; }`\n - `title: string`\n - `type: 'single' | 'group'`\n - `unreadCount: number`\n - `capabilities?: { allowedReactions?: string[]; archive?: boolean; attachments?: object; customEmojiReactions?: boolean; delete?: -2 | -1 | 0 | 1 | 2; deleteChat?: boolean; deleteChatForEveryone?: boolean; deleteForMe?: boolean; deleteMaxAge?: number; disappearingTimer?: { omitEmptyTimer?: boolean; timers?: number[]; types?: 'afterRead' | 'afterSend'[]; }; edit?: -2 | -1 | 0 | 1 | 2; editMaxAge?: number; editMaxCount?: number; formatting?: object; locationMessage?: -2 | -1 | 0 | 1 | 2; markAsUnread?: boolean; maxTextLength?: number; messageRequest?: { acceptWithButton?: -2 | -1 | 0 | 1 | 2; acceptWithMessage?: -2 | -1 | 0 | 1 | 2; }; participantActions?: { ban?: -2 | -1 | 0 | 1 | 2; invite?: -2 | -1 | 0 | 1 | 2; kick?: -2 | -1 | 0 | 1 | 2; leave?: -2 | -1 | 0 | 1 | 2; revokeInvite?: -2 | -1 | 0 | 1 | 2; }; poll?: -2 | -1 | 0 | 1 | 2; reaction?: -2 | -1 | 0 | 1 | 2; reactionCount?: number; readReceipts?: boolean; reply?: -2 | -1 | 0 | 1 | 2; state?: { avatar?: { level: -2 | -1 | 0 | 1 | 2; }; description?: { level: -2 | -1 | 0 | 1 | 2; }; disappearingTimer?: { level: -2 | -1 | 0 | 1 | 2; }; title?: { level: -2 | -1 | 0 | 1 | 2; }; }; thread?: -2 | -1 | 0 | 1 | 2; typingNotifications?: boolean; }`\n - `description?: string`\n - `draft?: { text: string; attachments?: object; }`\n - `imgURL?: string`\n - `isArchived?: boolean`\n - `isLowPriority?: boolean`\n - `isMarkedUnread?: boolean`\n - `isMuted?: boolean`\n - `isPinned?: boolean`\n - `isReadOnly?: boolean`\n - `lastActivity?: string`\n - `lastReadMessageSortKey?: string`\n - `localChatID?: string`\n - `messageExpirySeconds?: number`\n - `reminder?: { dismissOnIncomingMessage?: boolean; remindAt?: string; }`\n - `snooze?: { snoozeUntil?: string; userSnoozedAt?: string; }`\n - `unreadMentionsCount?: number`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst chat = await client.chats.notifyAnyway('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nconsole.log(chat);\n```",
+ perLanguage: {
+ typescript: {
+ method: 'client.chats.notifyAnyway',
+ example:
+ "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst chat = await client.chats.notifyAnyway('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nconsole.log(chat.id);",
+ },
+ python: {
+ method: 'chats.notify_anyway',
+ example:
+ 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nchat = client.chats.notify_anyway(\n "!NCdzlIaMjZUmvmvyHU:beeper.com",\n)\nprint(chat.id)',
+ },
+ go: {
+ method: 'client.Chats.NotifyAnyway',
+ example:
+ 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\tchat, err := client.Chats.NotifyAnyway(\n\t\tcontext.TODO(),\n\t\t"!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\tbeeperdesktopapi.ChatNotifyAnywayParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", chat.ID)\n}\n',
+ },
+ cli: {
+ method: 'chats notify_anyway',
+ example:
+ "beeper-desktop-cli chats notify-anyway \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com'",
+ },
+ php: {
+ method: 'chats->notifyAnyway',
+ example:
+ "chats->notifyAnyway('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nvar_dump($chat);",
+ },
+ http: {
+ example:
+ 'curl http://localhost:23373/v1/chats/$CHAT_ID/notify-anyway \\\n -X POST \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN"',
+ },
+ },
+ },
{
name: 'create',
endpoint: '/v1/chats/{chatID}/reminders',
@@ -538,38 +779,38 @@ const EMBEDDED_METHODS: MethodEntry[] = [
description: 'Set a reminder for a chat at a specific time',
stainlessPath: '(resource) chats.reminders > (method) create',
qualified: 'client.chats.reminders.create',
- params: ['chatID: string;', 'reminder: { remindAtMs: number; dismissOnIncomingMessage?: boolean; };'],
+ params: ['chatID: string;', 'reminder: { remindAt: string; dismissOnIncomingMessage?: boolean; };'],
markdown:
- "## create\n\n`client.chats.reminders.create(chatID: string, reminder: { remindAtMs: number; dismissOnIncomingMessage?: boolean; }): void`\n\n**post** `/v1/chats/{chatID}/reminders`\n\nSet a reminder for a chat at a specific time\n\n### Parameters\n\n- `chatID: string`\n Unique identifier of the chat.\n\n- `reminder: { remindAtMs: number; dismissOnIncomingMessage?: boolean; }`\n Reminder configuration\n - `remindAtMs: number`\n Unix timestamp in milliseconds when reminder should trigger\n - `dismissOnIncomingMessage?: boolean`\n Cancel reminder if someone messages in the chat\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nawait client.chats.reminders.create('!NCdzlIaMjZUmvmvyHU:beeper.com', { reminder: { remindAtMs: 0 } })\n```",
+ "## create\n\n`client.chats.reminders.create(chatID: string, reminder: { remindAt: string; dismissOnIncomingMessage?: boolean; }): void`\n\n**post** `/v1/chats/{chatID}/reminders`\n\nSet a reminder for a chat at a specific time\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `reminder: { remindAt: string; dismissOnIncomingMessage?: boolean; }`\n Reminder configuration\n - `remindAt: string`\n Timestamp when the reminder should trigger.\n - `dismissOnIncomingMessage?: boolean`\n Cancel reminder if someone messages in the chat\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nawait client.chats.reminders.create('!NCdzlIaMjZUmvmvyHU:beeper.com', { reminder: { remindAt: '2025-08-31T23:30:12.520Z' } })\n```",
perLanguage: {
typescript: {
method: 'client.chats.reminders.create',
example:
- "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nawait client.chats.reminders.create('!NCdzlIaMjZUmvmvyHU:beeper.com', {\n reminder: { remindAtMs: 0 },\n});",
+ "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nawait client.chats.reminders.create('!NCdzlIaMjZUmvmvyHU:beeper.com', {\n reminder: { remindAt: '2025-08-31T23:30:12.520Z' },\n});",
},
python: {
method: 'chats.reminders.create',
example:
- 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nclient.chats.reminders.create(\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n reminder={\n "remind_at_ms": 0\n },\n)',
+ 'import os\nfrom datetime import datetime\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nclient.chats.reminders.create(\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n reminder={\n "remind_at": datetime.fromisoformat("2025-08-31T23:30:12.520")\n },\n)',
},
go: {
method: 'client.Chats.Reminders.New',
example:
- 'package main\n\nimport (\n\t"context"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\terr := client.Chats.Reminders.New(\n\t\tcontext.TODO(),\n\t\t"!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\tbeeperdesktopapi.ChatReminderNewParams{\n\t\t\tReminder: beeperdesktopapi.ChatReminderNewParamsReminder{\n\t\t\t\tRemindAtMs: 0,\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n',
+ 'package main\n\nimport (\n\t"context"\n\t"time"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\terr := client.Chats.Reminders.New(\n\t\tcontext.TODO(),\n\t\t"!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\tbeeperdesktopapi.ChatReminderNewParams{\n\t\t\tReminder: beeperdesktopapi.ChatReminderNewParamsReminder{\n\t\t\t\tRemindAt: time.Now(),\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n',
},
cli: {
method: 'reminders create',
example:
- "beeper-desktop-cli chats:reminders create \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com' \\\n --reminder '{remindAtMs: 0}'",
+ "beeper-desktop-cli chats:reminders create \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com' \\\n --reminder \"{remindAt: '2025-08-31T23:30:12.520Z'}\"",
},
php: {
method: 'chats->reminders->create',
example:
- "chats->reminders->create(\n '!NCdzlIaMjZUmvmvyHU:beeper.com',\n reminder: ['remindAtMs' => 0, 'dismissOnIncomingMessage' => true],\n);\n\nvar_dump($result);",
+ "chats->reminders->create(\n '!NCdzlIaMjZUmvmvyHU:beeper.com',\n reminder: [\n 'remindAt' => new \\DateTimeImmutable('2025-08-31T23:30:12.520Z'),\n 'dismissOnIncomingMessage' => true,\n ],\n);\n\nvar_dump($result);",
},
http: {
example:
- 'curl http://localhost:23373/v1/chats/$CHAT_ID/reminders \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN" \\\n -d \'{\n "reminder": {\n "remindAtMs": 0\n }\n }\'',
+ 'curl http://localhost:23373/v1/chats/$CHAT_ID/reminders \\\n -H \'Content-Type: application/json\' \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN" \\\n -d \'{\n "reminder": {\n "remindAt": "2025-08-31T23:30:12.520Z"\n }\n }\'',
},
},
},
@@ -583,7 +824,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
qualified: 'client.chats.reminders.delete',
params: ['chatID: string;'],
markdown:
- "## delete\n\n`client.chats.reminders.delete(chatID: string): void`\n\n**delete** `/v1/chats/{chatID}/reminders`\n\nClear an existing reminder from a chat\n\n### Parameters\n\n- `chatID: string`\n Unique identifier of the chat.\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nawait client.chats.reminders.delete('!NCdzlIaMjZUmvmvyHU:beeper.com')\n```",
+ "## delete\n\n`client.chats.reminders.delete(chatID: string): void`\n\n**delete** `/v1/chats/{chatID}/reminders`\n\nClear an existing reminder from a chat\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nawait client.chats.reminders.delete('!NCdzlIaMjZUmvmvyHU:beeper.com')\n```",
perLanguage: {
typescript: {
method: 'client.chats.reminders.delete',
@@ -628,32 +869,32 @@ const EMBEDDED_METHODS: MethodEntry[] = [
response:
'{ chatID: string; messageID: string; reactionKey: string; success: true; transactionID: string; }',
markdown:
- "## add\n\n`client.chats.messages.reactions.add(chatID: string, messageID: string, reactionKey: string, transactionID?: string): { chatID: string; messageID: string; reactionKey: string; success: true; transactionID: string; }`\n\n**post** `/v1/chats/{chatID}/messages/{messageID}/reactions`\n\nAdd a reaction to an existing message.\n\n### Parameters\n\n- `chatID: string`\n Unique identifier of the chat.\n\n- `messageID: string`\n\n- `reactionKey: string`\n Reaction key to add (emoji, shortcode, or custom emoji key)\n\n- `transactionID?: string`\n Optional transaction ID for deduplication and local echo tracking\n\n### Returns\n\n- `{ chatID: string; messageID: string; reactionKey: string; success: true; transactionID: string; }`\n\n - `chatID: string`\n - `messageID: string`\n - `reactionKey: string`\n - `success: true`\n - `transactionID: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.chats.messages.reactions.add('messageID', { chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com', reactionKey: 'x' });\n\nconsole.log(response);\n```",
+ "## add\n\n`client.chats.messages.reactions.add(chatID: string, messageID: string, reactionKey: string, transactionID?: string): { chatID: string; messageID: string; reactionKey: string; success: true; transactionID: string; }`\n\n**post** `/v1/chats/{chatID}/messages/{messageID}/reactions`\n\nAdd a reaction to an existing message.\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `messageID: string`\n Message ID.\n\n- `reactionKey: string`\n Reaction key to add (emoji, shortcode, or custom emoji key)\n\n- `transactionID?: string`\n Optional transaction ID for deduplication and send tracking\n\n### Returns\n\n- `{ chatID: string; messageID: string; reactionKey: string; success: true; transactionID: string; }`\n\n - `chatID: string`\n - `messageID: string`\n - `reactionKey: string`\n - `success: true`\n - `transactionID: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.chats.messages.reactions.add('1343993', { chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com', reactionKey: 'x' });\n\nconsole.log(response);\n```",
perLanguage: {
typescript: {
method: 'client.chats.messages.reactions.add',
example:
- "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst response = await client.chats.messages.reactions.add('messageID', {\n chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',\n reactionKey: 'x',\n});\n\nconsole.log(response.chatID);",
+ "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst response = await client.chats.messages.reactions.add('1343993', {\n chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',\n reactionKey: 'x',\n});\n\nconsole.log(response.chatID);",
},
python: {
method: 'chats.messages.reactions.add',
example:
- 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nresponse = client.chats.messages.reactions.add(\n message_id="messageID",\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n reaction_key="x",\n)\nprint(response.chat_id)',
+ 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nresponse = client.chats.messages.reactions.add(\n message_id="1343993",\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n reaction_key="x",\n)\nprint(response.chat_id)',
},
go: {
method: 'client.Chats.Messages.Reactions.Add',
example:
- 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\tresponse, err := client.Chats.Messages.Reactions.Add(\n\t\tcontext.TODO(),\n\t\t"messageID",\n\t\tbeeperdesktopapi.ChatMessageReactionAddParams{\n\t\t\tChatID: "!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\t\tReactionKey: "x",\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", response.ChatID)\n}\n',
+ 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\tresponse, err := client.Chats.Messages.Reactions.Add(\n\t\tcontext.TODO(),\n\t\t"1343993",\n\t\tbeeperdesktopapi.ChatMessageReactionAddParams{\n\t\t\tChatID: "!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\t\tReactionKey: "x",\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", response.ChatID)\n}\n',
},
cli: {
method: 'reactions add',
example:
- "beeper-desktop-cli chats:messages:reactions add \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com' \\\n --message-id messageID \\\n --reaction-key x",
+ "beeper-desktop-cli chats:messages:reactions add \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com' \\\n --message-id 1343993 \\\n --reaction-key x",
},
php: {
method: 'chats->messages->reactions->add',
example:
- "chats->messages->reactions->add(\n 'messageID',\n chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',\n reactionKey: 'x',\n transactionID: 'transactionID',\n);\n\nvar_dump($response);",
+ "chats->messages->reactions->add(\n '1343993',\n chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',\n reactionKey: 'x',\n transactionID: 'transactionID',\n);\n\nvar_dump($response);",
},
http: {
example:
@@ -663,7 +904,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
},
{
name: 'delete',
- endpoint: '/v1/chats/{chatID}/messages/{messageID}/reactions',
+ endpoint: '/v1/chats/{chatID}/messages/{messageID}/reactions/{reactionKey}',
httpMethod: 'delete',
summary: 'Remove a reaction',
description: 'Remove the reaction added by the authenticated user from an existing message.',
@@ -672,36 +913,36 @@ const EMBEDDED_METHODS: MethodEntry[] = [
params: ['chatID: string;', 'messageID: string;', 'reactionKey: string;'],
response: '{ chatID: string; messageID: string; reactionKey: string; success: true; }',
markdown:
- "## delete\n\n`client.chats.messages.reactions.delete(chatID: string, messageID: string, reactionKey: string): { chatID: string; messageID: string; reactionKey: string; success: true; }`\n\n**delete** `/v1/chats/{chatID}/messages/{messageID}/reactions`\n\nRemove the reaction added by the authenticated user from an existing message.\n\n### Parameters\n\n- `chatID: string`\n Unique identifier of the chat.\n\n- `messageID: string`\n\n- `reactionKey: string`\n Reaction key to remove\n\n### Returns\n\n- `{ chatID: string; messageID: string; reactionKey: string; success: true; }`\n\n - `chatID: string`\n - `messageID: string`\n - `reactionKey: string`\n - `success: true`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst reaction = await client.chats.messages.reactions.delete('messageID', { chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com', reactionKey: 'x' });\n\nconsole.log(reaction);\n```",
+ "## delete\n\n`client.chats.messages.reactions.delete(chatID: string, messageID: string, reactionKey: string): { chatID: string; messageID: string; reactionKey: string; success: true; }`\n\n**delete** `/v1/chats/{chatID}/messages/{messageID}/reactions/{reactionKey}`\n\nRemove the reaction added by the authenticated user from an existing message.\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `messageID: string`\n Message ID.\n\n- `reactionKey: string`\n Reaction key to remove (emoji, shortcode, or custom emoji key)\n\n### Returns\n\n- `{ chatID: string; messageID: string; reactionKey: string; success: true; }`\n\n - `chatID: string`\n - `messageID: string`\n - `reactionKey: string`\n - `success: true`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst reaction = await client.chats.messages.reactions.delete('x', { chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com', messageID: '1343993' });\n\nconsole.log(reaction);\n```",
perLanguage: {
typescript: {
method: 'client.chats.messages.reactions.delete',
example:
- "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst reaction = await client.chats.messages.reactions.delete('messageID', {\n chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',\n reactionKey: 'x',\n});\n\nconsole.log(reaction.chatID);",
+ "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst reaction = await client.chats.messages.reactions.delete('x', {\n chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',\n messageID: '1343993',\n});\n\nconsole.log(reaction.chatID);",
},
python: {
method: 'chats.messages.reactions.delete',
example:
- 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nreaction = client.chats.messages.reactions.delete(\n message_id="messageID",\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n reaction_key="x",\n)\nprint(reaction.chat_id)',
+ 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nreaction = client.chats.messages.reactions.delete(\n reaction_key="x",\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n message_id="1343993",\n)\nprint(reaction.chat_id)',
},
go: {
method: 'client.Chats.Messages.Reactions.Delete',
example:
- 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\treaction, err := client.Chats.Messages.Reactions.Delete(\n\t\tcontext.TODO(),\n\t\t"messageID",\n\t\tbeeperdesktopapi.ChatMessageReactionDeleteParams{\n\t\t\tChatID: "!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\t\tReactionKey: "x",\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", reaction.ChatID)\n}\n',
+ 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\treaction, err := client.Chats.Messages.Reactions.Delete(\n\t\tcontext.TODO(),\n\t\t"x",\n\t\tbeeperdesktopapi.ChatMessageReactionDeleteParams{\n\t\t\tChatID: "!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\t\tMessageID: "1343993",\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", reaction.ChatID)\n}\n',
},
cli: {
method: 'reactions delete',
example:
- "beeper-desktop-cli chats:messages:reactions delete \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com' \\\n --message-id messageID \\\n --reaction-key x",
+ "beeper-desktop-cli chats:messages:reactions delete \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com' \\\n --message-id 1343993 \\\n --reaction-key x",
},
php: {
method: 'chats->messages->reactions->delete',
example:
- "chats->messages->reactions->delete(\n 'messageID', chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com', reactionKey: 'x'\n);\n\nvar_dump($reaction);",
+ "chats->messages->reactions->delete(\n 'x', chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com', messageID: '1343993'\n);\n\nvar_dump($reaction);",
},
http: {
example:
- 'curl http://localhost:23373/v1/chats/$CHAT_ID/messages/$MESSAGE_ID/reactions \\\n -X DELETE \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN"',
+ 'curl http://localhost:23373/v1/chats/$CHAT_ID/messages/$MESSAGE_ID/reactions/$REACTION_KEY \\\n -X DELETE \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN"',
},
},
},
@@ -729,9 +970,9 @@ const EMBEDDED_METHODS: MethodEntry[] = [
'sender?: string;',
],
response:
- "{ id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: object; srcURL?: string; }[]; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]; senderName?: string; text?: string; type?: string; }",
+ "{ id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: object; srcURL?: string; transcription?: object; }[]; editedTimestamp?: string; isDeleted?: boolean; isHidden?: boolean; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; links?: { title: string; url: string; favicon?: string; img?: string; imgSize?: { height?: number; width?: number; }; originalURL?: string; summary?: string; }[]; mentions?: string[]; reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]; seen?: boolean | string | object; senderName?: string; sendStatus?: { status: 'SUCCESS' | 'PENDING' | 'FAIL_RETRIABLE' | 'FAIL_PERMANENT'; timestamp: string; deliveredToUsers?: string[]; internalError?: string; message?: string; reason?: string; }; text?: string; type?: string; }",
markdown:
- "## search\n\n`client.messages.search(accountIDs?: string[], chatIDs?: string[], chatType?: 'group' | 'single', cursor?: string, dateAfter?: string, dateBefore?: string, direction?: 'after' | 'before', excludeLowPriority?: boolean, includeMuted?: boolean, limit?: number, mediaTypes?: 'any' | 'video' | 'image' | 'link' | 'file'[], query?: string, sender?: string): { id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: attachment[]; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; reactions?: reaction[]; senderName?: string; text?: string; type?: string; }`\n\n**get** `/v1/messages/search`\n\nSearch messages across chats.\n\n### Parameters\n\n- `accountIDs?: string[]`\n Limit search to specific account IDs.\n\n- `chatIDs?: string[]`\n Limit search to specific chat IDs.\n\n- `chatType?: 'group' | 'single'`\n Filter by chat type: 'group' for group chats, 'single' for 1:1 chats.\n\n- `cursor?: string`\n Opaque pagination cursor; do not inspect. Use together with 'direction'.\n\n- `dateAfter?: string`\n Only include messages with timestamp strictly after this ISO 8601 datetime (e.g., '2024-07-01T00:00:00Z' or '2024-07-01T00:00:00+02:00').\n\n- `dateBefore?: string`\n Only include messages with timestamp strictly before this ISO 8601 datetime (e.g., '2024-07-31T23:59:59Z' or '2024-07-31T23:59:59+02:00').\n\n- `direction?: 'after' | 'before'`\n Pagination direction used with 'cursor': 'before' fetches older results, 'after' fetches newer results. Defaults to 'before' when only 'cursor' is provided.\n\n- `excludeLowPriority?: boolean`\n Exclude messages marked Low Priority by the user. Default: true. Set to false to include all.\n\n- `includeMuted?: boolean`\n Include messages in chats marked as Muted by the user, which are usually less important. Default: true. Set to false if the user wants a more refined search.\n\n- `limit?: number`\n Maximum number of messages to return.\n\n- `mediaTypes?: 'any' | 'video' | 'image' | 'link' | 'file'[]`\n Filter messages by media types. Use ['any'] for any media type, or specify exact types like ['video', 'image']. Omit for no media filtering.\n\n- `query?: string`\n Literal word search (non-semantic). Finds messages containing these EXACT words in any order. Use single words users actually type, not concepts or phrases. Example: use \"dinner\" not \"dinner plans\", use \"sick\" not \"health issues\". If omitted, returns results filtered only by other parameters.\n\n- `sender?: string`\n Filter by sender: 'me' (messages sent by the authenticated user), 'others' (messages sent by others), or a specific user ID string (user.id).\n\n### Returns\n\n- `{ id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: object; srcURL?: string; }[]; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]; senderName?: string; text?: string; type?: string; }`\n\n - `id: string`\n - `accountID: string`\n - `chatID: string`\n - `senderID: string`\n - `sortKey: string`\n - `timestamp: string`\n - `attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: { height?: number; width?: number; }; srcURL?: string; }[]`\n - `isSender?: boolean`\n - `isUnread?: boolean`\n - `linkedMessageID?: string`\n - `reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]`\n - `senderName?: string`\n - `text?: string`\n - `type?: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\n// Automatically fetches more pages as needed.\nfor await (const message of client.messages.search()) {\n console.log(message);\n}\n```",
+ "## search\n\n`client.messages.search(accountIDs?: string[], chatIDs?: string[], chatType?: 'group' | 'single', cursor?: string, dateAfter?: string, dateBefore?: string, direction?: 'after' | 'before', excludeLowPriority?: boolean, includeMuted?: boolean, limit?: number, mediaTypes?: 'any' | 'video' | 'image' | 'link' | 'file'[], query?: string, sender?: string): { id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: attachment[]; editedTimestamp?: string; isDeleted?: boolean; isHidden?: boolean; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; links?: object[]; mentions?: string[]; reactions?: reaction[]; seen?: boolean | string | object; senderName?: string; sendStatus?: object; text?: string; type?: string; }`\n\n**get** `/v1/messages/search`\n\nSearch messages across chats.\n\n### Parameters\n\n- `accountIDs?: string[]`\n Limit search to specific account IDs.\n\n- `chatIDs?: string[]`\n Limit search to specific chat IDs.\n\n- `chatType?: 'group' | 'single'`\n Filter by chat type: 'group' for group chats, 'single' for 1:1 chats.\n\n- `cursor?: string`\n Opaque pagination cursor; do not inspect. Use together with 'direction'.\n\n- `dateAfter?: string`\n Only include messages with timestamp strictly after this ISO 8601 datetime (e.g., '2024-07-01T00:00:00Z' or '2024-07-01T00:00:00+02:00').\n\n- `dateBefore?: string`\n Only include messages with timestamp strictly before this ISO 8601 datetime (e.g., '2024-07-31T23:59:59Z' or '2024-07-31T23:59:59+02:00').\n\n- `direction?: 'after' | 'before'`\n Pagination direction used with 'cursor': 'before' fetches older results, 'after' fetches newer results. Defaults to 'before' when only 'cursor' is provided.\n\n- `excludeLowPriority?: boolean`\n Exclude messages marked Low Priority by the user. Default: true. Set to false to include all.\n\n- `includeMuted?: boolean`\n Include messages in chats marked as Muted by the user, which are usually less important. Default: true. Set to false if the user wants a more refined search.\n\n- `limit?: number`\n Maximum number of messages to return.\n\n- `mediaTypes?: 'any' | 'video' | 'image' | 'link' | 'file'[]`\n Filter messages by media types. Use ['any'] for any media type, or specify exact types like ['video', 'image']. Omit for no media filtering.\n\n- `query?: string`\n Literal word search (non-semantic). Finds messages containing these EXACT words in any order. Use single words users actually type, not concepts or phrases. Example: use \"dinner\" not \"dinner plans\", use \"sick\" not \"health issues\". If omitted, returns results filtered only by other parameters.\n\n- `sender?: string`\n Filter by sender: 'me' (messages sent by the authenticated user), 'others' (messages sent by others), or a specific user ID string (user.id).\n\n### Returns\n\n- `{ id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: object; srcURL?: string; transcription?: object; }[]; editedTimestamp?: string; isDeleted?: boolean; isHidden?: boolean; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; links?: { title: string; url: string; favicon?: string; img?: string; imgSize?: { height?: number; width?: number; }; originalURL?: string; summary?: string; }[]; mentions?: string[]; reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]; seen?: boolean | string | object; senderName?: string; sendStatus?: { status: 'SUCCESS' | 'PENDING' | 'FAIL_RETRIABLE' | 'FAIL_PERMANENT'; timestamp: string; deliveredToUsers?: string[]; internalError?: string; message?: string; reason?: string; }; text?: string; type?: string; }`\n\n - `id: string`\n - `accountID: string`\n - `chatID: string`\n - `senderID: string`\n - `sortKey: string`\n - `timestamp: string`\n - `attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: { height?: number; width?: number; }; srcURL?: string; transcription?: { engine: string; transcription: string; language?: string; }; }[]`\n - `editedTimestamp?: string`\n - `isDeleted?: boolean`\n - `isHidden?: boolean`\n - `isSender?: boolean`\n - `isUnread?: boolean`\n - `linkedMessageID?: string`\n - `links?: { title: string; url: string; favicon?: string; img?: string; imgSize?: { height?: number; width?: number; }; originalURL?: string; summary?: string; }[]`\n - `mentions?: string[]`\n - `reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]`\n - `seen?: boolean | string | object`\n - `senderName?: string`\n - `sendStatus?: { status: 'SUCCESS' | 'PENDING' | 'FAIL_RETRIABLE' | 'FAIL_PERMANENT'; timestamp: string; deliveredToUsers?: string[]; internalError?: string; message?: string; reason?: string; }`\n - `text?: string`\n - `type?: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\n// Automatically fetches more pages as needed.\nfor await (const message of client.messages.search()) {\n console.log(message);\n}\n```",
perLanguage: {
typescript: {
method: 'client.messages.search',
@@ -755,7 +996,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
php: {
method: 'messages->search',
example:
- "messages->search(\n accountIDs: [\n 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc',\n 'local-instagram_ba_eRfQMmnSNy_p7Ih7HL7RduRpKFU',\n ],\n chatIDs: ['!NCdzlIaMjZUmvmvyHU:beeper.com', '1231073'],\n chatType: 'group',\n cursor: '1725489123456|c29tZUltc2dQYWdl',\n dateAfter: new \\DateTimeImmutable('2025-08-01T00:00:00Z'),\n dateBefore: new \\DateTimeImmutable('2025-08-31T23:59:59Z'),\n direction: 'before',\n excludeLowPriority: true,\n includeMuted: true,\n limit: 20,\n mediaTypes: ['any'],\n query: 'dinner',\n sender: 'sender',\n);\n\nvar_dump($page);",
+ "messages->search(\n accountIDs: [\n 'matrix', 'discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'\n ],\n chatIDs: ['!NCdzlIaMjZUmvmvyHU:beeper.com', '1231073'],\n chatType: 'group',\n cursor: '1725489123456|c29tZUltc2dQYWdl',\n dateAfter: new \\DateTimeImmutable('2025-08-01T00:00:00Z'),\n dateBefore: new \\DateTimeImmutable('2025-08-31T23:59:59Z'),\n direction: 'before',\n excludeLowPriority: true,\n includeMuted: true,\n limit: 20,\n mediaTypes: ['any'],\n query: 'dinner',\n sender: 'sender',\n);\n\nvar_dump($page);",
},
http: {
example:
@@ -773,9 +1014,9 @@ const EMBEDDED_METHODS: MethodEntry[] = [
qualified: 'client.messages.list',
params: ['chatID: string;', 'cursor?: string;', "direction?: 'after' | 'before';"],
response:
- "{ id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: object; srcURL?: string; }[]; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]; senderName?: string; text?: string; type?: string; }",
+ "{ id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: object; srcURL?: string; transcription?: object; }[]; editedTimestamp?: string; isDeleted?: boolean; isHidden?: boolean; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; links?: { title: string; url: string; favicon?: string; img?: string; imgSize?: { height?: number; width?: number; }; originalURL?: string; summary?: string; }[]; mentions?: string[]; reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]; seen?: boolean | string | object; senderName?: string; sendStatus?: { status: 'SUCCESS' | 'PENDING' | 'FAIL_RETRIABLE' | 'FAIL_PERMANENT'; timestamp: string; deliveredToUsers?: string[]; internalError?: string; message?: string; reason?: string; }; text?: string; type?: string; }",
markdown:
- "## list\n\n`client.messages.list(chatID: string, cursor?: string, direction?: 'after' | 'before'): { id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: attachment[]; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; reactions?: reaction[]; senderName?: string; text?: string; type?: string; }`\n\n**get** `/v1/chats/{chatID}/messages`\n\nList all messages in a chat with cursor-based pagination. Sorted by timestamp.\n\n### Parameters\n\n- `chatID: string`\n Unique identifier of the chat.\n\n- `cursor?: string`\n Opaque pagination cursor; do not inspect. Use together with 'direction'.\n\n- `direction?: 'after' | 'before'`\n Pagination direction used with 'cursor': 'before' fetches older results, 'after' fetches newer results. Defaults to 'before' when only 'cursor' is provided.\n\n### Returns\n\n- `{ id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: object; srcURL?: string; }[]; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]; senderName?: string; text?: string; type?: string; }`\n\n - `id: string`\n - `accountID: string`\n - `chatID: string`\n - `senderID: string`\n - `sortKey: string`\n - `timestamp: string`\n - `attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: { height?: number; width?: number; }; srcURL?: string; }[]`\n - `isSender?: boolean`\n - `isUnread?: boolean`\n - `linkedMessageID?: string`\n - `reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]`\n - `senderName?: string`\n - `text?: string`\n - `type?: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\n// Automatically fetches more pages as needed.\nfor await (const message of client.messages.list('!NCdzlIaMjZUmvmvyHU:beeper.com')) {\n console.log(message);\n}\n```",
+ "## list\n\n`client.messages.list(chatID: string, cursor?: string, direction?: 'after' | 'before'): { id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: attachment[]; editedTimestamp?: string; isDeleted?: boolean; isHidden?: boolean; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; links?: object[]; mentions?: string[]; reactions?: reaction[]; seen?: boolean | string | object; senderName?: string; sendStatus?: object; text?: string; type?: string; }`\n\n**get** `/v1/chats/{chatID}/messages`\n\nList all messages in a chat with cursor-based pagination. Sorted by timestamp.\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `cursor?: string`\n Opaque pagination cursor; do not inspect. Use together with 'direction'.\n\n- `direction?: 'after' | 'before'`\n Pagination direction used with 'cursor': 'before' fetches older results, 'after' fetches newer results. Defaults to 'before' when only 'cursor' is provided.\n\n### Returns\n\n- `{ id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: object; srcURL?: string; transcription?: object; }[]; editedTimestamp?: string; isDeleted?: boolean; isHidden?: boolean; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; links?: { title: string; url: string; favicon?: string; img?: string; imgSize?: { height?: number; width?: number; }; originalURL?: string; summary?: string; }[]; mentions?: string[]; reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]; seen?: boolean | string | object; senderName?: string; sendStatus?: { status: 'SUCCESS' | 'PENDING' | 'FAIL_RETRIABLE' | 'FAIL_PERMANENT'; timestamp: string; deliveredToUsers?: string[]; internalError?: string; message?: string; reason?: string; }; text?: string; type?: string; }`\n\n - `id: string`\n - `accountID: string`\n - `chatID: string`\n - `senderID: string`\n - `sortKey: string`\n - `timestamp: string`\n - `attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: { height?: number; width?: number; }; srcURL?: string; transcription?: { engine: string; transcription: string; language?: string; }; }[]`\n - `editedTimestamp?: string`\n - `isDeleted?: boolean`\n - `isHidden?: boolean`\n - `isSender?: boolean`\n - `isUnread?: boolean`\n - `linkedMessageID?: string`\n - `links?: { title: string; url: string; favicon?: string; img?: string; imgSize?: { height?: number; width?: number; }; originalURL?: string; summary?: string; }[]`\n - `mentions?: string[]`\n - `reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]`\n - `seen?: boolean | string | object`\n - `senderName?: string`\n - `sendStatus?: { status: 'SUCCESS' | 'PENDING' | 'FAIL_RETRIABLE' | 'FAIL_PERMANENT'; timestamp: string; deliveredToUsers?: string[]; internalError?: string; message?: string; reason?: string; }`\n - `text?: string`\n - `type?: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\n// Automatically fetches more pages as needed.\nfor await (const message of client.messages.list('!NCdzlIaMjZUmvmvyHU:beeper.com')) {\n console.log(message);\n}\n```",
perLanguage: {
typescript: {
method: 'client.messages.list',
@@ -819,13 +1060,13 @@ const EMBEDDED_METHODS: MethodEntry[] = [
qualified: 'client.messages.send',
params: [
'chatID: string;',
- "attachment?: { uploadID: string; duration?: number; fileName?: string; mimeType?: string; size?: { height: number; width: number; }; type?: 'gif' | 'voiceNote' | 'sticker'; };",
+ "attachment?: { uploadID: string; duration?: number; fileName?: string; mimeType?: string; size?: { height: number; width: number; }; type?: 'image' | 'video' | 'audio' | 'file' | 'gif' | 'voice-note' | 'sticker'; };",
'replyToMessageID?: string;',
'text?: string;',
],
response: '{ chatID: string; pendingMessageID: string; }',
markdown:
- "## send\n\n`client.messages.send(chatID: string, attachment?: { uploadID: string; duration?: number; fileName?: string; mimeType?: string; size?: { height: number; width: number; }; type?: 'gif' | 'voiceNote' | 'sticker'; }, replyToMessageID?: string, text?: string): { chatID: string; pendingMessageID: string; }`\n\n**post** `/v1/chats/{chatID}/messages`\n\nSend a text message to a specific chat. Supports replying to existing messages. Returns a pending message ID.\n\n### Parameters\n\n- `chatID: string`\n Unique identifier of the chat.\n\n- `attachment?: { uploadID: string; duration?: number; fileName?: string; mimeType?: string; size?: { height: number; width: number; }; type?: 'gif' | 'voiceNote' | 'sticker'; }`\n Single attachment to send with the message\n - `uploadID: string`\n Upload ID from uploadAsset endpoint. Required to reference uploaded files.\n - `duration?: number`\n Duration in seconds (optional override of cached value)\n - `fileName?: string`\n Filename (optional override of cached value)\n - `mimeType?: string`\n MIME type (optional override of cached value)\n - `size?: { height: number; width: number; }`\n Dimensions (optional override of cached value)\n - `type?: 'gif' | 'voiceNote' | 'sticker'`\n Special attachment type (gif, voiceNote, sticker). If omitted, auto-detected from mimeType\n\n- `replyToMessageID?: string`\n Provide a message ID to send this as a reply to an existing message\n\n- `text?: string`\n Text content of the message you want to send. You may use markdown.\n\n### Returns\n\n- `{ chatID: string; pendingMessageID: string; }`\n\n - `chatID: string`\n - `pendingMessageID: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.messages.send('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nconsole.log(response);\n```",
+ "## send\n\n`client.messages.send(chatID: string, attachment?: { uploadID: string; duration?: number; fileName?: string; mimeType?: string; size?: { height: number; width: number; }; type?: 'image' | 'video' | 'audio' | 'file' | 'gif' | 'voice-note' | 'sticker'; }, replyToMessageID?: string, text?: string): { chatID: string; pendingMessageID: string; }`\n\n**post** `/v1/chats/{chatID}/messages`\n\nSend a text message to a specific chat. Supports replying to existing messages. Returns a pending message ID.\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `attachment?: { uploadID: string; duration?: number; fileName?: string; mimeType?: string; size?: { height: number; width: number; }; type?: 'image' | 'video' | 'audio' | 'file' | 'gif' | 'voice-note' | 'sticker'; }`\n Single attachment to send with the message\n - `uploadID: string`\n Upload ID from uploadAsset endpoint. Required to reference uploaded files.\n - `duration?: number`\n Duration in seconds (optional override of cached value)\n - `fileName?: string`\n Filename (optional override of cached value)\n - `mimeType?: string`\n MIME type (optional override of cached value)\n - `size?: { height: number; width: number; }`\n Dimensions (optional override of cached value)\n - `type?: 'image' | 'video' | 'audio' | 'file' | 'gif' | 'voice-note' | 'sticker'`\n Attachment type hint (image, video, audio, file, gif, voice-note, sticker). If omitted, auto-detected from mimeType\n\n- `replyToMessageID?: string`\n Provide a message ID to send this as a reply to an existing message\n\n- `text?: string`\n Draft text. Plain text and Markdown are converted to Matrix HTML with the same rules used by send and edit.\n\n### Returns\n\n- `{ chatID: string; pendingMessageID: string; }`\n\n - `chatID: string`\n - `pendingMessageID: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.messages.send('!NCdzlIaMjZUmvmvyHU:beeper.com');\n\nconsole.log(response);\n```",
perLanguage: {
typescript: {
method: 'client.messages.send',
@@ -850,7 +1091,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
php: {
method: 'messages->send',
example:
- "messages->send(\n '!NCdzlIaMjZUmvmvyHU:beeper.com',\n attachment: [\n 'uploadID' => 'uploadID',\n 'duration' => 0,\n 'fileName' => 'fileName',\n 'mimeType' => 'mimeType',\n 'size' => ['height' => 0, 'width' => 0],\n 'type' => 'gif',\n ],\n replyToMessageID: 'replyToMessageID',\n text: 'text',\n);\n\nvar_dump($response);",
+ "messages->send(\n '!NCdzlIaMjZUmvmvyHU:beeper.com',\n attachment: [\n 'uploadID' => 'uploadID',\n 'duration' => 0,\n 'fileName' => 'fileName',\n 'mimeType' => 'mimeType',\n 'size' => ['height' => 0, 'width' => 0],\n 'type' => 'image',\n ],\n replyToMessageID: 'replyToMessageID',\n text: 'text',\n);\n\nvar_dump($response);",
},
http: {
example:
@@ -858,6 +1099,52 @@ const EMBEDDED_METHODS: MethodEntry[] = [
},
},
},
+ {
+ name: 'retrieve',
+ endpoint: '/v1/chats/{chatID}/messages/{messageID}',
+ httpMethod: 'get',
+ summary: 'Retrieve a message',
+ description:
+ 'Retrieve a message by final message ID, pendingMessageID, or Matrix event ID. Chat ID may be a Beeper chat ID or local chat ID.',
+ stainlessPath: '(resource) messages > (method) retrieve',
+ qualified: 'client.messages.retrieve',
+ params: ['chatID: string;', 'messageID: string;'],
+ response:
+ "{ id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: object; srcURL?: string; transcription?: object; }[]; editedTimestamp?: string; isDeleted?: boolean; isHidden?: boolean; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; links?: { title: string; url: string; favicon?: string; img?: string; imgSize?: { height?: number; width?: number; }; originalURL?: string; summary?: string; }[]; mentions?: string[]; reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]; seen?: boolean | string | object; senderName?: string; sendStatus?: { status: 'SUCCESS' | 'PENDING' | 'FAIL_RETRIABLE' | 'FAIL_PERMANENT'; timestamp: string; deliveredToUsers?: string[]; internalError?: string; message?: string; reason?: string; }; text?: string; type?: string; }",
+ markdown:
+ "## retrieve\n\n`client.messages.retrieve(chatID: string, messageID: string): { id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: attachment[]; editedTimestamp?: string; isDeleted?: boolean; isHidden?: boolean; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; links?: object[]; mentions?: string[]; reactions?: reaction[]; seen?: boolean | string | object; senderName?: string; sendStatus?: object; text?: string; type?: string; }`\n\n**get** `/v1/chats/{chatID}/messages/{messageID}`\n\nRetrieve a message by final message ID, pendingMessageID, or Matrix event ID. Chat ID may be a Beeper chat ID or local chat ID.\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `messageID: string`\n Message ID.\n\n### Returns\n\n- `{ id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: object; srcURL?: string; transcription?: object; }[]; editedTimestamp?: string; isDeleted?: boolean; isHidden?: boolean; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; links?: { title: string; url: string; favicon?: string; img?: string; imgSize?: { height?: number; width?: number; }; originalURL?: string; summary?: string; }[]; mentions?: string[]; reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]; seen?: boolean | string | object; senderName?: string; sendStatus?: { status: 'SUCCESS' | 'PENDING' | 'FAIL_RETRIABLE' | 'FAIL_PERMANENT'; timestamp: string; deliveredToUsers?: string[]; internalError?: string; message?: string; reason?: string; }; text?: string; type?: string; }`\n\n - `id: string`\n - `accountID: string`\n - `chatID: string`\n - `senderID: string`\n - `sortKey: string`\n - `timestamp: string`\n - `attachments?: { type: 'unknown' | 'img' | 'video' | 'audio'; id?: string; duration?: number; fileName?: string; fileSize?: number; isGif?: boolean; isSticker?: boolean; isVoiceNote?: boolean; mimeType?: string; posterImg?: string; size?: { height?: number; width?: number; }; srcURL?: string; transcription?: { engine: string; transcription: string; language?: string; }; }[]`\n - `editedTimestamp?: string`\n - `isDeleted?: boolean`\n - `isHidden?: boolean`\n - `isSender?: boolean`\n - `isUnread?: boolean`\n - `linkedMessageID?: string`\n - `links?: { title: string; url: string; favicon?: string; img?: string; imgSize?: { height?: number; width?: number; }; originalURL?: string; summary?: string; }[]`\n - `mentions?: string[]`\n - `reactions?: { id: string; participantID: string; reactionKey: string; emoji?: boolean; imgURL?: string; }[]`\n - `seen?: boolean | string | object`\n - `senderName?: string`\n - `sendStatus?: { status: 'SUCCESS' | 'PENDING' | 'FAIL_RETRIABLE' | 'FAIL_PERMANENT'; timestamp: string; deliveredToUsers?: string[]; internalError?: string; message?: string; reason?: string; }`\n - `text?: string`\n - `type?: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst message = await client.messages.retrieve('1343993', { chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com' });\n\nconsole.log(message);\n```",
+ perLanguage: {
+ typescript: {
+ method: 'client.messages.retrieve',
+ example:
+ "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst message = await client.messages.retrieve('1343993', {\n chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',\n});\n\nconsole.log(message.id);",
+ },
+ python: {
+ method: 'messages.retrieve',
+ example:
+ 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nmessage = client.messages.retrieve(\n message_id="1343993",\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n)\nprint(message.id)',
+ },
+ go: {
+ method: 'client.Messages.Get',
+ example:
+ 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\tmessage, err := client.Messages.Get(\n\t\tcontext.TODO(),\n\t\t"1343993",\n\t\tbeeperdesktopapi.MessageGetParams{\n\t\t\tChatID: "!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", message.ID)\n}\n',
+ },
+ cli: {
+ method: 'messages retrieve',
+ example:
+ "beeper-desktop-cli messages retrieve \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com' \\\n --message-id 1343993",
+ },
+ php: {
+ method: 'messages->retrieve',
+ example:
+ "messages->retrieve(\n '1343993', chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com'\n);\n\nvar_dump($message);",
+ },
+ http: {
+ example:
+ 'curl http://localhost:23373/v1/chats/$CHAT_ID/messages/$MESSAGE_ID \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN"',
+ },
+ },
+ },
{
name: 'update',
endpoint: '/v1/chats/{chatID}/messages/{messageID}',
@@ -867,34 +1154,35 @@ const EMBEDDED_METHODS: MethodEntry[] = [
stainlessPath: '(resource) messages > (method) update',
qualified: 'client.messages.update',
params: ['chatID: string;', 'messageID: string;', 'text: string;'],
- response: '{ chatID: string; messageID: string; success: boolean; }',
+ response:
+ "{ id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: object[]; editedTimestamp?: string; isDeleted?: boolean; isHidden?: boolean; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; links?: { title: string; url: string; favicon?: string; img?: string; imgSize?: object; originalURL?: string; summary?: string; }[]; mentions?: string[]; reactions?: object[]; seen?: boolean | string | object; senderName?: string; sendStatus?: { status: 'SUCCESS' | 'PENDING' | 'FAIL_RETRIABLE' | 'FAIL_PERMANENT'; timestamp: string; deliveredToUsers?: string[]; internalError?: string; message?: string; reason?: string; }; text?: string; type?: string; }",
markdown:
- "## update\n\n`client.messages.update(chatID: string, messageID: string, text: string): { chatID: string; messageID: string; success: boolean; }`\n\n**put** `/v1/chats/{chatID}/messages/{messageID}`\n\nEdit the text content of an existing message. Messages with attachments cannot be edited.\n\n### Parameters\n\n- `chatID: string`\n Unique identifier of the chat.\n\n- `messageID: string`\n\n- `text: string`\n New text content for the message\n\n### Returns\n\n- `{ chatID: string; messageID: string; success: boolean; }`\n\n - `chatID: string`\n - `messageID: string`\n - `success: boolean`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst message = await client.messages.update('messageID', { chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com', text: 'x' });\n\nconsole.log(message);\n```",
+ "## update\n\n`client.messages.update(chatID: string, messageID: string, text: string): object`\n\n**put** `/v1/chats/{chatID}/messages/{messageID}`\n\nEdit the text content of an existing message. Messages with attachments cannot be edited.\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `messageID: string`\n Message ID.\n\n- `text: string`\n New text content for the message\n\n### Returns\n\n- `{ id: string; accountID: string; chatID: string; senderID: string; sortKey: string; timestamp: string; attachments?: object[]; editedTimestamp?: string; isDeleted?: boolean; isHidden?: boolean; isSender?: boolean; isUnread?: boolean; linkedMessageID?: string; links?: { title: string; url: string; favicon?: string; img?: string; imgSize?: object; originalURL?: string; summary?: string; }[]; mentions?: string[]; reactions?: object[]; seen?: boolean | string | object; senderName?: string; sendStatus?: { status: 'SUCCESS' | 'PENDING' | 'FAIL_RETRIABLE' | 'FAIL_PERMANENT'; timestamp: string; deliveredToUsers?: string[]; internalError?: string; message?: string; reason?: string; }; text?: string; type?: string; }`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst message = await client.messages.update('1343993', { chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com', text: 'x' });\n\nconsole.log(message);\n```",
perLanguage: {
typescript: {
method: 'client.messages.update',
example:
- "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst message = await client.messages.update('messageID', {\n chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',\n text: 'x',\n});\n\nconsole.log(message.chatID);",
+ "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst message = await client.messages.update('1343993', {\n chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',\n text: 'x',\n});\n\nconsole.log(message);",
},
python: {
method: 'messages.update',
example:
- 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nmessage = client.messages.update(\n message_id="messageID",\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n text="x",\n)\nprint(message.chat_id)',
+ 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nmessage = client.messages.update(\n message_id="1343993",\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n text="x",\n)\nprint(message)',
},
go: {
method: 'client.Messages.Update',
example:
- 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\tmessage, err := client.Messages.Update(\n\t\tcontext.TODO(),\n\t\t"messageID",\n\t\tbeeperdesktopapi.MessageUpdateParams{\n\t\t\tChatID: "!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\t\tText: "x",\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", message.ChatID)\n}\n',
+ 'package main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\tmessage, err := client.Messages.Update(\n\t\tcontext.TODO(),\n\t\t"1343993",\n\t\tbeeperdesktopapi.MessageUpdateParams{\n\t\t\tChatID: "!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\t\tText: "x",\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", message)\n}\n',
},
cli: {
method: 'messages update',
example:
- "beeper-desktop-cli messages update \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com' \\\n --message-id messageID \\\n --text x",
+ "beeper-desktop-cli messages update \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com' \\\n --message-id 1343993 \\\n --text x",
},
php: {
method: 'messages->update',
example:
- "messages->update(\n 'messageID', chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com', text: 'x'\n);\n\nvar_dump($message);",
+ "messages->update(\n '1343993', chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com', text: 'x'\n);\n\nvar_dump($message);",
},
http: {
example:
@@ -902,19 +1190,63 @@ const EMBEDDED_METHODS: MethodEntry[] = [
},
},
},
+ {
+ name: 'delete',
+ endpoint: '/v1/chats/{chatID}/messages/{messageID}',
+ httpMethod: 'delete',
+ summary: 'Delete a message',
+ description:
+ 'Delete a message by final message ID. Pending message IDs are not accepted because messages cannot be deleted while sending.',
+ stainlessPath: '(resource) messages > (method) delete',
+ qualified: 'client.messages.delete',
+ params: ['chatID: string;', 'messageID: string;', 'forEveryone?: boolean;'],
+ markdown:
+ "## delete\n\n`client.messages.delete(chatID: string, messageID: string, forEveryone?: boolean): void`\n\n**delete** `/v1/chats/{chatID}/messages/{messageID}`\n\nDelete a message by final message ID. Pending message IDs are not accepted because messages cannot be deleted while sending.\n\n### Parameters\n\n- `chatID: string`\n Chat ID. Input routes also accept the local chat ID from this Beeper Desktop installation when available.\n\n- `messageID: string`\n Message ID.\n\n- `forEveryone?: boolean`\n True to request deletion for everyone when the network supports it; false to delete only for the authenticated user when supported.\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nawait client.messages.delete('1343993', { chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com' })\n```",
+ perLanguage: {
+ typescript: {
+ method: 'client.messages.delete',
+ example:
+ "import BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nawait client.messages.delete('1343993', { chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com' });",
+ },
+ python: {
+ method: 'messages.delete',
+ example:
+ 'import os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\nclient.messages.delete(\n message_id="1343993",\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n)',
+ },
+ go: {
+ method: 'client.Messages.Delete',
+ example:
+ 'package main\n\nimport (\n\t"context"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"),\n\t)\n\terr := client.Messages.Delete(\n\t\tcontext.TODO(),\n\t\t"1343993",\n\t\tbeeperdesktopapi.MessageDeleteParams{\n\t\t\tChatID: "!NCdzlIaMjZUmvmvyHU:beeper.com",\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n',
+ },
+ cli: {
+ method: 'messages delete',
+ example:
+ "beeper-desktop-cli messages delete \\\n --access-token 'My Access Token' \\\n --chat-id '!NCdzlIaMjZUmvmvyHU:beeper.com' \\\n --message-id 1343993",
+ },
+ php: {
+ method: 'messages->delete',
+ example:
+ "messages->delete(\n '1343993', chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com', forEveryone: true\n);\n\nvar_dump($result);",
+ },
+ http: {
+ example:
+ 'curl http://localhost:23373/v1/chats/$CHAT_ID/messages/$MESSAGE_ID \\\n -X DELETE \\\n -H "Authorization: Bearer $BEEPER_ACCESS_TOKEN"',
+ },
+ },
+ },
{
name: 'download',
endpoint: '/v1/assets/download',
httpMethod: 'post',
- summary: 'Download an asset',
+ summary: 'Download a file',
description:
- 'Download a Matrix asset using its mxc:// or localmxc:// URL to the device running Beeper Desktop and return the local file URL.',
+ 'Download a Matrix file using its mxc:// or localmxc:// URL to the device running Beeper Desktop and return the local file URL.',
stainlessPath: '(resource) assets > (method) download',
qualified: 'client.assets.download',
params: ['url: string;'],
response: '{ error?: string; srcURL?: string; }',
markdown:
- "## download\n\n`client.assets.download(url: string): { error?: string; srcURL?: string; }`\n\n**post** `/v1/assets/download`\n\nDownload a Matrix asset using its mxc:// or localmxc:// URL to the device running Beeper Desktop and return the local file URL.\n\n### Parameters\n\n- `url: string`\n Matrix content URL (mxc:// or localmxc://) for the asset to download.\n\n### Returns\n\n- `{ error?: string; srcURL?: string; }`\n\n - `error?: string`\n - `srcURL?: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.assets.download({ url: 'mxc://example.org/Q4x9CqGz1pB3Oa6XgJ' });\n\nconsole.log(response);\n```",
+ "## download\n\n`client.assets.download(url: string): { error?: string; srcURL?: string; }`\n\n**post** `/v1/assets/download`\n\nDownload a Matrix file using its mxc:// or localmxc:// URL to the device running Beeper Desktop and return the local file URL.\n\n### Parameters\n\n- `url: string`\n Matrix content URL (mxc:// or localmxc://) for the file to download.\n\n### Returns\n\n- `{ error?: string; srcURL?: string; }`\n\n - `error?: string`\n - `srcURL?: string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.assets.download({ url: 'mxc://example.org/Q4x9CqGz1pB3Oa6XgJ' });\n\nconsole.log(response);\n```",
perLanguage: {
typescript: {
method: 'client.assets.download',
@@ -951,16 +1283,16 @@ const EMBEDDED_METHODS: MethodEntry[] = [
name: 'upload',
endpoint: '/v1/assets/upload',
httpMethod: 'post',
- summary: 'Upload an asset',
+ summary: 'Upload a file',
description:
- 'Upload a file to a temporary location using multipart/form-data. Returns an uploadID that can be referenced when sending messages with attachments.',
+ 'Upload a file to a temporary location using multipart/form-data. Returns an uploadID that can be referenced when sending a message or materializing a draft attachment.',
stainlessPath: '(resource) assets > (method) upload',
qualified: 'client.assets.upload',
params: ['file: string;', 'fileName?: string;', 'mimeType?: string;'],
response:
'{ duration?: number; error?: string; fileName?: string; fileSize?: number; height?: number; mimeType?: string; srcURL?: string; uploadID?: string; width?: number; }',
markdown:
- "## upload\n\n`client.assets.upload(file: string, fileName?: string, mimeType?: string): { duration?: number; error?: string; fileName?: string; fileSize?: number; height?: number; mimeType?: string; srcURL?: string; uploadID?: string; width?: number; }`\n\n**post** `/v1/assets/upload`\n\nUpload a file to a temporary location using multipart/form-data. Returns an uploadID that can be referenced when sending messages with attachments.\n\n### Parameters\n\n- `file: string`\n The file to upload (max 500 MB).\n\n- `fileName?: string`\n Original filename. Defaults to the uploaded file name if omitted\n\n- `mimeType?: string`\n MIME type. Auto-detected from magic bytes if omitted\n\n### Returns\n\n- `{ duration?: number; error?: string; fileName?: string; fileSize?: number; height?: number; mimeType?: string; srcURL?: string; uploadID?: string; width?: number; }`\n\n - `duration?: number`\n - `error?: string`\n - `fileName?: string`\n - `fileSize?: number`\n - `height?: number`\n - `mimeType?: string`\n - `srcURL?: string`\n - `uploadID?: string`\n - `width?: number`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.assets.upload({ file: fs.createReadStream('path/to/file') });\n\nconsole.log(response);\n```",
+ "## upload\n\n`client.assets.upload(file: string, fileName?: string, mimeType?: string): { duration?: number; error?: string; fileName?: string; fileSize?: number; height?: number; mimeType?: string; srcURL?: string; uploadID?: string; width?: number; }`\n\n**post** `/v1/assets/upload`\n\nUpload a file to a temporary location using multipart/form-data. Returns an uploadID that can be referenced when sending a message or materializing a draft attachment.\n\n### Parameters\n\n- `file: string`\n The file to upload (max 500 MB).\n\n- `fileName?: string`\n Original filename. Defaults to the uploaded file name if omitted\n\n- `mimeType?: string`\n MIME type. Auto-detected from magic bytes if omitted\n\n### Returns\n\n- `{ duration?: number; error?: string; fileName?: string; fileSize?: number; height?: number; mimeType?: string; srcURL?: string; uploadID?: string; width?: number; }`\n\n - `duration?: number`\n - `error?: string`\n - `fileName?: string`\n - `fileSize?: number`\n - `height?: number`\n - `mimeType?: string`\n - `srcURL?: string`\n - `uploadID?: string`\n - `width?: number`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.assets.upload({ file: fs.createReadStream('path/to/file') });\n\nconsole.log(response);\n```",
perLanguage: {
typescript: {
method: 'client.assets.upload',
@@ -997,16 +1329,16 @@ const EMBEDDED_METHODS: MethodEntry[] = [
name: 'upload_base64',
endpoint: '/v1/assets/upload/base64',
httpMethod: 'post',
- summary: 'Upload an asset (base64)',
+ summary: 'Upload a file (base64)',
description:
- 'Upload a file using a JSON body with base64-encoded content. Returns an uploadID that can be referenced when sending messages with attachments. Alternative to the multipart upload endpoint.',
+ 'Upload a file using a JSON body with base64-encoded content. Returns an uploadID that can be referenced when sending a message or materializing a draft attachment. Alternative to the multipart upload endpoint.',
stainlessPath: '(resource) assets > (method) upload_base64',
qualified: 'client.assets.uploadBase64',
params: ['content: string;', 'fileName?: string;', 'mimeType?: string;'],
response:
'{ duration?: number; error?: string; fileName?: string; fileSize?: number; height?: number; mimeType?: string; srcURL?: string; uploadID?: string; width?: number; }',
markdown:
- "## upload_base64\n\n`client.assets.uploadBase64(content: string, fileName?: string, mimeType?: string): { duration?: number; error?: string; fileName?: string; fileSize?: number; height?: number; mimeType?: string; srcURL?: string; uploadID?: string; width?: number; }`\n\n**post** `/v1/assets/upload/base64`\n\nUpload a file using a JSON body with base64-encoded content. Returns an uploadID that can be referenced when sending messages with attachments. Alternative to the multipart upload endpoint.\n\n### Parameters\n\n- `content: string`\n Base64-encoded file content (max ~500MB decoded)\n\n- `fileName?: string`\n Original filename. Generated if omitted\n\n- `mimeType?: string`\n MIME type. Auto-detected from magic bytes if omitted\n\n### Returns\n\n- `{ duration?: number; error?: string; fileName?: string; fileSize?: number; height?: number; mimeType?: string; srcURL?: string; uploadID?: string; width?: number; }`\n\n - `duration?: number`\n - `error?: string`\n - `fileName?: string`\n - `fileSize?: number`\n - `height?: number`\n - `mimeType?: string`\n - `srcURL?: string`\n - `uploadID?: string`\n - `width?: number`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.assets.uploadBase64({ content: 'x' });\n\nconsole.log(response);\n```",
+ "## upload_base64\n\n`client.assets.uploadBase64(content: string, fileName?: string, mimeType?: string): { duration?: number; error?: string; fileName?: string; fileSize?: number; height?: number; mimeType?: string; srcURL?: string; uploadID?: string; width?: number; }`\n\n**post** `/v1/assets/upload/base64`\n\nUpload a file using a JSON body with base64-encoded content. Returns an uploadID that can be referenced when sending a message or materializing a draft attachment. Alternative to the multipart upload endpoint.\n\n### Parameters\n\n- `content: string`\n Base64-encoded file content (max ~500MB decoded)\n\n- `fileName?: string`\n Original filename. Generated if omitted\n\n- `mimeType?: string`\n MIME type. Auto-detected from magic bytes if omitted\n\n### Returns\n\n- `{ duration?: number; error?: string; fileName?: string; fileSize?: number; height?: number; mimeType?: string; srcURL?: string; uploadID?: string; width?: number; }`\n\n - `duration?: number`\n - `error?: string`\n - `fileName?: string`\n - `fileSize?: number`\n - `height?: number`\n - `mimeType?: string`\n - `srcURL?: string`\n - `uploadID?: string`\n - `width?: number`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.assets.uploadBase64({ content: 'x' });\n\nconsole.log(response);\n```",
perLanguage: {
typescript: {
method: 'client.assets.uploadBase64',
@@ -1043,7 +1375,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
name: 'serve',
endpoint: '/v1/assets/serve',
httpMethod: 'get',
- summary: 'Serve an asset',
+ summary: 'Serve a file',
description:
'Stream a file given an mxc://, localmxc://, or file:// URL. Downloads first if not cached. Supports Range requests for seeking in large files.',
stainlessPath: '(resource) assets > (method) serve',
@@ -1051,7 +1383,7 @@ const EMBEDDED_METHODS: MethodEntry[] = [
params: ['url: string;'],
response: 'string',
markdown:
- "## serve\n\n`client.assets.serve(url: string): string`\n\n**get** `/v1/assets/serve`\n\nStream a file given an mxc://, localmxc://, or file:// URL. Downloads first if not cached. Supports Range requests for seeking in large files.\n\n### Parameters\n\n- `url: string`\n Asset URL to serve. Accepts mxc://, localmxc://, or file:// URLs.\n\n### Returns\n\n- `string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.assets.serve({ url: 'x' });\n\nconsole.log(response);\n\nconst content = await response.blob()\nconsole.log(content)\n```",
+ "## serve\n\n`client.assets.serve(url: string): string`\n\n**get** `/v1/assets/serve`\n\nStream a file given an mxc://, localmxc://, or file:// URL. Downloads first if not cached. Supports Range requests for seeking in large files.\n\n### Parameters\n\n- `url: string`\n File URL to serve. Accepts mxc://, localmxc://, or file:// URLs.\n\n### Returns\n\n- `string`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst response = await client.assets.serve({ url: 'x' });\n\nconsole.log(response);\n\nconst content = await response.blob()\nconsole.log(content)\n```",
perLanguage: {
typescript: {
method: 'client.assets.serve',
@@ -1087,15 +1419,15 @@ const EMBEDDED_METHODS: MethodEntry[] = [
name: 'retrieve',
endpoint: '/v1/info',
httpMethod: 'get',
- summary: 'Get Connect server info',
+ summary: 'Retrieve server info',
description:
- 'Returns app, platform, server, and endpoint discovery metadata for this Beeper Desktop instance.',
+ 'Returns app, platform, server, endpoint discovery, OAuth, and WebSocket metadata for this Beeper Desktop instance.',
stainlessPath: '(resource) info > (method) retrieve',
qualified: 'client.info.retrieve',
response:
'{ app: { bundle_id: string; name: string; version: string; }; endpoints: { mcp: string; oauth: { authorization_endpoint: string; introspection_endpoint: string; registration_endpoint: string; revocation_endpoint: string; token_endpoint: string; userinfo_endpoint: string; }; spec: string; ws_events: string; }; platform: { arch: string; os: string; release?: string; }; server: { base_url: string; hostname: string; mcp_enabled: boolean; port: number; remote_access: boolean; status: string; }; }',
markdown:
- "## retrieve\n\n`client.info.retrieve(): { app: object; endpoints: object; platform: object; server: object; }`\n\n**get** `/v1/info`\n\nReturns app, platform, server, and endpoint discovery metadata for this Beeper Desktop instance.\n\n### Returns\n\n- `{ app: { bundle_id: string; name: string; version: string; }; endpoints: { mcp: string; oauth: { authorization_endpoint: string; introspection_endpoint: string; registration_endpoint: string; revocation_endpoint: string; token_endpoint: string; userinfo_endpoint: string; }; spec: string; ws_events: string; }; platform: { arch: string; os: string; release?: string; }; server: { base_url: string; hostname: string; mcp_enabled: boolean; port: number; remote_access: boolean; status: string; }; }`\n\n - `app: { bundle_id: string; name: string; version: string; }`\n - `endpoints: { mcp: string; oauth: { authorization_endpoint: string; introspection_endpoint: string; registration_endpoint: string; revocation_endpoint: string; token_endpoint: string; userinfo_endpoint: string; }; spec: string; ws_events: string; }`\n - `platform: { arch: string; os: string; release?: string; }`\n - `server: { base_url: string; hostname: string; mcp_enabled: boolean; port: number; remote_access: boolean; status: string; }`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst info = await client.info.retrieve();\n\nconsole.log(info);\n```",
+ "## retrieve\n\n`client.info.retrieve(): { app: object; endpoints: object; platform: object; server: object; }`\n\n**get** `/v1/info`\n\nReturns app, platform, server, endpoint discovery, OAuth, and WebSocket metadata for this Beeper Desktop instance.\n\n### Returns\n\n- `{ app: { bundle_id: string; name: string; version: string; }; endpoints: { mcp: string; oauth: { authorization_endpoint: string; introspection_endpoint: string; registration_endpoint: string; revocation_endpoint: string; token_endpoint: string; userinfo_endpoint: string; }; spec: string; ws_events: string; }; platform: { arch: string; os: string; release?: string; }; server: { base_url: string; hostname: string; mcp_enabled: boolean; port: number; remote_access: boolean; status: string; }; }`\n\n - `app: { bundle_id: string; name: string; version: string; }`\n - `endpoints: { mcp: string; oauth: { authorization_endpoint: string; introspection_endpoint: string; registration_endpoint: string; revocation_endpoint: string; token_endpoint: string; userinfo_endpoint: string; }; spec: string; ws_events: string; }`\n - `platform: { arch: string; os: string; release?: string; }`\n - `server: { base_url: string; hostname: string; mcp_enabled: boolean; port: number; remote_access: boolean; status: string; }`\n\n### Example\n\n```typescript\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\nconst info = await client.info.retrieve();\n\nconsole.log(info);\n```",
perLanguage: {
typescript: {
method: 'client.info.retrieve',
@@ -1133,27 +1465,27 @@ const EMBEDDED_READMES: { language: string; content: string }[] = [
{
language: 'cli',
content:
- "# Beeper Desktop CLI\n\nThe official CLI for the [Beeper Desktop REST API](https://developers.beeper.com/desktop-api/).\n\n\n\n## Installation\n\n### Installing with Homebrew\n\n~~~sh\nbrew install beeper/tap/beeper-desktop-cli\n~~~\n\n### Installing with Go\n\nTo test or install the CLI locally, you need [Go](https://go.dev/doc/install) version 1.22 or later installed.\n\n~~~sh\ngo install 'github.com/beeper/desktop-api-cli/cmd/beeper-desktop-cli@latest'\n~~~\n\nOnce you have run `go install`, the binary is placed in your Go bin directory:\n\n- **Default location**: `$HOME/go/bin` (or `$GOPATH/bin` if GOPATH is set)\n- **Check your path**: Run `go env GOPATH` to see the base directory\n\nIf commands aren't found after installation, add the Go bin directory to your PATH:\n\n~~~sh\n# Add to your shell profile (.zshrc, .bashrc, etc.)\nexport PATH=\"$PATH:$(go env GOPATH)/bin\"\n~~~\n\n\n\n### Running Locally\n\nAfter cloning the git repository for this project, you can use the\n`scripts/run` script to run the tool locally:\n\n~~~sh\n./scripts/run args...\n~~~\n\n## Usage\n\nThe CLI follows a resource-based command structure:\n\n~~~sh\nbeeper-desktop-cli [resource] [flags...]\n~~~\n\n~~~sh\nbeeper-desktop-cli chats search \\\n --access-token 'My Access Token' \\\n --include-muted \\\n --limit 3 \\\n --type single\n~~~\n\nFor details about specific commands, use the `--help` flag.\n\n### Environment variables\n\n| Environment variable | Description | Required |\n| --------------------- | ----------------------------------------------------------------------------------------------------- | -------- |\n| `BEEPER_ACCESS_TOKEN` | Bearer access token obtained via OAuth2 PKCE flow or created in-app. Required for all API operations. | yes |\n\n### Global flags\n\n- `--access-token` - Bearer access token obtained via OAuth2 PKCE flow or created in-app. Required for all API operations. (can also be set with `BEEPER_ACCESS_TOKEN` env var)\n- `--help` - Show command line usage\n- `--debug` - Enable debug logging (includes HTTP request/response details)\n- `--version`, `-v` - Show the CLI version\n- `--base-url` - Use a custom API backend URL\n- `--format` - Change the output format (`auto`, `explore`, `json`, `jsonl`, `pretty`, `raw`, `yaml`)\n- `--format-error` - Change the output format for errors (`auto`, `explore`, `json`, `jsonl`, `pretty`, `raw`, `yaml`)\n- `--transform` - Transform the data output using [GJSON syntax](https://github.com/tidwall/gjson/blob/master/SYNTAX.md)\n- `--transform-error` - Transform the error output using [GJSON syntax](https://github.com/tidwall/gjson/blob/master/SYNTAX.md)\n\n### Passing files as arguments\n\nTo pass files to your API, you can use the `@myfile.ext` syntax:\n\n~~~bash\nbeeper-desktop-cli --arg @abe.jpg\n~~~\n\nFiles can also be passed inside JSON or YAML blobs:\n\n~~~bash\nbeeper-desktop-cli --arg '{image: \"@abe.jpg\"}'\n# Equivalent:\nbeeper-desktop-cli < --username '\\@abe'\n~~~\n\n#### Explicit encoding\n\nFor JSON endpoints, the CLI tool does filetype sniffing to determine whether the\nfile contents should be sent as a string literal (for plain text files) or as a\nbase64-encoded string literal (for binary files). If you need to explicitly send\nthe file as either plain text or base64-encoded data, you can use\n`@file://myfile.txt` (for string encoding) or `@data://myfile.dat` (for\nbase64-encoding). Note that absolute paths will begin with `@file://` or\n`@data://`, followed by a third `/` (for example, `@file:///tmp/file.txt`).\n\n~~~bash\nbeeper-desktop-cli --arg @data://file.txt\n~~~\n\n## Linking different Go SDK versions\n\nYou can link the CLI against a different version of the Beeper Desktop Go SDK\nfor development purposes using the `./scripts/link` script.\n\nTo link to a specific version from a repository (version can be a branch,\ngit tag, or commit hash):\n\n~~~bash\n./scripts/link github.com/org/repo@version\n~~~\n\nTo link to a local copy of the SDK:\n\n~~~bash\n./scripts/link ../path/to/beeperdesktopapi-go\n~~~\n\nIf you run the link script without any arguments, it will default to `../beeperdesktopapi-go`.\n",
+ "# Beeper Desktop CLI\n\nThe official CLI for the [Beeper Desktop REST API](https://developers.beeper.com/desktop-api/).\n\n\n\n## Installation\n\n### Installing with Homebrew\n\n~~~sh\nbrew install beeper/tap/beeper-desktop-cli\n~~~\n\n### Installing with Go\n\nTo test or install the CLI locally, you need [Go](https://go.dev/doc/install) version 1.22 or later installed.\n\n~~~sh\ngo install 'github.com/beeper/desktop-api-cli/cmd/beeper-desktop-cli@latest'\n~~~\n\nOnce you have run `go install`, the binary is placed in your Go bin directory:\n\n- **Default location**: `$HOME/go/bin` (or `$GOPATH/bin` if GOPATH is set)\n- **Check your path**: Run `go env GOPATH` to see the base directory\n\nIf commands aren't found after installation, add the Go bin directory to your PATH:\n\n~~~sh\n# Add to your shell profile (.zshrc, .bashrc, etc.)\nexport PATH=\"$PATH:$(go env GOPATH)/bin\"\n~~~\n\n\n\n### Running Locally\n\nAfter cloning the git repository for this project, you can use the\n`scripts/run` script to run the tool locally:\n\n~~~sh\n./scripts/run args...\n~~~\n\n## Usage\n\nThe CLI follows a resource-based command structure:\n\n~~~sh\nbeeper-desktop-cli [resource] [flags...]\n~~~\n\n~~~sh\nbeeper-desktop-cli chats search \\\n --access-token 'My Access Token' \\\n --account-id matrix \\\n --account-id discordgo \\\n --account-id local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc \\\n --include-muted \\\n --limit 3 \\\n --type single\n~~~\n\nFor details about specific commands, use the `--help` flag.\n\n### Environment variables\n\n| Environment variable | Description | Required |\n| --------------------- | ----------------------------------------------------------------------------------------------------- | -------- |\n| `BEEPER_ACCESS_TOKEN` | Bearer access token obtained via OAuth2 PKCE flow or created in-app. Required for all API operations. | yes |\n\n### Global flags\n\n- `--access-token` - Bearer access token obtained via OAuth2 PKCE flow or created in-app. Required for all API operations. (can also be set with `BEEPER_ACCESS_TOKEN` env var)\n- `--help` - Show command line usage\n- `--debug` - Enable debug logging (includes HTTP request/response details)\n- `--version`, `-v` - Show the CLI version\n- `--base-url` - Use a custom API backend URL\n- `--format` - Change the output format (`auto`, `explore`, `json`, `jsonl`, `pretty`, `raw`, `yaml`)\n- `--format-error` - Change the output format for errors (`auto`, `explore`, `json`, `jsonl`, `pretty`, `raw`, `yaml`)\n- `--transform` - Transform the data output using [GJSON syntax](https://github.com/tidwall/gjson/blob/master/SYNTAX.md)\n- `--transform-error` - Transform the error output using [GJSON syntax](https://github.com/tidwall/gjson/blob/master/SYNTAX.md)\n\n### Passing files as arguments\n\nTo pass files to your API, you can use the `@myfile.ext` syntax:\n\n~~~bash\nbeeper-desktop-cli --arg @abe.jpg\n~~~\n\nFiles can also be passed inside JSON or YAML blobs:\n\n~~~bash\nbeeper-desktop-cli --arg '{image: \"@abe.jpg\"}'\n# Equivalent:\nbeeper-desktop-cli < --username '\\@abe'\n~~~\n\n#### Explicit encoding\n\nFor JSON endpoints, the CLI tool does filetype sniffing to determine whether the\nfile contents should be sent as a string literal (for plain text files) or as a\nbase64-encoded string literal (for binary files). If you need to explicitly send\nthe file as either plain text or base64-encoded data, you can use\n`@file://myfile.txt` (for string encoding) or `@data://myfile.dat` (for\nbase64-encoding). Note that absolute paths will begin with `@file://` or\n`@data://`, followed by a third `/` (for example, `@file:///tmp/file.txt`).\n\n~~~bash\nbeeper-desktop-cli --arg @data://file.txt\n~~~\n\n## Linking different Go SDK versions\n\nYou can link the CLI against a different version of the Beeper Desktop Go SDK\nfor development purposes using the `./scripts/link` script.\n\nTo link to a specific version from a repository (version can be a branch,\ngit tag, or commit hash):\n\n~~~bash\n./scripts/link github.com/org/repo@version\n~~~\n\nTo link to a local copy of the SDK:\n\n~~~bash\n./scripts/link ../path/to/beeperdesktopapi-go\n~~~\n\nIf you run the link script without any arguments, it will default to `../beeperdesktopapi-go`.\n",
},
{
language: 'go',
content:
- '# Beeper Desktop Go API Library\n\n
\n\nThe Beeper Desktop Go library provides convenient access to the [Beeper Desktop REST API](https://developers.beeper.com/desktop-api/)\nfrom applications written in Go.\n\n\n\n## MCP Server\n\nUse the Beeper Desktop MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[](https://cursor.com/en-US/install-mcp?name=%40beeper%2Fdesktop-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBiZWVwZXIvZGVza3RvcC1tY3AiXSwiZW52Ijp7IkJFRVBFUl9BQ0NFU1NfVE9LRU4iOiJNeSBBY2Nlc3MgVG9rZW4ifX0)\n[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40beeper%2Fdesktop-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40beeper%2Fdesktop-mcp%22%5D%2C%22env%22%3A%7B%22BEEPER_ACCESS_TOKEN%22%3A%22My%20Access%20Token%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n## Installation\n\n\n\n```go\nimport (\n\t"github.com/beeper/desktop-api-go" // imported as SDK_PackageName\n)\n```\n\n\n\nOr to pin the version:\n\n\n\n```sh\ngo get -u \'github.com/beeper/desktop-api-go@v0.0.1\'\n```\n\n\n\n## Requirements\n\nThis library requires Go 1.22+.\n\n## Usage\n\nThe full API of this library can be found in [api.md](api.md).\n\n```go\npackage main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"), // defaults to os.LookupEnv("BEEPER_ACCESS_TOKEN")\n\t)\n\tpage, err := client.Chats.Search(context.TODO(), beeperdesktopapi.ChatSearchParams{\n\t\tIncludeMuted: beeperdesktopapi.Bool(true),\n\t\tLimit: beeperdesktopapi.Int(3),\n\t\tType: beeperdesktopapi.ChatSearchParamsTypeSingle,\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", page)\n}\n\n```\n\n### Request fields\n\nAll request parameters are wrapped in a generic `Field` type,\nwhich we use to distinguish zero values from null or omitted fields.\n\nThis prevents accidentally sending a zero value if you forget a required parameter,\nand enables explicitly sending `null`, `false`, `\'\'`, or `0` on optional parameters.\nAny field not specified is not sent.\n\nTo construct fields with values, use the helpers `String()`, `Int()`, `Float()`, or most commonly, the generic `F[T]()`.\nTo send a null, use `Null[T]()`, and to send a nonconforming value, use `Raw[T](any)`. For example:\n\n```go\nparams := FooParams{\n\tName: SDK_PackageName.F("hello"),\n\n\t// Explicitly send `"description": null`\n\tDescription: SDK_PackageName.Null[string](),\n\n\tPoint: SDK_PackageName.F(SDK_PackageName.Point{\n\t\tX: SDK_PackageName.Int(0),\n\t\tY: SDK_PackageName.Int(1),\n\n\t\t// In cases where the API specifies a given type,\n\t\t// but you want to send something else, use `Raw`:\n\t\tZ: SDK_PackageName.Raw[int64](0.01), // sends a float\n\t}),\n}\n```\n\n### Response objects\n\nAll fields in response structs are value types (not pointers or wrappers).\n\nIf a given field is `null`, not present, or invalid, the corresponding field\nwill simply be its zero value.\n\nAll response structs also include a special `JSON` field, containing more detailed\ninformation about each property, which you can use like so:\n\n```go\nif res.Name == "" {\n\t// true if `"name"` is either not present or explicitly null\n\tres.JSON.Name.IsNull()\n\n\t// true if the `"name"` key was not present in the response JSON at all\n\tres.JSON.Name.IsMissing()\n\n\t// When the API returns data that cannot be coerced to the expected type:\n\tif res.JSON.Name.IsInvalid() {\n\t\traw := res.JSON.Name.Raw()\n\n\t\tlegacyName := struct{\n\t\t\tFirst string `json:"first"`\n\t\t\tLast string `json:"last"`\n\t\t}{}\n\t\tjson.Unmarshal([]byte(raw), &legacyName)\n\t\tname = legacyName.First + " " + legacyName.Last\n\t}\n}\n```\n\nThese `.JSON` structs also include an `Extras` map containing\nany properties in the json response that were not specified\nin the struct. This can be useful for API features not yet\npresent in the SDK.\n\n```go\nbody := res.JSON.ExtraFields["my_unexpected_field"].Raw()\n```\n\n### RequestOptions\n\nThis library uses the functional options pattern. Functions defined in the\n`SDK_PackageOptionName` package return a `RequestOption`, which is a closure that mutates a\n`RequestConfig`. These options can be supplied to the client or at individual\nrequests. For example:\n\n```go\nclient := SDK_PackageName.SDK_ClientInitializerName(\n\t// Adds a header to every request made by the client\n\tSDK_PackageOptionName.WithHeader("X-Some-Header", "custom_header_info"),\n)\n\nclient.Accounts.List(context.TODO(), ...,\n\t// Override the header\n\tSDK_PackageOptionName.WithHeader("X-Some-Header", "some_other_custom_header_info"),\n\t// Add an undocumented field to the request body, using sjson syntax\n\tSDK_PackageOptionName.WithJSONSet("some.json.path", map[string]string{"my": "object"}),\n)\n```\n\nSee the [full list of request options](https://pkg.go.dev/github.com/beeper/desktop-api-go/SDK_PackageOptionName).\n\n### Pagination\n\nThis library provides some conveniences for working with paginated list endpoints.\n\nYou can use `.ListAutoPaging()` methods to iterate through items across all pages:\n\n```go\niter := client.Messages.SearchAutoPaging(context.TODO(), beeperdesktopapi.MessageSearchParams{\n\tAccountIDs: []string{"local-telegram_ba_QFrb5lrLPhO3OT5MFBeTWv0x4BI"},\n\tLimit: beeperdesktopapi.Int(10),\n\tQuery: beeperdesktopapi.String("deployment"),\n})\n// Automatically fetches more pages as needed.\nfor iter.Next() {\n\tmessage := iter.Current()\n\tfmt.Printf("%+v\\n", message)\n}\nif err := iter.Err(); err != nil {\n\tpanic(err.Error())\n}\n```\n\nOr you can use simple `.List()` methods to fetch a single page and receive a standard response object\nwith additional helper methods like `.GetNextPage()`, e.g.:\n\n```go\npage, err := client.Messages.Search(context.TODO(), beeperdesktopapi.MessageSearchParams{\n\tAccountIDs: []string{"local-telegram_ba_QFrb5lrLPhO3OT5MFBeTWv0x4BI"},\n\tLimit: beeperdesktopapi.Int(10),\n\tQuery: beeperdesktopapi.String("deployment"),\n})\nfor page != nil {\n\tfor _, message := range page.Items {\n\t\tfmt.Printf("%+v\\n", message)\n\t}\n\tpage, err = page.GetNextPage()\n}\nif err != nil {\n\tpanic(err.Error())\n}\n```\n\n### Errors\n\nWhen the API returns a non-success status code, we return an error with type\n`*SDK_PackageName.Error`. This contains the `StatusCode`, `*http.Request`, and\n`*http.Response` values of the request, as well as the JSON of the error body\n(much like other response objects in the SDK).\n\nTo handle errors, we recommend that you use the `errors.As` pattern:\n\n```go\n_, err := client.Accounts.List(context.TODO())\nif err != nil {\n\tvar apierr *beeperdesktopapi.Error\n\tif errors.As(err, &apierr) {\n\t\tprintln(string(apierr.DumpRequest(true))) // Prints the serialized HTTP request\n\t\tprintln(string(apierr.DumpResponse(true))) // Prints the serialized HTTP response\n\t}\n\tpanic(err.Error()) // GET "/v1/accounts": 400 Bad Request { ... }\n}\n```\n\nWhen other errors occur, they are returned unwrapped; for example,\nif HTTP transport fails, you might receive `*url.Error` wrapping `*net.OpError`.\n\n### Timeouts\n\nRequests do not time out by default; use context to configure a timeout for a request lifecycle.\n\nNote that if a request is [retried](#retries), the context timeout does not start over.\nTo set a per-retry timeout, use `SDK_PackageOptionName.WithRequestTimeout()`.\n\n```go\n// This sets the timeout for the request, including all the retries.\nctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)\ndefer cancel()\nclient.Accounts.List(\n\tctx,\n\t// This sets the per-retry timeout\n\toption.WithRequestTimeout(20*time.Second),\n)\n```\n\n### File uploads\n\nRequest parameters that correspond to file uploads in multipart requests are typed as\n`param.Field[io.Reader]`. The contents of the `io.Reader` will by default be sent as a multipart form\npart with the file name of "anonymous_file" and content-type of "application/octet-stream".\n\nThe file name and content-type can be customized by implementing `Name() string` or `ContentType()\nstring` on the run-time type of `io.Reader`. Note that `os.File` implements `Name() string`, so a\nfile returned by `os.Open` will be sent with the file name on disk.\n\nWe also provide a helper `SDK_PackageName.FileParam(reader io.Reader, filename string, contentType string)`\nwhich can be used to wrap any `io.Reader` with the appropriate file name and content type.\n\n```go\n// A file from the file system\nfile, err := os.Open("/path/to/file")\nbeeperdesktopapi.AssetUploadParams{\n\tFile: file,\n}\n\n// A file from a string\nbeeperdesktopapi.AssetUploadParams{\n\tFile: strings.NewReader("my file contents"),\n}\n\n// With a custom filename and contentType\nbeeperdesktopapi.AssetUploadParams{\n\tFile: beeperdesktopapi.File(strings.NewReader(`{"hello": "foo"}`), "file.go", "application/json"),\n}\n```\n\n### Retries\n\nCertain errors will be automatically retried 2 times by default, with a short exponential backoff.\nWe retry by default all connection errors, 408 Request Timeout, 409 Conflict, 429 Rate Limit,\nand >=500 Internal errors.\n\nYou can use the `WithMaxRetries` option to configure or disable this:\n\n```go\n// Configure the default for all requests:\nclient := beeperdesktopapi.NewClient(\n\toption.WithMaxRetries(0), // default is 2\n)\n\n// Override per-request:\nclient.Accounts.List(context.TODO(), option.WithMaxRetries(5))\n```\n\n\n### Accessing raw response data (e.g. response headers)\n\nYou can access the raw HTTP response data by using the `option.WithResponseInto()` request option. This is useful when\nyou need to examine response headers, status codes, or other details.\n\n```go\n// Create a variable to store the HTTP response\nvar response *http.Response\naccounts, err := client.Accounts.List(context.TODO(), option.WithResponseInto(&response))\nif err != nil {\n\t// handle error\n}\nfmt.Printf("%+v\\n", accounts)\n\nfmt.Printf("Status Code: %d\\n", response.StatusCode)\nfmt.Printf("Headers: %+#v\\n", response.Header)\n```\n\n### Making custom/undocumented requests\n\nThis library is typed for convenient access to the documented API. If you need to access undocumented\nendpoints, params, or response properties, the library can still be used.\n\n#### Undocumented endpoints\n\nTo make requests to undocumented endpoints, you can use `client.Get`, `client.Post`, and other HTTP verbs.\n`RequestOptions` on the client, such as retries, will be respected when making these requests.\n\n```go\nvar (\n // params can be an io.Reader, a []byte, an encoding/json serializable object,\n // or a "…Params" struct defined in this library.\n params map[string]interface{}\n\n // result can be an []byte, *http.Response, a encoding/json deserializable object,\n // or a model defined in this library.\n result *http.Response\n)\nerr := client.Post(context.Background(), "/unspecified", params, &result)\nif err != nil {\n …\n}\n```\n\n#### Undocumented request params\n\nTo make requests using undocumented parameters, you may use either the `SDK_PackageOptionName.WithQuerySet()`\nor the `SDK_PackageOptionName.WithJSONSet()` methods.\n\n```go\nparams := FooNewParams{\n ID: SDK_PackageName.F("id_xxxx"),\n Data: SDK_PackageName.F(FooNewParamsData{\n FirstName: SDK_PackageName.F("John"),\n }),\n}\nclient.Foo.New(context.Background(), params, SDK_PackageOptionName.WithJSONSet("data.last_name", "Doe"))\n```\n\n#### Undocumented response properties\n\nTo access undocumented response properties, you may either access the raw JSON of the response as a string\nwith `result.JSON.RawJSON()`, or get the raw JSON of a particular field on the result with\n`result.JSON.Foo.Raw()`.\n\nAny fields that are not present on the response struct will be saved and can be accessed by `result.JSON.ExtraFields()` which returns the extra fields as a `map[string]Field`.\n\n### Middleware\n\nWe provide `SDK_PackageOptionName.WithMiddleware` which applies the given\nmiddleware to requests.\n\n```go\nfunc Logger(req *http.Request, next SDK_PackageOptionName.MiddlewareNext) (res *http.Response, err error) {\n\t// Before the request\n\tstart := time.Now()\n\tLogReq(req)\n\n\t// Forward the request to the next handler\n\tres, err = next(req)\n\n\t// Handle stuff after the request\n\tend := time.Now()\n\tLogRes(res, err, start - end)\n\n return res, err\n}\n\nclient := SDK_PackageName.SDK_ClientInitializerName(\n\tSDK_PackageOptionName.WithMiddleware(Logger),\n)\n```\n\nWhen multiple middlewares are provided as variadic arguments, the middlewares\nare applied left to right. If `SDK_PackageOptionName.WithMiddleware` is given\nmultiple times, for example first in the client then the method, the\nmiddleware in the client will run first and the middleware given in the method\nwill run next.\n\nYou may also replace the default `http.Client` with\n`SDK_PackageOptionName.WithHTTPClient(client)`. Only one http client is\naccepted (this overwrites any previous client) and receives requests after any\nmiddleware has been applied.\n\n## Semantic versioning\n\nThis package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:\n\n1. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_\n2. Changes that we do not expect to impact the vast majority of users in practice.\n\nWe take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.\n\nWe are keen for your feedback; please open an [issue](https://www.github.com/beeper/desktop-api-go/issues) with questions, bugs, or suggestions.\n\n## Contributing\n\nSee [the contributing documentation](./CONTRIBUTING.md).\n',
+ '# Beeper Desktop Go API Library\n\n
\n\nThe Beeper Desktop Go library provides convenient access to the [Beeper Desktop REST API](https://developers.beeper.com/desktop-api/)\nfrom applications written in Go.\n\n\n\n## MCP Server\n\nUse the Beeper Desktop MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[](https://cursor.com/en-US/install-mcp?name=%40beeper%2Fdesktop-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBiZWVwZXIvZGVza3RvcC1tY3AiXSwiZW52Ijp7IkJFRVBFUl9BQ0NFU1NfVE9LRU4iOiJNeSBBY2Nlc3MgVG9rZW4ifX0)\n[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40beeper%2Fdesktop-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40beeper%2Fdesktop-mcp%22%5D%2C%22env%22%3A%7B%22BEEPER_ACCESS_TOKEN%22%3A%22My%20Access%20Token%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n## Installation\n\n\n\n```go\nimport (\n\t"github.com/beeper/desktop-api-go" // imported as SDK_PackageName\n)\n```\n\n\n\nOr to pin the version:\n\n\n\n```sh\ngo get -u \'github.com/beeper/desktop-api-go@v0.0.1\'\n```\n\n\n\n## Requirements\n\nThis library requires Go 1.22+.\n\n## Usage\n\nThe full API of this library can be found in [api.md](api.md).\n\n```go\npackage main\n\nimport (\n\t"context"\n\t"fmt"\n\n\t"github.com/beeper/desktop-api-go"\n\t"github.com/beeper/desktop-api-go/option"\n)\n\nfunc main() {\n\tclient := beeperdesktopapi.NewClient(\n\t\toption.WithAccessToken("My Access Token"), // defaults to os.LookupEnv("BEEPER_ACCESS_TOKEN")\n\t)\n\tpage, err := client.Chats.Search(context.TODO(), beeperdesktopapi.ChatSearchParams{\n\t\tAccountIDs: []string{"matrix", "discordgo", "local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc"},\n\t\tIncludeMuted: beeperdesktopapi.Bool(true),\n\t\tLimit: beeperdesktopapi.Int(3),\n\t\tType: beeperdesktopapi.ChatSearchParamsTypeSingle,\n\t})\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf("%+v\\n", page)\n}\n\n```\n\n### Request fields\n\nAll request parameters are wrapped in a generic `Field` type,\nwhich we use to distinguish zero values from null or omitted fields.\n\nThis prevents accidentally sending a zero value if you forget a required parameter,\nand enables explicitly sending `null`, `false`, `\'\'`, or `0` on optional parameters.\nAny field not specified is not sent.\n\nTo construct fields with values, use the helpers `String()`, `Int()`, `Float()`, or most commonly, the generic `F[T]()`.\nTo send a null, use `Null[T]()`, and to send a nonconforming value, use `Raw[T](any)`. For example:\n\n```go\nparams := FooParams{\n\tName: SDK_PackageName.F("hello"),\n\n\t// Explicitly send `"description": null`\n\tDescription: SDK_PackageName.Null[string](),\n\n\tPoint: SDK_PackageName.F(SDK_PackageName.Point{\n\t\tX: SDK_PackageName.Int(0),\n\t\tY: SDK_PackageName.Int(1),\n\n\t\t// In cases where the API specifies a given type,\n\t\t// but you want to send something else, use `Raw`:\n\t\tZ: SDK_PackageName.Raw[int64](0.01), // sends a float\n\t}),\n}\n```\n\n### Response objects\n\nAll fields in response structs are value types (not pointers or wrappers).\n\nIf a given field is `null`, not present, or invalid, the corresponding field\nwill simply be its zero value.\n\nAll response structs also include a special `JSON` field, containing more detailed\ninformation about each property, which you can use like so:\n\n```go\nif res.Name == "" {\n\t// true if `"name"` is either not present or explicitly null\n\tres.JSON.Name.IsNull()\n\n\t// true if the `"name"` key was not present in the response JSON at all\n\tres.JSON.Name.IsMissing()\n\n\t// When the API returns data that cannot be coerced to the expected type:\n\tif res.JSON.Name.IsInvalid() {\n\t\traw := res.JSON.Name.Raw()\n\n\t\tlegacyName := struct{\n\t\t\tFirst string `json:"first"`\n\t\t\tLast string `json:"last"`\n\t\t}{}\n\t\tjson.Unmarshal([]byte(raw), &legacyName)\n\t\tname = legacyName.First + " " + legacyName.Last\n\t}\n}\n```\n\nThese `.JSON` structs also include an `Extras` map containing\nany properties in the json response that were not specified\nin the struct. This can be useful for API features not yet\npresent in the SDK.\n\n```go\nbody := res.JSON.ExtraFields["my_unexpected_field"].Raw()\n```\n\n### RequestOptions\n\nThis library uses the functional options pattern. Functions defined in the\n`SDK_PackageOptionName` package return a `RequestOption`, which is a closure that mutates a\n`RequestConfig`. These options can be supplied to the client or at individual\nrequests. For example:\n\n```go\nclient := SDK_PackageName.SDK_ClientInitializerName(\n\t// Adds a header to every request made by the client\n\tSDK_PackageOptionName.WithHeader("X-Some-Header", "custom_header_info"),\n)\n\nclient.Accounts.List(context.TODO(), ...,\n\t// Override the header\n\tSDK_PackageOptionName.WithHeader("X-Some-Header", "some_other_custom_header_info"),\n\t// Add an undocumented field to the request body, using sjson syntax\n\tSDK_PackageOptionName.WithJSONSet("some.json.path", map[string]string{"my": "object"}),\n)\n```\n\nSee the [full list of request options](https://pkg.go.dev/github.com/beeper/desktop-api-go/SDK_PackageOptionName).\n\n### Pagination\n\nThis library provides some conveniences for working with paginated list endpoints.\n\nYou can use `.ListAutoPaging()` methods to iterate through items across all pages:\n\n```go\niter := client.Messages.SearchAutoPaging(context.TODO(), beeperdesktopapi.MessageSearchParams{\n\tAccountIDs: []string{"discordgo", "local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc"},\n\tLimit: beeperdesktopapi.Int(10),\n\tQuery: beeperdesktopapi.String("oauth"),\n})\n// Automatically fetches more pages as needed.\nfor iter.Next() {\n\tmessage := iter.Current()\n\tfmt.Printf("%+v\\n", message)\n}\nif err := iter.Err(); err != nil {\n\tpanic(err.Error())\n}\n```\n\nOr you can use simple `.List()` methods to fetch a single page and receive a standard response object\nwith additional helper methods like `.GetNextPage()`, e.g.:\n\n```go\npage, err := client.Messages.Search(context.TODO(), beeperdesktopapi.MessageSearchParams{\n\tAccountIDs: []string{"discordgo", "local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc"},\n\tLimit: beeperdesktopapi.Int(10),\n\tQuery: beeperdesktopapi.String("oauth"),\n})\nfor page != nil {\n\tfor _, message := range page.Items {\n\t\tfmt.Printf("%+v\\n", message)\n\t}\n\tpage, err = page.GetNextPage()\n}\nif err != nil {\n\tpanic(err.Error())\n}\n```\n\n### Errors\n\nWhen the API returns a non-success status code, we return an error with type\n`*SDK_PackageName.Error`. This contains the `StatusCode`, `*http.Request`, and\n`*http.Response` values of the request, as well as the JSON of the error body\n(much like other response objects in the SDK).\n\nTo handle errors, we recommend that you use the `errors.As` pattern:\n\n```go\n_, err := client.Accounts.List(context.TODO())\nif err != nil {\n\tvar apierr *beeperdesktopapi.Error\n\tif errors.As(err, &apierr) {\n\t\tprintln(string(apierr.DumpRequest(true))) // Prints the serialized HTTP request\n\t\tprintln(string(apierr.DumpResponse(true))) // Prints the serialized HTTP response\n\t}\n\tpanic(err.Error()) // GET "/v1/accounts": 400 Bad Request { ... }\n}\n```\n\nWhen other errors occur, they are returned unwrapped; for example,\nif HTTP transport fails, you might receive `*url.Error` wrapping `*net.OpError`.\n\n### Timeouts\n\nRequests do not time out by default; use context to configure a timeout for a request lifecycle.\n\nNote that if a request is [retried](#retries), the context timeout does not start over.\nTo set a per-retry timeout, use `SDK_PackageOptionName.WithRequestTimeout()`.\n\n```go\n// This sets the timeout for the request, including all the retries.\nctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)\ndefer cancel()\nclient.Accounts.List(\n\tctx,\n\t// This sets the per-retry timeout\n\toption.WithRequestTimeout(20*time.Second),\n)\n```\n\n### File uploads\n\nRequest parameters that correspond to file uploads in multipart requests are typed as\n`param.Field[io.Reader]`. The contents of the `io.Reader` will by default be sent as a multipart form\npart with the file name of "anonymous_file" and content-type of "application/octet-stream".\n\nThe file name and content-type can be customized by implementing `Name() string` or `ContentType()\nstring` on the run-time type of `io.Reader`. Note that `os.File` implements `Name() string`, so a\nfile returned by `os.Open` will be sent with the file name on disk.\n\nWe also provide a helper `SDK_PackageName.FileParam(reader io.Reader, filename string, contentType string)`\nwhich can be used to wrap any `io.Reader` with the appropriate file name and content type.\n\n```go\n// A file from the file system\nfile, err := os.Open("/path/to/file")\nbeeperdesktopapi.AssetUploadParams{\n\tFile: file,\n}\n\n// A file from a string\nbeeperdesktopapi.AssetUploadParams{\n\tFile: strings.NewReader("my file contents"),\n}\n\n// With a custom filename and contentType\nbeeperdesktopapi.AssetUploadParams{\n\tFile: beeperdesktopapi.File(strings.NewReader(`{"hello": "foo"}`), "file.go", "application/json"),\n}\n```\n\n### Retries\n\nCertain errors will be automatically retried 2 times by default, with a short exponential backoff.\nWe retry by default all connection errors, 408 Request Timeout, 409 Conflict, 429 Rate Limit,\nand >=500 Internal errors.\n\nYou can use the `WithMaxRetries` option to configure or disable this:\n\n```go\n// Configure the default for all requests:\nclient := beeperdesktopapi.NewClient(\n\toption.WithMaxRetries(0), // default is 2\n)\n\n// Override per-request:\nclient.Accounts.List(context.TODO(), option.WithMaxRetries(5))\n```\n\n\n### Accessing raw response data (e.g. response headers)\n\nYou can access the raw HTTP response data by using the `option.WithResponseInto()` request option. This is useful when\nyou need to examine response headers, status codes, or other details.\n\n```go\n// Create a variable to store the HTTP response\nvar response *http.Response\naccounts, err := client.Accounts.List(context.TODO(), option.WithResponseInto(&response))\nif err != nil {\n\t// handle error\n}\nfmt.Printf("%+v\\n", accounts)\n\nfmt.Printf("Status Code: %d\\n", response.StatusCode)\nfmt.Printf("Headers: %+#v\\n", response.Header)\n```\n\n### Making custom/undocumented requests\n\nThis library is typed for convenient access to the documented API. If you need to access undocumented\nendpoints, params, or response properties, the library can still be used.\n\n#### Undocumented endpoints\n\nTo make requests to undocumented endpoints, you can use `client.Get`, `client.Post`, and other HTTP verbs.\n`RequestOptions` on the client, such as retries, will be respected when making these requests.\n\n```go\nvar (\n // params can be an io.Reader, a []byte, an encoding/json serializable object,\n // or a "…Params" struct defined in this library.\n params map[string]interface{}\n\n // result can be an []byte, *http.Response, a encoding/json deserializable object,\n // or a model defined in this library.\n result *http.Response\n)\nerr := client.Post(context.Background(), "/unspecified", params, &result)\nif err != nil {\n …\n}\n```\n\n#### Undocumented request params\n\nTo make requests using undocumented parameters, you may use either the `SDK_PackageOptionName.WithQuerySet()`\nor the `SDK_PackageOptionName.WithJSONSet()` methods.\n\n```go\nparams := FooNewParams{\n ID: SDK_PackageName.F("id_xxxx"),\n Data: SDK_PackageName.F(FooNewParamsData{\n FirstName: SDK_PackageName.F("John"),\n }),\n}\nclient.Foo.New(context.Background(), params, SDK_PackageOptionName.WithJSONSet("data.last_name", "Doe"))\n```\n\n#### Undocumented response properties\n\nTo access undocumented response properties, you may either access the raw JSON of the response as a string\nwith `result.JSON.RawJSON()`, or get the raw JSON of a particular field on the result with\n`result.JSON.Foo.Raw()`.\n\nAny fields that are not present on the response struct will be saved and can be accessed by `result.JSON.ExtraFields()` which returns the extra fields as a `map[string]Field`.\n\n### Middleware\n\nWe provide `SDK_PackageOptionName.WithMiddleware` which applies the given\nmiddleware to requests.\n\n```go\nfunc Logger(req *http.Request, next SDK_PackageOptionName.MiddlewareNext) (res *http.Response, err error) {\n\t// Before the request\n\tstart := time.Now()\n\tLogReq(req)\n\n\t// Forward the request to the next handler\n\tres, err = next(req)\n\n\t// Handle stuff after the request\n\tend := time.Now()\n\tLogRes(res, err, start - end)\n\n return res, err\n}\n\nclient := SDK_PackageName.SDK_ClientInitializerName(\n\tSDK_PackageOptionName.WithMiddleware(Logger),\n)\n```\n\nWhen multiple middlewares are provided as variadic arguments, the middlewares\nare applied left to right. If `SDK_PackageOptionName.WithMiddleware` is given\nmultiple times, for example first in the client then the method, the\nmiddleware in the client will run first and the middleware given in the method\nwill run next.\n\nYou may also replace the default `http.Client` with\n`SDK_PackageOptionName.WithHTTPClient(client)`. Only one http client is\naccepted (this overwrites any previous client) and receives requests after any\nmiddleware has been applied.\n\n## Semantic versioning\n\nThis package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:\n\n1. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_\n2. Changes that we do not expect to impact the vast majority of users in practice.\n\nWe take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.\n\nWe are keen for your feedback; please open an [issue](https://www.github.com/beeper/desktop-api-go/issues) with questions, bugs, or suggestions.\n\n## Contributing\n\nSee [the contributing documentation](./CONTRIBUTING.md).\n',
},
{
language: 'php',
content:
- '# Beeper Desktop PHP API Library\n\nThe Beeper Desktop PHP library provides convenient access to the Beeper Desktop REST API from any PHP 8.1.0+ application.\n\n## Installation\n\nTo use this package, install via Composer by adding the following to your application\'s `composer.json`:\n\n\n```json\n{\n "repositories": [\n {\n "type": "vcs",\n "url": "git@github.com:beeper/desktop-api-php.git"\n }\n ],\n "require": {\n "beeper/desktop-api-php": "dev-main"\n }\n}\n```\n\n\n## Usage\n\n```php\nchats->search(includeMuted: true, limit: 3, type: \'single\');\n\nvar_dump($page->id);\n```',
+ '# Beeper Desktop PHP API Library\n\nThe Beeper Desktop PHP library provides convenient access to the Beeper Desktop REST API from any PHP 8.1.0+ application.\n\n## Installation\n\nTo use this package, install via Composer by adding the following to your application\'s `composer.json`:\n\n\n```json\n{\n "repositories": [\n {\n "type": "vcs",\n "url": "git@github.com:beeper/desktop-api-php.git"\n }\n ],\n "require": {\n "beeper/desktop-api-php": "dev-main"\n }\n}\n```\n\n\n## Usage\n\n```php\nchats->search(\n accountIDs: [\n \'matrix\', \'discordgo\', \'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc\'\n ],\n includeMuted: true,\n limit: 3,\n type: \'single\',\n);\n\nvar_dump($page->id);\n```',
},
{
language: 'python',
content:
- '# Beeper Desktop Python API library\n\n\n[)](https://pypi.org/project/beeper_desktop_api/)\n\nThe Beeper Desktop Python library provides convenient access to the Beeper Desktop REST API from any Python 3.9+\napplication. The library includes type definitions for all request params and response fields,\nand offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).\n\n\n\n\n\n## MCP Server\n\nUse the Beeper Desktop MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[](https://cursor.com/en-US/install-mcp?name=%40beeper%2Fdesktop-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBiZWVwZXIvZGVza3RvcC1tY3AiXSwiZW52Ijp7IkJFRVBFUl9BQ0NFU1NfVE9LRU4iOiJNeSBBY2Nlc3MgVG9rZW4ifX0)\n[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40beeper%2Fdesktop-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40beeper%2Fdesktop-mcp%22%5D%2C%22env%22%3A%7B%22BEEPER_ACCESS_TOKEN%22%3A%22My%20Access%20Token%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n## Documentation\n\nThe REST API documentation can be found on [developers.beeper.com](https://developers.beeper.com/desktop-api/). The full API of this library can be found in [api.md](api.md).\n\n## Installation\n\n```sh\n# install from the production repo\npip install git+ssh://git@github.com/beeper/desktop-api-python.git\n```\n> [!NOTE]\n> Once this package is [published to PyPI](https://www.stainless.com/docs/guides/publish), this will become: `pip install beeper_desktop_api`\n\n## Usage\n\nThe full API of this library can be found in [api.md](api.md).\n\n```python\nimport os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\n\npage = client.chats.search(\n include_muted=True,\n limit=3,\n type="single",\n)\nprint(page.items)\n```\n\nWhile you can provide a `access_token` keyword argument,\nwe recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)\nto add `BEEPER_ACCESS_TOKEN="My Access Token"` to your `.env` file\nso that your Access Token is not stored in source control.\n\n## Async usage\n\nSimply import `AsyncBeeperDesktop` instead of `BeeperDesktop` and use `await` with each API call:\n\n```python\nimport os\nimport asyncio\nfrom beeper_desktop_api import AsyncBeeperDesktop\n\nclient = AsyncBeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\n\nasync def main() -> None:\n page = await client.chats.search(\n include_muted=True,\n limit=3,\n type="single",\n )\n print(page.items)\n\nasyncio.run(main())\n```\n\nFunctionality between the synchronous and asynchronous clients is otherwise identical.\n\n### With aiohttp\n\nBy default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.\n\nYou can enable this by installing `aiohttp`:\n\n```sh\n# install from the production repo\npip install \'beeper_desktop_api[aiohttp] @ git+ssh://git@github.com/beeper/desktop-api-python.git\'\n```\n\nThen you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:\n\n```python\nimport os\nimport asyncio\nfrom beeper_desktop_api import DefaultAioHttpClient\nfrom beeper_desktop_api import AsyncBeeperDesktop\n\nasync def main() -> None:\n async with AsyncBeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n http_client=DefaultAioHttpClient(),\n) as client:\n page = await client.chats.search(\n include_muted=True,\n limit=3,\n type="single",\n )\n print(page.items)\n\nasyncio.run(main())\n```\n\n\n\n## Using types\n\nNested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:\n\n- Serializing back into JSON, `model.to_json()`\n- Converting to a dictionary, `model.to_dict()`\n\nTyped requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.\n\n## Pagination\n\nList methods in the Beeper Desktop API are paginated.\n\nThis library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually:\n\n```python\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop()\n\nall_messages = []\n# Automatically fetches more pages as needed.\nfor message in client.messages.search(\n account_ids=["local-telegram_ba_QFrb5lrLPhO3OT5MFBeTWv0x4BI"],\n limit=10,\n query="deployment",\n):\n # Do something with message here\n all_messages.append(message)\nprint(all_messages)\n```\n\nOr, asynchronously:\n\n```python\nimport asyncio\nfrom beeper_desktop_api import AsyncBeeperDesktop\n\nclient = AsyncBeeperDesktop()\n\nasync def main() -> None:\n all_messages = []\n # Iterate through items across all pages, issuing requests as needed.\n async for message in client.messages.search(\n account_ids=["local-telegram_ba_QFrb5lrLPhO3OT5MFBeTWv0x4BI"],\n limit=10,\n query="deployment",\n):\n all_messages.append(message)\n print(all_messages)\n\nasyncio.run(main())\n```\n\nAlternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages:\n\n```python\nfirst_page = await client.messages.search(\n account_ids=["local-telegram_ba_QFrb5lrLPhO3OT5MFBeTWv0x4BI"],\n limit=10,\n query="deployment",\n)\nif first_page.has_next_page():\n print(f"will fetch next page using these details: {first_page.next_page_info()}")\n next_page = await first_page.get_next_page()\n print(f"number of items we just fetched: {len(next_page.items)}")\n\n# Remove `await` for non-async usage.\n```\n\nOr just work directly with the returned data:\n\n```python\nfirst_page = await client.messages.search(\n account_ids=["local-telegram_ba_QFrb5lrLPhO3OT5MFBeTWv0x4BI"],\n limit=10,\n query="deployment",\n)\n\nprint(f"next page cursor: {first_page.oldest_cursor}") # => "next page cursor: ..."\nfor message in first_page.items:\n print(message.id)\n\n# Remove `await` for non-async usage.\n```\n\n## Nested params\n\nNested parameters are dictionaries, typed using `TypedDict`, for example:\n\n```python\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop()\n\nchat = client.chats.create(\n account_id="accountID",\n user={},\n)\nprint(chat.user)\n```\n\n## File uploads\n\nRequest parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`.\n\n```python\nfrom pathlib import Path\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop()\n\nclient.assets.upload(\n file=Path("/path/to/file"),\n)\n```\n\nThe async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically.\n\n## Handling errors\n\nWhen the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `beeper_desktop_api.APIConnectionError` is raised.\n\nWhen the API returns a non-success status code (that is, 4xx or 5xx\nresponse), a subclass of `beeper_desktop_api.APIStatusError` is raised, containing `status_code` and `response` properties.\n\nAll errors inherit from `beeper_desktop_api.APIError`.\n\n```python\nimport beeper_desktop_api\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop()\n\ntry:\n client.accounts.list()\nexcept beeper_desktop_api.APIConnectionError as e:\n print("The server could not be reached")\n print(e.__cause__) # an underlying Exception, likely raised within httpx.\nexcept beeper_desktop_api.RateLimitError as e:\n print("A 429 status code was received; we should back off a bit.")\nexcept beeper_desktop_api.APIStatusError as e:\n print("Another non-200-range status code was received")\n print(e.status_code)\n print(e.response)\n```\n\nError codes are as follows:\n\n| Status Code | Error Type |\n| ----------- | -------------------------- |\n| 400 | `BadRequestError` |\n| 401 | `AuthenticationError` |\n| 403 | `PermissionDeniedError` |\n| 404 | `NotFoundError` |\n| 422 | `UnprocessableEntityError` |\n| 429 | `RateLimitError` |\n| >=500 | `InternalServerError` |\n| N/A | `APIConnectionError` |\n\n### Retries\n\nCertain errors are automatically retried 2 times by default, with a short exponential backoff.\nConnection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,\n429 Rate Limit, and >=500 Internal errors are all retried by default.\n\nYou can use the `max_retries` option to configure or disable retry settings:\n\n```python\nfrom beeper_desktop_api import BeeperDesktop\n\n# Configure the default for all requests:\nclient = BeeperDesktop(\n # default is 2\n max_retries=0,\n)\n\n# Or, configure per-request:\nclient.with_options(max_retries = 5).accounts.list()\n```\n\n### Timeouts\n\nBy default requests time out after 30 seconds. You can configure this with a `timeout` option,\nwhich accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:\n\n```python\nfrom beeper_desktop_api import BeeperDesktop\n\n# Configure the default for all requests:\nclient = BeeperDesktop(\n # 20 seconds (default is 30 seconds)\n timeout=20.0,\n)\n\n# More granular control:\nclient = BeeperDesktop(\n timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),\n)\n\n# Override per-request:\nclient.with_options(timeout = 5.0).accounts.list()\n```\n\nOn timeout, an `APITimeoutError` is thrown.\n\nNote that requests that time out are [retried twice by default](#retries).\n\n\n\n## Advanced\n\n### Logging\n\nWe use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.\n\nYou can enable logging by setting the environment variable `BEEPER_LOG` to `info`.\n\n```shell\n$ export BEEPER_LOG=info\n```\n\nOr to `debug` for more verbose logging.\n\n### How to tell whether `None` means `null` or missing\n\nIn an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`:\n\n```py\nif response.my_field is None:\n if \'my_field\' not in response.model_fields_set:\n print(\'Got json like {}, without a "my_field" key present at all.\')\n else:\n print(\'Got json like {"my_field": null}.\')\n```\n\n### Accessing raw response data (e.g. headers)\n\nThe "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g.,\n\n```py\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop()\nresponse = client.accounts.with_raw_response.list()\nprint(response.headers.get(\'X-My-Header\'))\n\naccount = response.parse() # get the object that `accounts.list()` would have returned\nprint(account)\n```\n\nThese methods return an [`APIResponse`](https://github.com/beeper/desktop-api-python/tree/main/src/beeper_desktop_api/_response.py) object.\n\nThe async client returns an [`AsyncAPIResponse`](https://github.com/beeper/desktop-api-python/tree/main/src/beeper_desktop_api/_response.py) with the same structure, the only difference being `await`able methods for reading the response content.\n\n#### `.with_streaming_response`\n\nThe above interface eagerly reads the full response body when you make the request, which may not always be what you want.\n\nTo stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods.\n\n```python\nwith client.accounts.with_streaming_response.list() as response :\n print(response.headers.get(\'X-My-Header\'))\n\n for line in response.iter_lines():\n print(line)\n```\n\nThe context manager is required so that the response will reliably be closed.\n\n### Making custom/undocumented requests\n\nThis library is typed for convenient access to the documented API.\n\nIf you need to access undocumented endpoints, params, or response properties, the library can still be used.\n\n#### Undocumented endpoints\n\nTo make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other\nhttp verbs. Options on the client will be respected (such as retries) when making this request.\n\n```py\nimport httpx\n\nresponse = client.post(\n "/foo",\n cast_to=httpx.Response,\n body={"my_param": True},\n)\n\nprint(response.headers.get("x-foo"))\n```\n\n#### Undocumented request params\n\nIf you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request\noptions.\n\n#### Undocumented response properties\n\nTo access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You\ncan also get all the extra fields on the Pydantic model as a dict with\n[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra).\n\n### Configuring the HTTP client\n\nYou can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including:\n\n- Support for [proxies](https://www.python-httpx.org/advanced/proxies/)\n- Custom [transports](https://www.python-httpx.org/advanced/transports/)\n- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality\n\n```python\nimport httpx\nfrom beeper_desktop_api import BeeperDesktop, DefaultHttpxClient\n\nclient = BeeperDesktop(\n # Or use the `BEEPER_BASE_URL` env var\n base_url="http://my.test.server.example.com:8083",\n http_client=DefaultHttpxClient(proxy="http://my.test.proxy.example.com", transport=httpx.HTTPTransport(local_address="0.0.0.0")),\n)\n```\n\nYou can also customize the client on a per-request basis by using `with_options()`:\n\n```python\nclient.with_options(http_client=DefaultHttpxClient(...))\n```\n\n### Managing HTTP resources\n\nBy default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting.\n\n```py\nfrom beeper_desktop_api import BeeperDesktop\n\nwith BeeperDesktop() as client:\n # make requests here\n ...\n\n# HTTP client is now closed\n```\n\n## Versioning\n\nThis package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:\n\n1. Changes that only affect static types, without breaking runtime behavior.\n2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_\n3. Changes that we do not expect to impact the vast majority of users in practice.\n\nWe take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.\n\nWe are keen for your feedback; please open an [issue](https://www.github.com/beeper/desktop-api-python/issues) with questions, bugs, or suggestions.\n\n### Determining the installed version\n\nIf you\'ve upgraded to the latest version but aren\'t seeing any new features you were expecting then your python environment is likely still using an older version.\n\nYou can determine the version that is being used at runtime with:\n\n```py\nimport beeper_desktop_api\nprint(beeper_desktop_api.__version__)\n```\n\n## Requirements\n\nPython 3.9 or higher.\n\n## Contributing\n\nSee [the contributing documentation](./CONTRIBUTING.md).\n',
+ '# Beeper Desktop Python API library\n\n\n[)](https://pypi.org/project/beeper_desktop_api/)\n\nThe Beeper Desktop Python library provides convenient access to the Beeper Desktop REST API from any Python 3.9+\napplication. The library includes type definitions for all request params and response fields,\nand offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).\n\n\n\n\n\n## MCP Server\n\nUse the Beeper Desktop MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[](https://cursor.com/en-US/install-mcp?name=%40beeper%2Fdesktop-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBiZWVwZXIvZGVza3RvcC1tY3AiXSwiZW52Ijp7IkJFRVBFUl9BQ0NFU1NfVE9LRU4iOiJNeSBBY2Nlc3MgVG9rZW4ifX0)\n[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40beeper%2Fdesktop-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40beeper%2Fdesktop-mcp%22%5D%2C%22env%22%3A%7B%22BEEPER_ACCESS_TOKEN%22%3A%22My%20Access%20Token%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n## Documentation\n\nThe REST API documentation can be found on [developers.beeper.com](https://developers.beeper.com/desktop-api/). The full API of this library can be found in [api.md](api.md).\n\n## Installation\n\n```sh\n# install from the production repo\npip install git+ssh://git@github.com/beeper/desktop-api-python.git\n```\n> [!NOTE]\n> Once this package is [published to PyPI](https://www.stainless.com/docs/guides/publish), this will become: `pip install beeper_desktop_api`\n\n## Usage\n\nThe full API of this library can be found in [api.md](api.md).\n\n```python\nimport os\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\n\npage = client.chats.search(\n account_ids=["matrix", "discordgo", "local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc"],\n include_muted=True,\n limit=3,\n type="single",\n)\nprint(page.items)\n```\n\nWhile you can provide a `access_token` keyword argument,\nwe recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)\nto add `BEEPER_ACCESS_TOKEN="My Access Token"` to your `.env` file\nso that your Access Token is not stored in source control.\n\n## Async usage\n\nSimply import `AsyncBeeperDesktop` instead of `BeeperDesktop` and use `await` with each API call:\n\n```python\nimport os\nimport asyncio\nfrom beeper_desktop_api import AsyncBeeperDesktop\n\nclient = AsyncBeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n)\n\nasync def main() -> None:\n page = await client.chats.search(\n account_ids=["matrix", "discordgo", "local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc"],\n include_muted=True,\n limit=3,\n type="single",\n )\n print(page.items)\n\nasyncio.run(main())\n```\n\nFunctionality between the synchronous and asynchronous clients is otherwise identical.\n\n### With aiohttp\n\nBy default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.\n\nYou can enable this by installing `aiohttp`:\n\n```sh\n# install from the production repo\npip install \'beeper_desktop_api[aiohttp] @ git+ssh://git@github.com/beeper/desktop-api-python.git\'\n```\n\nThen you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:\n\n```python\nimport os\nimport asyncio\nfrom beeper_desktop_api import DefaultAioHttpClient\nfrom beeper_desktop_api import AsyncBeeperDesktop\n\nasync def main() -> None:\n async with AsyncBeeperDesktop(\n access_token=os.environ.get("BEEPER_ACCESS_TOKEN"), # This is the default and can be omitted\n http_client=DefaultAioHttpClient(),\n) as client:\n page = await client.chats.search(\n account_ids=["matrix", "discordgo", "local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc"],\n include_muted=True,\n limit=3,\n type="single",\n )\n print(page.items)\n\nasyncio.run(main())\n```\n\n\n\n## Using types\n\nNested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:\n\n- Serializing back into JSON, `model.to_json()`\n- Converting to a dictionary, `model.to_dict()`\n\nTyped requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.\n\n## Pagination\n\nList methods in the Beeper Desktop API are paginated.\n\nThis library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually:\n\n```python\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop()\n\nall_messages = []\n# Automatically fetches more pages as needed.\nfor message in client.messages.search(\n account_ids=["discordgo", "local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc"],\n limit=10,\n query="oauth",\n):\n # Do something with message here\n all_messages.append(message)\nprint(all_messages)\n```\n\nOr, asynchronously:\n\n```python\nimport asyncio\nfrom beeper_desktop_api import AsyncBeeperDesktop\n\nclient = AsyncBeeperDesktop()\n\nasync def main() -> None:\n all_messages = []\n # Iterate through items across all pages, issuing requests as needed.\n async for message in client.messages.search(\n account_ids=["discordgo", "local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc"],\n limit=10,\n query="oauth",\n):\n all_messages.append(message)\n print(all_messages)\n\nasyncio.run(main())\n```\n\nAlternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages:\n\n```python\nfirst_page = await client.messages.search(\n account_ids=["discordgo", "local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc"],\n limit=10,\n query="oauth",\n)\nif first_page.has_next_page():\n print(f"will fetch next page using these details: {first_page.next_page_info()}")\n next_page = await first_page.get_next_page()\n print(f"number of items we just fetched: {len(next_page.items)}")\n\n# Remove `await` for non-async usage.\n```\n\nOr just work directly with the returned data:\n\n```python\nfirst_page = await client.messages.search(\n account_ids=["discordgo", "local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc"],\n limit=10,\n query="oauth",\n)\n\nprint(f"next page cursor: {first_page.oldest_cursor}") # => "next page cursor: ..."\nfor message in first_page.items:\n print(message.id)\n\n# Remove `await` for non-async usage.\n```\n\n## Nested params\n\nNested parameters are dictionaries, typed using `TypedDict`, for example:\n\n```python\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop()\n\nchat = client.chats.update(\n chat_id="!NCdzlIaMjZUmvmvyHU:beeper.com",\n draft={\n "text": "text"\n },\n)\nprint(chat.draft)\n```\n\n## File uploads\n\nRequest parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`.\n\n```python\nfrom pathlib import Path\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop()\n\nclient.assets.upload(\n file=Path("/path/to/file"),\n)\n```\n\nThe async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically.\n\n## Handling errors\n\nWhen the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `beeper_desktop_api.APIConnectionError` is raised.\n\nWhen the API returns a non-success status code (that is, 4xx or 5xx\nresponse), a subclass of `beeper_desktop_api.APIStatusError` is raised, containing `status_code` and `response` properties.\n\nAll errors inherit from `beeper_desktop_api.APIError`.\n\n```python\nimport beeper_desktop_api\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop()\n\ntry:\n client.accounts.list()\nexcept beeper_desktop_api.APIConnectionError as e:\n print("The server could not be reached")\n print(e.__cause__) # an underlying Exception, likely raised within httpx.\nexcept beeper_desktop_api.RateLimitError as e:\n print("A 429 status code was received; we should back off a bit.")\nexcept beeper_desktop_api.APIStatusError as e:\n print("Another non-200-range status code was received")\n print(e.status_code)\n print(e.response)\n```\n\nError codes are as follows:\n\n| Status Code | Error Type |\n| ----------- | -------------------------- |\n| 400 | `BadRequestError` |\n| 401 | `AuthenticationError` |\n| 403 | `PermissionDeniedError` |\n| 404 | `NotFoundError` |\n| 422 | `UnprocessableEntityError` |\n| 429 | `RateLimitError` |\n| >=500 | `InternalServerError` |\n| N/A | `APIConnectionError` |\n\n### Retries\n\nCertain errors are automatically retried 2 times by default, with a short exponential backoff.\nConnection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,\n429 Rate Limit, and >=500 Internal errors are all retried by default.\n\nYou can use the `max_retries` option to configure or disable retry settings:\n\n```python\nfrom beeper_desktop_api import BeeperDesktop\n\n# Configure the default for all requests:\nclient = BeeperDesktop(\n # default is 2\n max_retries=0,\n)\n\n# Or, configure per-request:\nclient.with_options(max_retries = 5).accounts.list()\n```\n\n### Timeouts\n\nBy default requests time out after 30 seconds. You can configure this with a `timeout` option,\nwhich accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object:\n\n```python\nfrom beeper_desktop_api import BeeperDesktop\n\n# Configure the default for all requests:\nclient = BeeperDesktop(\n # 20 seconds (default is 30 seconds)\n timeout=20.0,\n)\n\n# More granular control:\nclient = BeeperDesktop(\n timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),\n)\n\n# Override per-request:\nclient.with_options(timeout = 5.0).accounts.list()\n```\n\nOn timeout, an `APITimeoutError` is thrown.\n\nNote that requests that time out are [retried twice by default](#retries).\n\n\n\n## Advanced\n\n### Logging\n\nWe use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module.\n\nYou can enable logging by setting the environment variable `BEEPER_LOG` to `info`.\n\n```shell\n$ export BEEPER_LOG=info\n```\n\nOr to `debug` for more verbose logging.\n\n### How to tell whether `None` means `null` or missing\n\nIn an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`:\n\n```py\nif response.my_field is None:\n if \'my_field\' not in response.model_fields_set:\n print(\'Got json like {}, without a "my_field" key present at all.\')\n else:\n print(\'Got json like {"my_field": null}.\')\n```\n\n### Accessing raw response data (e.g. headers)\n\nThe "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g.,\n\n```py\nfrom beeper_desktop_api import BeeperDesktop\n\nclient = BeeperDesktop()\nresponse = client.accounts.with_raw_response.list()\nprint(response.headers.get(\'X-My-Header\'))\n\naccount = response.parse() # get the object that `accounts.list()` would have returned\nprint(account)\n```\n\nThese methods return an [`APIResponse`](https://github.com/beeper/desktop-api-python/tree/main/src/beeper_desktop_api/_response.py) object.\n\nThe async client returns an [`AsyncAPIResponse`](https://github.com/beeper/desktop-api-python/tree/main/src/beeper_desktop_api/_response.py) with the same structure, the only difference being `await`able methods for reading the response content.\n\n#### `.with_streaming_response`\n\nThe above interface eagerly reads the full response body when you make the request, which may not always be what you want.\n\nTo stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods.\n\n```python\nwith client.accounts.with_streaming_response.list() as response :\n print(response.headers.get(\'X-My-Header\'))\n\n for line in response.iter_lines():\n print(line)\n```\n\nThe context manager is required so that the response will reliably be closed.\n\n### Making custom/undocumented requests\n\nThis library is typed for convenient access to the documented API.\n\nIf you need to access undocumented endpoints, params, or response properties, the library can still be used.\n\n#### Undocumented endpoints\n\nTo make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other\nhttp verbs. Options on the client will be respected (such as retries) when making this request.\n\n```py\nimport httpx\n\nresponse = client.post(\n "/foo",\n cast_to=httpx.Response,\n body={"my_param": True},\n)\n\nprint(response.headers.get("x-foo"))\n```\n\n#### Undocumented request params\n\nIf you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request\noptions.\n\n#### Undocumented response properties\n\nTo access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You\ncan also get all the extra fields on the Pydantic model as a dict with\n[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra).\n\n### Configuring the HTTP client\n\nYou can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including:\n\n- Support for [proxies](https://www.python-httpx.org/advanced/proxies/)\n- Custom [transports](https://www.python-httpx.org/advanced/transports/)\n- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality\n\n```python\nimport httpx\nfrom beeper_desktop_api import BeeperDesktop, DefaultHttpxClient\n\nclient = BeeperDesktop(\n # Or use the `BEEPER_BASE_URL` env var\n base_url="http://my.test.server.example.com:8083",\n http_client=DefaultHttpxClient(proxy="http://my.test.proxy.example.com", transport=httpx.HTTPTransport(local_address="0.0.0.0")),\n)\n```\n\nYou can also customize the client on a per-request basis by using `with_options()`:\n\n```python\nclient.with_options(http_client=DefaultHttpxClient(...))\n```\n\n### Managing HTTP resources\n\nBy default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting.\n\n```py\nfrom beeper_desktop_api import BeeperDesktop\n\nwith BeeperDesktop() as client:\n # make requests here\n ...\n\n# HTTP client is now closed\n```\n\n## Versioning\n\nThis package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:\n\n1. Changes that only affect static types, without breaking runtime behavior.\n2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_\n3. Changes that we do not expect to impact the vast majority of users in practice.\n\nWe take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.\n\nWe are keen for your feedback; please open an [issue](https://www.github.com/beeper/desktop-api-python/issues) with questions, bugs, or suggestions.\n\n### Determining the installed version\n\nIf you\'ve upgraded to the latest version but aren\'t seeing any new features you were expecting then your python environment is likely still using an older version.\n\nYou can determine the version that is being used at runtime with:\n\n```py\nimport beeper_desktop_api\nprint(beeper_desktop_api.__version__)\n```\n\n## Requirements\n\nPython 3.9 or higher.\n\n## Contributing\n\nSee [the contributing documentation](./CONTRIBUTING.md).\n',
},
{
language: 'typescript',
content:
- "# TypeScript SDK for Beeper Desktop API Library\n\n[)](https://npmjs.org/package/@beeper/desktop-api) \n\nThis library provides convenient access to the Beeper Desktop REST API from server-side TypeScript or JavaScript.\n\n\n\nThe REST API documentation can be found on [developers.beeper.com](https://developers.beeper.com/desktop-api/). The full API of this library can be found in [api.md](api.md).\n\n\n\n## MCP Server\n\nUse the Beeper Desktop MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[](https://cursor.com/en-US/install-mcp?name=%40beeper%2Fdesktop-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBiZWVwZXIvZGVza3RvcC1tY3AiXSwiZW52Ijp7IkJFRVBFUl9BQ0NFU1NfVE9LRU4iOiJNeSBBY2Nlc3MgVG9rZW4ifX0)\n[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40beeper%2Fdesktop-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40beeper%2Fdesktop-mcp%22%5D%2C%22env%22%3A%7B%22BEEPER_ACCESS_TOKEN%22%3A%22My%20Access%20Token%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n## Installation\n\n```sh\nnpm install @beeper/desktop-api\n```\n\n\n\n## Usage\n\nThe full API of this library can be found in [api.md](api.md).\n\n\n```js\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst page = await client.chats.search({\n includeMuted: true,\n limit: 3,\n type: 'single',\n});\nconst chat = page.items[0];\n\nconsole.log(chat.id);\n```\n\n\n\n### Request & Response types\n\nThis library includes TypeScript definitions for all request params and response fields. You may import and use them like so:\n\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst accounts: BeeperDesktop.AccountListResponse = await client.accounts.list();\n```\n\nDocumentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors.\n\n## File uploads\n\nRequest parameters that correspond to file uploads can be passed in many different forms:\n- `File` (or an object with the same structure)\n- a `fetch` `Response` (or an object with the same structure)\n- an `fs.ReadStream`\n- the return value of our `toFile` helper\n\n```ts\nimport fs from 'fs';\nimport BeeperDesktop, { toFile } from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\n// If you have access to Node `fs` we recommend using `fs.createReadStream()`:\nawait client.assets.upload({ file: fs.createReadStream('/path/to/file') });\n\n// Or if you have the web `File` API you can pass a `File` instance:\nawait client.assets.upload({ file: new File(['my bytes'], 'file') });\n\n// You can also pass a `fetch` `Response`:\nawait client.assets.upload({ file: await fetch('https://somesite/file') });\n\n// Finally, if none of the above are convenient, you can use our `toFile` helper:\nawait client.assets.upload({ file: await toFile(Buffer.from('my bytes'), 'file') });\nawait client.assets.upload({ file: await toFile(new Uint8Array([0, 1, 2]), 'file') });\n```\n\n\n\n## Handling errors\n\nWhen the library is unable to connect to the API,\nor if the API returns a non-success status code (i.e., 4xx or 5xx response),\na subclass of `APIError` will be thrown:\n\n\n```ts\nconst accounts = await client.accounts.list().catch(async (err) => {\n if (err instanceof BeeperDesktop.APIError) {\n console.log(err.status); // 400\n console.log(err.name); // BadRequestError\n console.log(err.headers); // {server: 'nginx', ...}\n } else {\n throw err;\n }\n});\n```\n\nError codes are as follows:\n\n| Status Code | Error Type |\n| ----------- | -------------------------- |\n| 400 | `BadRequestError` |\n| 401 | `AuthenticationError` |\n| 403 | `PermissionDeniedError` |\n| 404 | `NotFoundError` |\n| 422 | `UnprocessableEntityError` |\n| 429 | `RateLimitError` |\n| >=500 | `InternalServerError` |\n| N/A | `APIConnectionError` |\n\n### Retries\n\nCertain errors will be automatically retried 2 times by default, with a short exponential backoff.\nConnection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,\n429 Rate Limit, and >=500 Internal errors will all be retried by default.\n\nYou can use the `maxRetries` option to configure or disable this:\n\n\n```js\n// Configure the default for all requests:\nconst client = new BeeperDesktop({\n maxRetries: 0, // default is 2\n});\n\n// Or, configure per-request:\nawait client.accounts.list({\n maxRetries: 5,\n});\n```\n\n### Timeouts\n\nRequests time out after 30 seconds by default. You can configure this with a `timeout` option:\n\n\n```ts\n// Configure the default for all requests:\nconst client = new BeeperDesktop({\n timeout: 20 * 1000, // 20 seconds (default is 30 seconds)\n});\n\n// Override per-request:\nawait client.accounts.list({\n timeout: 5 * 1000,\n});\n```\n\nOn timeout, an `APIConnectionTimeoutError` is thrown.\n\nNote that requests which time out will be [retried twice by default](#retries).\n\n## Auto-pagination\n\nList methods in the BeeperDesktop API are paginated.\nYou can use the `for await … of` syntax to iterate through items across all pages:\n\n```ts\nasync function fetchAllMessages(params) {\n const allMessages = [];\n // Automatically fetches more pages as needed.\n for await (const message of client.messages.search({\n accountIDs: ['local-telegram_ba_QFrb5lrLPhO3OT5MFBeTWv0x4BI'],\n limit: 10,\n query: 'deployment',\n })) {\n allMessages.push(message);\n }\n return allMessages;\n}\n```\n\nAlternatively, you can request a single page at a time:\n\n```ts\nlet page = await client.messages.search({\n accountIDs: ['local-telegram_ba_QFrb5lrLPhO3OT5MFBeTWv0x4BI'],\n limit: 10,\n query: 'deployment',\n});\nfor (const message of page.items) {\n console.log(message);\n}\n\n// Convenience methods are provided for manually paginating:\nwhile (page.hasNextPage()) {\n page = await page.getNextPage();\n // ...\n}\n```\n\n\n\n## Advanced Usage\n\n### Tree shaking\n\nThis library supports tree shaking to reduce bundle size. Instead of importing the full client, you can create a client only including the API resources you need:\n\n~~~ts\nimport { createClient } from '@beeper/desktop-api/tree-shakable';\nimport { Accounts } from '@beeper/desktop-api/resources/accounts/accounts';\nimport { BaseChats } from '@beeper/desktop-api/resources/chats/chats';\n\nconst client = createClient({\n // Specify the resources you'd like to use ...\n resources: [Accounts, BaseChats],\n});\n\n// ... then make API calls as usual.\nconst accounts = await client.accounts.list();\nconst chat = await client.chats.retrieve('!NCdzlIaMjZUmvmvyHU:beeper.com');\n~~~\n\nEach API resource has two versions, the full resource (e.g., `Accounts`) which includes all subresources, and the base resource (e.g., `BaseAccounts`) which does not.\n\nThe tree-shaken client is fully typed, so TypeScript will provide accurate autocomplete and prevent access to resources not included in your configuration.\nThe `createClient` function automatically infers the correct type, but you can also use the `PartialBeeperDesktop` type explicitly:\n\n~~~ts\nimport BeeperDesktop from '@beeper/desktop-api';\nimport { createClient, type PartialBeeperDesktop } from '@beeper/desktop-api/tree-shakable';\nimport { BaseAccounts } from '@beeper/desktop-api/resources/accounts/accounts';\n\n// Explicit variable type\nconst client: PartialBeeperDesktop<{ accounts: BaseAccounts }> = createClient({\n resources: [BaseAccounts],\n /* ... */\n});\n\n// Function parameter type\nasync function main(client: PartialBeeperDesktop<{ accounts: BaseAccounts }>) {\n const accounts = await client.accounts.list();\n}\n\n// Works with any client that has the accounts resource\nconst treeShakableClient = createClient({\n resources: [BaseAccounts],\n /* ... */\n});\nconst fullClient = new BeeperDesktop(/* ... */);\n\nmain(treeShakableClient); // Works\nmain(fullClient); // Also works\n~~~\n\n### Accessing raw Response data (e.g., headers)\n\nThe \"raw\" `Response` returned by `fetch()` can be accessed through the `.asResponse()` method on the `APIPromise` type that all methods return.\nThis method returns as soon as the headers for a successful response are received and does not consume the response body, so you are free to write custom parsing or streaming logic.\n\nYou can also use the `.withResponse()` method to get the raw `Response` along with the parsed data.\nUnlike `.asResponse()` this method consumes the body, returning once it is parsed.\n\n\n```ts\nconst client = new BeeperDesktop();\n\nconst response = await client.accounts.list().asResponse();\nconsole.log(response.headers.get('X-My-Header'));\nconsole.log(response.statusText); // access the underlying Response object\n\nconst { data: accounts, response: raw } = await client.accounts.list().withResponse();\nconsole.log(raw.headers.get('X-My-Header'));\nconsole.log(accounts);\n```\n\n### Logging\n\n> [!IMPORTANT]\n> All log messages are intended for debugging only. The format and content of log messages\n> may change between releases.\n\n#### Log levels\n\nThe log level can be configured in two ways:\n\n1. Via the `BEEPER_LOG` environment variable\n2. Using the `logLevel` client option (overrides the environment variable if set)\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n logLevel: 'debug', // Show all log messages\n});\n```\n\nAvailable log levels, from most to least verbose:\n\n- `'debug'` - Show debug messages, info, warnings, and errors\n- `'info'` - Show info messages, warnings, and errors\n- `'warn'` - Show warnings and errors (default)\n- `'error'` - Show only errors\n- `'off'` - Disable all logging\n\nAt the `'debug'` level, all HTTP requests and responses are logged, including headers and bodies.\nSome authentication-related headers are redacted, but sensitive data in request and response bodies\nmay still be visible.\n\n#### Custom logger\n\nBy default, this library logs to `globalThis.console`. You can also provide a custom logger.\nMost logging libraries are supported, including [pino](https://www.npmjs.com/package/pino), [winston](https://www.npmjs.com/package/winston), [bunyan](https://www.npmjs.com/package/bunyan), [consola](https://www.npmjs.com/package/consola), [signale](https://www.npmjs.com/package/signale), and [@std/log](https://jsr.io/@std/log). If your logger doesn't work, please open an issue.\n\nWhen providing a custom logger, the `logLevel` option still controls which messages are emitted, messages\nbelow the configured level will not be sent to your logger.\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\nimport pino from 'pino';\n\nconst logger = pino();\n\nconst client = new BeeperDesktop({\n logger: logger.child({ name: 'BeeperDesktop' }),\n logLevel: 'debug', // Send all messages to pino, allowing it to filter\n});\n```\n\n### Making custom/undocumented requests\n\nThis library is typed for convenient access to the documented API. If you need to access undocumented\nendpoints, params, or response properties, the library can still be used.\n\n#### Undocumented endpoints\n\nTo make requests to undocumented endpoints, you can use `client.get`, `client.post`, and other HTTP verbs.\nOptions on the client, such as retries, will be respected when making these requests.\n\n```ts\nawait client.post('/some/path', {\n body: { some_prop: 'foo' },\n query: { some_query_arg: 'bar' },\n});\n```\n\n#### Undocumented request params\n\nTo make requests using undocumented parameters, you may use `// @ts-expect-error` on the undocumented\nparameter. This library doesn't validate at runtime that the request matches the type, so any extra values you\nsend will be sent as-is.\n\n```ts\nclient.chats.search({\n // ...\n // @ts-expect-error baz is not yet public\n baz: 'undocumented option',\n});\n```\n\nFor requests with the `GET` verb, any extra params will be in the query, all other requests will send the\nextra param in the body.\n\nIf you want to explicitly send an extra argument, you can do so with the `query`, `body`, and `headers` request\noptions.\n\n#### Undocumented response properties\n\nTo access undocumented response properties, you may access the response object with `// @ts-expect-error` on\nthe response object, or cast the response object to the requisite type. Like the request params, we do not\nvalidate or strip extra properties from the response from the API.\n\n### Customizing the fetch client\n\nBy default, this library expects a global `fetch` function is defined.\n\nIf you want to use a different `fetch` function, you can either polyfill the global:\n\n```ts\nimport fetch from 'my-fetch';\n\nglobalThis.fetch = fetch;\n```\n\nOr pass it to the client:\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\nimport fetch from 'my-fetch';\n\nconst client = new BeeperDesktop({ fetch });\n```\n\n### Fetch options\n\nIf you want to set custom `fetch` options without overriding the `fetch` function, you can provide a `fetchOptions` object when instantiating the client or making a request. (Request-specific options override client options.)\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n fetchOptions: {\n // `RequestInit` options\n },\n});\n```\n\n#### Configuring proxies\n\nTo modify proxy behavior, you can provide custom `fetchOptions` that add runtime-specific proxy\noptions to requests:\n\n
**Node** [[docs](https://github.com/nodejs/undici/blob/main/docs/docs/api/ProxyAgent.md#example---proxyagent-with-fetch)]\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\nimport * as undici from 'undici';\n\nconst proxyAgent = new undici.ProxyAgent('http://localhost:8888');\nconst client = new BeeperDesktop({\n fetchOptions: {\n dispatcher: proxyAgent,\n },\n});\n```\n\n
**Bun** [[docs](https://bun.sh/guides/http/proxy)]\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n fetchOptions: {\n proxy: 'http://localhost:8888',\n },\n});\n```\n\n
**Deno** [[docs](https://docs.deno.com/api/deno/~/Deno.createHttpClient)]\n\n```ts\nimport BeeperDesktop from 'npm:@beeper/desktop-api';\n\nconst httpClient = Deno.createHttpClient({ proxy: { url: 'http://localhost:8888' } });\nconst client = new BeeperDesktop({\n fetchOptions: {\n client: httpClient,\n },\n});\n```\n\n## Frequently Asked Questions\n\n## Semantic versioning\n\nThis package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:\n\n1. Changes that only affect static types, without breaking runtime behavior.\n2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_\n3. Changes that we do not expect to impact the vast majority of users in practice.\n\nWe take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.\n\nWe are keen for your feedback; please open an [issue](https://www.github.com/beeper/desktop-api-js/issues) with questions, bugs, or suggestions.\n\n## Requirements\n\nTypeScript >= 4.9 is supported.\n\nThe following runtimes are supported:\n\n- Web browsers (Up-to-date Chrome, Firefox, Safari, Edge, and more)\n- Node.js 20 LTS or later ([non-EOL](https://endoflife.date/nodejs)) versions.\n- Deno v1.28.0 or higher.\n- Bun 1.0 or later.\n- Cloudflare Workers.\n- Vercel Edge Runtime.\n- Jest 28 or greater with the `\"node\"` environment (`\"jsdom\"` is not supported at this time).\n- Nitro v2.6 or greater.\n\nNote that React Native is not supported at this time.\n\nIf you are interested in other runtime environments, please open or upvote an issue on GitHub.\n\n## Contributing\n\nSee [the contributing documentation](./CONTRIBUTING.md).\n",
+ "# TypeScript SDK for Beeper Desktop API Library\n\n[)](https://npmjs.org/package/@beeper/desktop-api) \n\nThis library provides convenient access to the Beeper Desktop REST API from server-side TypeScript or JavaScript.\n\n\n\nThe REST API documentation can be found on [developers.beeper.com](https://developers.beeper.com/desktop-api/). The full API of this library can be found in [api.md](api.md).\n\n\n\n## MCP Server\n\nUse the Beeper Desktop MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.\n\n[](https://cursor.com/en-US/install-mcp?name=%40beeper%2Fdesktop-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBiZWVwZXIvZGVza3RvcC1tY3AiXSwiZW52Ijp7IkJFRVBFUl9BQ0NFU1NfVE9LRU4iOiJNeSBBY2Nlc3MgVG9rZW4ifX0)\n[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40beeper%2Fdesktop-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40beeper%2Fdesktop-mcp%22%5D%2C%22env%22%3A%7B%22BEEPER_ACCESS_TOKEN%22%3A%22My%20Access%20Token%22%7D%7D)\n\n> Note: You may need to set environment variables in your MCP client.\n\n## Installation\n\n```sh\nnpm install @beeper/desktop-api\n```\n\n\n\n## Usage\n\nThe full API of this library can be found in [api.md](api.md).\n\n\n```js\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst page = await client.chats.search({\n accountIDs: ['matrix', 'discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'],\n includeMuted: true,\n limit: 3,\n type: 'single',\n});\nconst chat = page.items[0];\n\nconsole.log(chat.id);\n```\n\n\n\n### Request & Response types\n\nThis library includes TypeScript definitions for all request params and response fields. You may import and use them like so:\n\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n accessToken: process.env['BEEPER_ACCESS_TOKEN'], // This is the default and can be omitted\n});\n\nconst accounts: BeeperDesktop.AccountListResponse = await client.accounts.list();\n```\n\nDocumentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors.\n\n## File uploads\n\nRequest parameters that correspond to file uploads can be passed in many different forms:\n- `File` (or an object with the same structure)\n- a `fetch` `Response` (or an object with the same structure)\n- an `fs.ReadStream`\n- the return value of our `toFile` helper\n\n```ts\nimport fs from 'fs';\nimport BeeperDesktop, { toFile } from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop();\n\n// If you have access to Node `fs` we recommend using `fs.createReadStream()`:\nawait client.assets.upload({ file: fs.createReadStream('/path/to/file') });\n\n// Or if you have the web `File` API you can pass a `File` instance:\nawait client.assets.upload({ file: new File(['my bytes'], 'file') });\n\n// You can also pass a `fetch` `Response`:\nawait client.assets.upload({ file: await fetch('https://somesite/file') });\n\n// Finally, if none of the above are convenient, you can use our `toFile` helper:\nawait client.assets.upload({ file: await toFile(Buffer.from('my bytes'), 'file') });\nawait client.assets.upload({ file: await toFile(new Uint8Array([0, 1, 2]), 'file') });\n```\n\n\n\n## Handling errors\n\nWhen the library is unable to connect to the API,\nor if the API returns a non-success status code (i.e., 4xx or 5xx response),\na subclass of `APIError` will be thrown:\n\n\n```ts\nconst accounts = await client.accounts.list().catch(async (err) => {\n if (err instanceof BeeperDesktop.APIError) {\n console.log(err.status); // 400\n console.log(err.name); // BadRequestError\n console.log(err.headers); // {server: 'nginx', ...}\n } else {\n throw err;\n }\n});\n```\n\nError codes are as follows:\n\n| Status Code | Error Type |\n| ----------- | -------------------------- |\n| 400 | `BadRequestError` |\n| 401 | `AuthenticationError` |\n| 403 | `PermissionDeniedError` |\n| 404 | `NotFoundError` |\n| 422 | `UnprocessableEntityError` |\n| 429 | `RateLimitError` |\n| >=500 | `InternalServerError` |\n| N/A | `APIConnectionError` |\n\n### Retries\n\nCertain errors will be automatically retried 2 times by default, with a short exponential backoff.\nConnection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,\n429 Rate Limit, and >=500 Internal errors will all be retried by default.\n\nYou can use the `maxRetries` option to configure or disable this:\n\n\n```js\n// Configure the default for all requests:\nconst client = new BeeperDesktop({\n maxRetries: 0, // default is 2\n});\n\n// Or, configure per-request:\nawait client.accounts.list({\n maxRetries: 5,\n});\n```\n\n### Timeouts\n\nRequests time out after 30 seconds by default. You can configure this with a `timeout` option:\n\n\n```ts\n// Configure the default for all requests:\nconst client = new BeeperDesktop({\n timeout: 20 * 1000, // 20 seconds (default is 30 seconds)\n});\n\n// Override per-request:\nawait client.accounts.list({\n timeout: 5 * 1000,\n});\n```\n\nOn timeout, an `APIConnectionTimeoutError` is thrown.\n\nNote that requests which time out will be [retried twice by default](#retries).\n\n## Auto-pagination\n\nList methods in the BeeperDesktop API are paginated.\nYou can use the `for await … of` syntax to iterate through items across all pages:\n\n```ts\nasync function fetchAllMessages(params) {\n const allMessages = [];\n // Automatically fetches more pages as needed.\n for await (const message of client.messages.search({\n accountIDs: ['discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'],\n limit: 10,\n query: 'oauth',\n })) {\n allMessages.push(message);\n }\n return allMessages;\n}\n```\n\nAlternatively, you can request a single page at a time:\n\n```ts\nlet page = await client.messages.search({\n accountIDs: ['discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'],\n limit: 10,\n query: 'oauth',\n});\nfor (const message of page.items) {\n console.log(message);\n}\n\n// Convenience methods are provided for manually paginating:\nwhile (page.hasNextPage()) {\n page = await page.getNextPage();\n // ...\n}\n```\n\n\n\n## Advanced Usage\n\n### Tree shaking\n\nThis library supports tree shaking to reduce bundle size. Instead of importing the full client, you can create a client only including the API resources you need:\n\n~~~ts\nimport { createClient } from '@beeper/desktop-api/tree-shakable';\nimport { Accounts } from '@beeper/desktop-api/resources/accounts/accounts';\nimport { BaseChats } from '@beeper/desktop-api/resources/chats/chats';\n\nconst client = createClient({\n // Specify the resources you'd like to use ...\n resources: [Accounts, BaseChats],\n});\n\n// ... then make API calls as usual.\nconst accounts = await client.accounts.list();\nconst chat = await client.chats.retrieve('!NCdzlIaMjZUmvmvyHU:beeper.com');\n~~~\n\nEach API resource has two versions, the full resource (e.g., `Accounts`) which includes all subresources, and the base resource (e.g., `BaseAccounts`) which does not.\n\nThe tree-shaken client is fully typed, so TypeScript will provide accurate autocomplete and prevent access to resources not included in your configuration.\nThe `createClient` function automatically infers the correct type, but you can also use the `PartialBeeperDesktop` type explicitly:\n\n~~~ts\nimport BeeperDesktop from '@beeper/desktop-api';\nimport { createClient, type PartialBeeperDesktop } from '@beeper/desktop-api/tree-shakable';\nimport { BaseAccounts } from '@beeper/desktop-api/resources/accounts/accounts';\n\n// Explicit variable type\nconst client: PartialBeeperDesktop<{ accounts: BaseAccounts }> = createClient({\n resources: [BaseAccounts],\n /* ... */\n});\n\n// Function parameter type\nasync function main(client: PartialBeeperDesktop<{ accounts: BaseAccounts }>) {\n const accounts = await client.accounts.list();\n}\n\n// Works with any client that has the accounts resource\nconst treeShakableClient = createClient({\n resources: [BaseAccounts],\n /* ... */\n});\nconst fullClient = new BeeperDesktop(/* ... */);\n\nmain(treeShakableClient); // Works\nmain(fullClient); // Also works\n~~~\n\n### Accessing raw Response data (e.g., headers)\n\nThe \"raw\" `Response` returned by `fetch()` can be accessed through the `.asResponse()` method on the `APIPromise` type that all methods return.\nThis method returns as soon as the headers for a successful response are received and does not consume the response body, so you are free to write custom parsing or streaming logic.\n\nYou can also use the `.withResponse()` method to get the raw `Response` along with the parsed data.\nUnlike `.asResponse()` this method consumes the body, returning once it is parsed.\n\n\n```ts\nconst client = new BeeperDesktop();\n\nconst response = await client.accounts.list().asResponse();\nconsole.log(response.headers.get('X-My-Header'));\nconsole.log(response.statusText); // access the underlying Response object\n\nconst { data: accounts, response: raw } = await client.accounts.list().withResponse();\nconsole.log(raw.headers.get('X-My-Header'));\nconsole.log(accounts);\n```\n\n### Logging\n\n> [!IMPORTANT]\n> All log messages are intended for debugging only. The format and content of log messages\n> may change between releases.\n\n#### Log levels\n\nThe log level can be configured in two ways:\n\n1. Via the `BEEPER_LOG` environment variable\n2. Using the `logLevel` client option (overrides the environment variable if set)\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n logLevel: 'debug', // Show all log messages\n});\n```\n\nAvailable log levels, from most to least verbose:\n\n- `'debug'` - Show debug messages, info, warnings, and errors\n- `'info'` - Show info messages, warnings, and errors\n- `'warn'` - Show warnings and errors (default)\n- `'error'` - Show only errors\n- `'off'` - Disable all logging\n\nAt the `'debug'` level, all HTTP requests and responses are logged, including headers and bodies.\nSome authentication-related headers are redacted, but sensitive data in request and response bodies\nmay still be visible.\n\n#### Custom logger\n\nBy default, this library logs to `globalThis.console`. You can also provide a custom logger.\nMost logging libraries are supported, including [pino](https://www.npmjs.com/package/pino), [winston](https://www.npmjs.com/package/winston), [bunyan](https://www.npmjs.com/package/bunyan), [consola](https://www.npmjs.com/package/consola), [signale](https://www.npmjs.com/package/signale), and [@std/log](https://jsr.io/@std/log). If your logger doesn't work, please open an issue.\n\nWhen providing a custom logger, the `logLevel` option still controls which messages are emitted, messages\nbelow the configured level will not be sent to your logger.\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\nimport pino from 'pino';\n\nconst logger = pino();\n\nconst client = new BeeperDesktop({\n logger: logger.child({ name: 'BeeperDesktop' }),\n logLevel: 'debug', // Send all messages to pino, allowing it to filter\n});\n```\n\n### Making custom/undocumented requests\n\nThis library is typed for convenient access to the documented API. If you need to access undocumented\nendpoints, params, or response properties, the library can still be used.\n\n#### Undocumented endpoints\n\nTo make requests to undocumented endpoints, you can use `client.get`, `client.post`, and other HTTP verbs.\nOptions on the client, such as retries, will be respected when making these requests.\n\n```ts\nawait client.post('/some/path', {\n body: { some_prop: 'foo' },\n query: { some_query_arg: 'bar' },\n});\n```\n\n#### Undocumented request params\n\nTo make requests using undocumented parameters, you may use `// @ts-expect-error` on the undocumented\nparameter. This library doesn't validate at runtime that the request matches the type, so any extra values you\nsend will be sent as-is.\n\n```ts\nclient.chats.search({\n // ...\n // @ts-expect-error baz is not yet public\n baz: 'undocumented option',\n});\n```\n\nFor requests with the `GET` verb, any extra params will be in the query, all other requests will send the\nextra param in the body.\n\nIf you want to explicitly send an extra argument, you can do so with the `query`, `body`, and `headers` request\noptions.\n\n#### Undocumented response properties\n\nTo access undocumented response properties, you may access the response object with `// @ts-expect-error` on\nthe response object, or cast the response object to the requisite type. Like the request params, we do not\nvalidate or strip extra properties from the response from the API.\n\n### Customizing the fetch client\n\nBy default, this library expects a global `fetch` function is defined.\n\nIf you want to use a different `fetch` function, you can either polyfill the global:\n\n```ts\nimport fetch from 'my-fetch';\n\nglobalThis.fetch = fetch;\n```\n\nOr pass it to the client:\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\nimport fetch from 'my-fetch';\n\nconst client = new BeeperDesktop({ fetch });\n```\n\n### Fetch options\n\nIf you want to set custom `fetch` options without overriding the `fetch` function, you can provide a `fetchOptions` object when instantiating the client or making a request. (Request-specific options override client options.)\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n fetchOptions: {\n // `RequestInit` options\n },\n});\n```\n\n#### Configuring proxies\n\nTo modify proxy behavior, you can provide custom `fetchOptions` that add runtime-specific proxy\noptions to requests:\n\n
**Node** [[docs](https://github.com/nodejs/undici/blob/main/docs/docs/api/ProxyAgent.md#example---proxyagent-with-fetch)]\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\nimport * as undici from 'undici';\n\nconst proxyAgent = new undici.ProxyAgent('http://localhost:8888');\nconst client = new BeeperDesktop({\n fetchOptions: {\n dispatcher: proxyAgent,\n },\n});\n```\n\n
**Bun** [[docs](https://bun.sh/guides/http/proxy)]\n\n```ts\nimport BeeperDesktop from '@beeper/desktop-api';\n\nconst client = new BeeperDesktop({\n fetchOptions: {\n proxy: 'http://localhost:8888',\n },\n});\n```\n\n
**Deno** [[docs](https://docs.deno.com/api/deno/~/Deno.createHttpClient)]\n\n```ts\nimport BeeperDesktop from 'npm:@beeper/desktop-api';\n\nconst httpClient = Deno.createHttpClient({ proxy: { url: 'http://localhost:8888' } });\nconst client = new BeeperDesktop({\n fetchOptions: {\n client: httpClient,\n },\n});\n```\n\n## Frequently Asked Questions\n\n## Semantic versioning\n\nThis package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:\n\n1. Changes that only affect static types, without breaking runtime behavior.\n2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_\n3. Changes that we do not expect to impact the vast majority of users in practice.\n\nWe take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.\n\nWe are keen for your feedback; please open an [issue](https://www.github.com/beeper/desktop-api-js/issues) with questions, bugs, or suggestions.\n\n## Requirements\n\nTypeScript >= 4.9 is supported.\n\nThe following runtimes are supported:\n\n- Web browsers (Up-to-date Chrome, Firefox, Safari, Edge, and more)\n- Node.js 20 LTS or later ([non-EOL](https://endoflife.date/nodejs)) versions.\n- Deno v1.28.0 or higher.\n- Bun 1.0 or later.\n- Cloudflare Workers.\n- Vercel Edge Runtime.\n- Jest 28 or greater with the `\"node\"` environment (`\"jsdom\"` is not supported at this time).\n- Nitro v2.6 or greater.\n\nNote that React Native is not supported at this time.\n\nIf you are interested in other runtime environments, please open or upvote an issue on GitHub.\n\n## Contributing\n\nSee [the contributing documentation](./CONTRIBUTING.md).\n",
},
];
diff --git a/packages/mcp-server/src/methods.ts b/packages/mcp-server/src/methods.ts
index 4269e66b..509a8540 100644
--- a/packages/mcp-server/src/methods.ts
+++ b/packages/mcp-server/src/methods.ts
@@ -52,6 +52,12 @@ export const sdkMethods: SdkMethod[] = [
httpMethod: 'get',
httpPath: '/v1/chats/{chatID}',
},
+ {
+ clientCallName: 'client.chats.update',
+ fullyQualifiedName: 'chats.update',
+ httpMethod: 'patch',
+ httpPath: '/v1/chats/{chatID}',
+ },
{
clientCallName: 'client.chats.list',
fullyQualifiedName: 'chats.list',
@@ -64,12 +70,36 @@ export const sdkMethods: SdkMethod[] = [
httpMethod: 'post',
httpPath: '/v1/chats/{chatID}/archive',
},
+ {
+ clientCallName: 'client.chats.markRead',
+ fullyQualifiedName: 'chats.markRead',
+ httpMethod: 'post',
+ httpPath: '/v1/chats/{chatID}/read',
+ },
+ {
+ clientCallName: 'client.chats.markUnread',
+ fullyQualifiedName: 'chats.markUnread',
+ httpMethod: 'post',
+ httpPath: '/v1/chats/{chatID}/unread',
+ },
+ {
+ clientCallName: 'client.chats.notifyAnyway',
+ fullyQualifiedName: 'chats.notifyAnyway',
+ httpMethod: 'post',
+ httpPath: '/v1/chats/{chatID}/notify-anyway',
+ },
{
clientCallName: 'client.chats.search',
fullyQualifiedName: 'chats.search',
httpMethod: 'get',
httpPath: '/v1/chats/search',
},
+ {
+ clientCallName: 'client.chats.start',
+ fullyQualifiedName: 'chats.start',
+ httpMethod: 'post',
+ httpPath: '/v1/chats/start',
+ },
{
clientCallName: 'client.chats.reminders.create',
fullyQualifiedName: 'chats.reminders.create',
@@ -86,7 +116,7 @@ export const sdkMethods: SdkMethod[] = [
clientCallName: 'client.chats.messages.reactions.delete',
fullyQualifiedName: 'chats.messages.reactions.delete',
httpMethod: 'delete',
- httpPath: '/v1/chats/{chatID}/messages/{messageID}/reactions',
+ httpPath: '/v1/chats/{chatID}/messages/{messageID}/reactions/{reactionKey}',
},
{
clientCallName: 'client.chats.messages.reactions.add',
@@ -94,6 +124,12 @@ export const sdkMethods: SdkMethod[] = [
httpMethod: 'post',
httpPath: '/v1/chats/{chatID}/messages/{messageID}/reactions',
},
+ {
+ clientCallName: 'client.messages.retrieve',
+ fullyQualifiedName: 'messages.retrieve',
+ httpMethod: 'get',
+ httpPath: '/v1/chats/{chatID}/messages/{messageID}',
+ },
{
clientCallName: 'client.messages.update',
fullyQualifiedName: 'messages.update',
@@ -106,6 +142,12 @@ export const sdkMethods: SdkMethod[] = [
httpMethod: 'get',
httpPath: '/v1/chats/{chatID}/messages',
},
+ {
+ clientCallName: 'client.messages.delete',
+ fullyQualifiedName: 'messages.delete',
+ httpMethod: 'delete',
+ httpPath: '/v1/chats/{chatID}/messages/{messageID}',
+ },
{
clientCallName: 'client.messages.search',
fullyQualifiedName: 'messages.search',
diff --git a/packages/mcp-server/src/options.ts b/packages/mcp-server/src/options.ts
index f1518764..c1b638c1 100644
--- a/packages/mcp-server/src/options.ts
+++ b/packages/mcp-server/src/options.ts
@@ -27,7 +27,7 @@ export type McpOptions = {
customInstructionsPath?: string | undefined;
};
-export type McpCodeExecutionMode = 'stainless-sandbox' | 'local';
+export type McpCodeExecutionMode = 'local';
export function parseCLIOptions(): CLIOptions {
const opts = yargs(hideBin(process.argv))
@@ -50,10 +50,10 @@ export function parseCLIOptions(): CLIOptions {
})
.option('code-execution-mode', {
type: 'string',
- choices: ['stainless-sandbox', 'local'],
- default: 'stainless-sandbox',
+ choices: ['local'],
+ default: 'local',
description:
- "Where to run code execution in code tool; 'stainless-sandbox' will execute code in Stainless-hosted sandboxes whereas 'local' will execute code locally on the MCP server machine.",
+ 'Where to run code execution in code tool; local will execute code locally on the MCP server machine.',
})
.option('custom-instructions-path', {
type: 'string',
diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts
index d5e01aeb..293ee947 100644
--- a/packages/mcp-server/src/server.ts
+++ b/packages/mcp-server/src/server.ts
@@ -28,7 +28,7 @@ export const newMcpServer = async ({
new McpServer(
{
name: 'beeper_desktop_api_api',
- version: '4.8.0',
+ version: '5.0.0',
},
{
instructions: await getInstructions({ stainlessApiKey, customInstructionsPath }),
@@ -182,7 +182,7 @@ export function selectTools(options?: McpOptions): McpTool[] {
includedTools.push(
codeTool({
blockedMethods: blockedMethodsForCodeTool(options),
- codeExecutionMode: options?.codeExecutionMode ?? 'stainless-sandbox',
+ codeExecutionMode: options?.codeExecutionMode ?? 'local',
}),
);
}
diff --git a/packages/mcp-server/tests/options.test.ts b/packages/mcp-server/tests/options.test.ts
index 17306295..dff9ed15 100644
--- a/packages/mcp-server/tests/options.test.ts
+++ b/packages/mcp-server/tests/options.test.ts
@@ -16,6 +16,7 @@ describe('parseCLIOptions', () => {
const result = parseCLIOptions();
expect(result.transport).toBe('stdio');
+ expect(result.codeExecutionMode).toBe('local');
cleanup();
});
diff --git a/scripts/detect-breaking-changes b/scripts/detect-breaking-changes
deleted file mode 100755
index 034131ce..00000000
--- a/scripts/detect-breaking-changes
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-
-cd "$(dirname "$0")/.."
-
-echo "==> Detecting breaking changes"
-
-TEST_PATHS=(
- tests/api-resources/top-level.test.ts
- tests/api-resources/accounts/accounts.test.ts
- tests/api-resources/accounts/contacts.test.ts
- tests/api-resources/chats/chats.test.ts
- tests/api-resources/chats/reminders.test.ts
- tests/api-resources/chats/messages/messages.test.ts
- tests/api-resources/chats/messages/reactions.test.ts
- tests/api-resources/messages.test.ts
- tests/api-resources/assets.test.ts
- tests/api-resources/info.test.ts
- tests/index.test.ts
-)
-
-for PATHSPEC in "${TEST_PATHS[@]}"; do
- # Try to check out previous versions of the test files
- # with the current SDK.
- git checkout "$1" -- "${PATHSPEC}" 2>/dev/null || true
-done
-
-# Instead of running the tests, use the linter to check if an
-# older test is no longer compatible with the latest SDK.
-./scripts/lint
diff --git a/src/client.ts b/src/client.ts
index 0edf0c47..90ff33ed 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -39,7 +39,9 @@ import {
} from './resources/assets';
import { Info, InfoRetrieveResponse } from './resources/info';
import {
+ MessageDeleteParams,
MessageListParams,
+ MessageRetrieveParams,
MessageSearchParams,
MessageSendParams,
MessageSendResponse,
@@ -56,8 +58,14 @@ import {
ChatListParams,
ChatListResponse,
ChatListResponsesCursorNoLimit,
+ ChatMarkReadParams,
+ ChatMarkUnreadParams,
+ ChatNotifyAnywayParams,
ChatRetrieveParams,
ChatSearchParams,
+ ChatStartParams,
+ ChatStartResponse,
+ ChatUpdateParams,
Chats,
ChatsCursorSearch,
} from './resources/chats/chats';
@@ -256,7 +264,7 @@ export class BaseBeeperDesktop {
/**
* Focus Beeper Desktop and optionally navigate to a specific chat, message, or
- * pre-fill draft text and attachment.
+ * pre-fill plain text and an image path.
*
* @example
* ```ts
@@ -854,7 +862,7 @@ export class BeeperDesktop extends BaseBeeperDesktop {
*/
assets: API.Assets = new API.Assets(this);
/**
- * Control the Beeper Desktop application
+ * Server discovery and capability metadata. Use /v1/info before authentication setup.
*/
info: API.Info = new API.Info(this);
}
@@ -891,21 +899,29 @@ export declare namespace BeeperDesktop {
type Chat as Chat,
type ChatCreateResponse as ChatCreateResponse,
type ChatListResponse as ChatListResponse,
+ type ChatStartResponse as ChatStartResponse,
type ChatListResponsesCursorNoLimit as ChatListResponsesCursorNoLimit,
type ChatsCursorSearch as ChatsCursorSearch,
type ChatCreateParams as ChatCreateParams,
type ChatRetrieveParams as ChatRetrieveParams,
+ type ChatUpdateParams as ChatUpdateParams,
type ChatListParams as ChatListParams,
type ChatArchiveParams as ChatArchiveParams,
+ type ChatMarkReadParams as ChatMarkReadParams,
+ type ChatMarkUnreadParams as ChatMarkUnreadParams,
+ type ChatNotifyAnywayParams as ChatNotifyAnywayParams,
type ChatSearchParams as ChatSearchParams,
+ type ChatStartParams as ChatStartParams,
};
export {
Messages as Messages,
type MessageUpdateResponse as MessageUpdateResponse,
type MessageSendResponse as MessageSendResponse,
+ type MessageRetrieveParams as MessageRetrieveParams,
type MessageUpdateParams as MessageUpdateParams,
type MessageListParams as MessageListParams,
+ type MessageDeleteParams as MessageDeleteParams,
type MessageSearchParams as MessageSearchParams,
type MessageSendParams as MessageSendParams,
};
diff --git a/src/resources/accounts/accounts.ts b/src/resources/accounts/accounts.ts
index c6e33e63..c3c997f0 100644
--- a/src/resources/accounts/accounts.ts
+++ b/src/resources/accounts/accounts.ts
@@ -20,8 +20,8 @@ export class BaseAccounts extends APIResource {
static override readonly _key: readonly ['accounts'] = Object.freeze(['accounts'] as const);
/**
- * Lists chat accounts across networks (WhatsApp, Telegram, Twitter/X, etc.)
- * actively connected to this Beeper Desktop instance
+ * List Chat Accounts connected to this Beeper Desktop instance, including bridge
+ * metadata and network identity.
*
* @example
* ```ts
@@ -44,12 +44,15 @@ export class Accounts extends BaseAccounts {
*/
export interface Account {
/**
- * Chat account added to Beeper. Use this to route account-scoped actions.
+ * Chat account added to Beeper. Use this to route account-scoped actions. Examples
+ * include matrix for Beeper/Matrix, discordgo for a cloud bridge,
+ * slackgo.TEAM-USER for workspace-scoped cloud bridges, and local-whatsapp*ba*...
+ * for local bridges.
*/
accountID: string;
/**
- * Bridge metadata for the account. Available in Beeper Desktop v4.2.789+.
+ * Bridge metadata for the account. Available in Beeper Desktop v4.2.785+.
*/
bridge: Account.Bridge;
@@ -67,21 +70,24 @@ export interface Account {
export namespace Account {
/**
- * Bridge metadata for the account. Available in Beeper Desktop v4.2.789+.
+ * Bridge metadata for the account. Available in Beeper Desktop v4.2.785+.
*/
export interface Bridge {
/**
- * Bridge instance identifier. Available in Beeper Desktop v4.2.789+.
+ * Bridge instance identifier. Matrix and cloud bridges often use the bridge type
+ * (for example matrix or discordgo); local bridges use a local bridge ID (for
+ * example local-whatsapp). Available in Beeper Desktop v4.2.785+.
*/
id: string;
/**
- * Bridge provider for the account. Available in Beeper Desktop v4.2.789+.
+ * Bridge provider for the account. Available in Beeper Desktop v4.2.785+.
*/
provider: 'cloud' | 'self-hosted' | 'local' | 'platform-sdk';
/**
- * Bridge type. Available in Beeper Desktop v4.2.789+.
+ * Bridge type, such as matrix, discordgo, slackgo, whatsapp, telegram, or twitter.
+ * Available in Beeper Desktop v4.2.785+.
*/
type: string;
}
diff --git a/src/resources/assets.ts b/src/resources/assets.ts
index 2281f83b..93203cf5 100644
--- a/src/resources/assets.ts
+++ b/src/resources/assets.ts
@@ -14,8 +14,8 @@ export class BaseAssets extends APIResource {
static override readonly _key: readonly ['assets'] = Object.freeze(['assets'] as const);
/**
- * Download a Matrix asset using its mxc:// or localmxc:// URL to the device
- * running Beeper Desktop and return the local file URL.
+ * Download a Matrix file using its mxc:// or localmxc:// URL to the device running
+ * Beeper Desktop and return the local file URL.
*
* @example
* ```ts
@@ -51,7 +51,8 @@ export class BaseAssets extends APIResource {
/**
* Upload a file to a temporary location using multipart/form-data. Returns an
- * uploadID that can be referenced when sending messages with attachments.
+ * uploadID that can be referenced when sending a message or materializing a draft
+ * attachment.
*
* @example
* ```ts
@@ -69,8 +70,8 @@ export class BaseAssets extends APIResource {
/**
* Upload a file using a JSON body with base64-encoded content. Returns an uploadID
- * that can be referenced when sending messages with attachments. Alternative to
- * the multipart upload endpoint.
+ * that can be referenced when sending a message or materializing a draft
+ * attachment. Alternative to the multipart upload endpoint.
*
* @example
* ```ts
@@ -98,7 +99,7 @@ export interface AssetDownloadResponse {
error?: string;
/**
- * Local file URL to the downloaded asset.
+ * Local file URL to the downloaded file.
*/
srcURL?: string;
}
@@ -135,12 +136,12 @@ export interface AssetUploadResponse {
mimeType?: string;
/**
- * Local file URL (file://) for the uploaded asset
+ * Local file URL (file://) for the uploaded file
*/
srcURL?: string;
/**
- * Unique upload ID for this asset
+ * Unique upload ID for this temporary file
*/
uploadID?: string;
@@ -182,12 +183,12 @@ export interface AssetUploadBase64Response {
mimeType?: string;
/**
- * Local file URL (file://) for the uploaded asset
+ * Local file URL (file://) for the uploaded file
*/
srcURL?: string;
/**
- * Unique upload ID for this asset
+ * Unique upload ID for this temporary file
*/
uploadID?: string;
@@ -199,14 +200,14 @@ export interface AssetUploadBase64Response {
export interface AssetDownloadParams {
/**
- * Matrix content URL (mxc:// or localmxc://) for the asset to download.
+ * Matrix content URL (mxc:// or localmxc://) for the file to download.
*/
url: string;
}
export interface AssetServeParams {
/**
- * Asset URL to serve. Accepts mxc://, localmxc://, or file:// URLs.
+ * File URL to serve. Accepts mxc://, localmxc://, or file:// URLs.
*/
url: string;
}
diff --git a/src/resources/chats/chats.ts b/src/resources/chats/chats.ts
index a6b40a91..03f7ade9 100644
--- a/src/resources/chats/chats.ts
+++ b/src/resources/chats/chats.ts
@@ -25,13 +25,14 @@ export class BaseChats extends APIResource {
static override readonly _key: readonly ['chats'] = Object.freeze(['chats'] as const);
/**
- * Create a direct or group chat with mode="create", or use mode="start" to resolve
- * a contact and open a direct chat.
+ * Create a direct or group chat from participant IDs. Returns the created chat.
*
* @example
* ```ts
* const chat = await client.chats.create({
* accountID: 'accountID',
+ * participantIDs: ['string'],
+ * type: 'single',
* });
* ```
*/
@@ -57,6 +58,26 @@ export class BaseChats extends APIResource {
return this._client.get(path`/v1/chats/${chatID}`, { query, ...options });
}
+ /**
+ * Update supported chat fields. Non-empty draft objects are accepted only when the
+ * current draft is empty. Send draft=null to clear the draft before setting new
+ * draft text or attachments.
+ *
+ * @example
+ * ```ts
+ * const chat = await client.chats.update(
+ * '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ * );
+ * ```
+ */
+ update(
+ chatID: string,
+ body: ChatUpdateParams | null | undefined = {},
+ options?: RequestOptions,
+ ): APIPromise {
+ return this._client.patch(path`/v1/chats/${chatID}`, { body, ...options });
+ }
+
/**
* List all chats sorted by last activity (most recent first). Combines all
* accounts into a single paginated list.
@@ -99,6 +120,61 @@ export class BaseChats extends APIResource {
});
}
+ /**
+ * Mark a chat as read, optionally through a specific message ID.
+ *
+ * @example
+ * ```ts
+ * const chat = await client.chats.markRead(
+ * '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ * );
+ * ```
+ */
+ markRead(
+ chatID: string,
+ body: ChatMarkReadParams | null | undefined = {},
+ options?: RequestOptions,
+ ): APIPromise {
+ return this._client.post(path`/v1/chats/${chatID}/read`, { body, ...options });
+ }
+
+ /**
+ * Mark a chat as unread, optionally from a specific message ID.
+ *
+ * @example
+ * ```ts
+ * const chat = await client.chats.markUnread(
+ * '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ * );
+ * ```
+ */
+ markUnread(
+ chatID: string,
+ body: ChatMarkUnreadParams | null | undefined = {},
+ options?: RequestOptions,
+ ): APIPromise {
+ return this._client.post(path`/v1/chats/${chatID}/unread`, { body, ...options });
+ }
+
+ /**
+ * Force a delivery notification when supported by the underlying network.
+ * Currently intended for iMessage on macOS; unsupported networks return an error.
+ *
+ * @example
+ * ```ts
+ * const chat = await client.chats.notifyAnyway(
+ * '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ * );
+ * ```
+ */
+ notifyAnyway(
+ chatID: string,
+ body?: ChatNotifyAnywayParams | null | undefined,
+ options?: RequestOptions,
+ ): APIPromise {
+ return this._client.post(path`/v1/chats/${chatID}/notify-anyway`, { body, ...options });
+ }
+
/**
* Search chats by title, network, or participant names.
*
@@ -116,6 +192,22 @@ export class BaseChats extends APIResource {
): PagePromise {
return this._client.getAPIList('/v1/chats/search', CursorSearch, { query, ...options });
}
+
+ /**
+ * Resolve a user/contact and open a direct chat. Reuses and returns an existing
+ * direct chat when one is found. Available in Beeper Desktop v4.2.808+.
+ *
+ * @example
+ * ```ts
+ * const response = await client.chats.start({
+ * accountID: 'accountID',
+ * user: {},
+ * });
+ * ```
+ */
+ start(body: ChatStartParams, options?: RequestOptions): APIPromise {
+ return this._client.post('/v1/chats/start', { body, ...options });
+ }
}
/**
* Manage chats
@@ -140,6 +232,11 @@ export interface Chat {
*/
accountID: string;
+ /**
+ * Display-only human-readable account/network name.
+ */
+ network: string;
+
/**
* Chat participants information.
*/
@@ -160,11 +257,41 @@ export interface Chat {
*/
unreadCount: number;
+ /**
+ * Chat capabilities reported by the platform.
+ */
+ capabilities?: Chat.Capabilities;
+
+ /**
+ * Group chat description/topic when available.
+ */
+ description?: string | null;
+
+ /**
+ * Current draft object for this chat, or null when no draft is set.
+ */
+ draft?: Chat.Draft | null;
+
+ /**
+ * Local filesystem path to the chat avatar image when available.
+ */
+ imgURL?: string | null;
+
/**
* True if chat is archived.
*/
isArchived?: boolean;
+ /**
+ * True if chat is marked low priority.
+ */
+ isLowPriority?: boolean;
+
+ /**
+ * True if the chat was explicitly marked unread by the authenticated user.
+ */
+ isMarkedUnread?: boolean;
+
/**
* True if chat notifications are muted.
*/
@@ -175,6 +302,11 @@ export interface Chat {
*/
isPinned?: boolean;
+ /**
+ * True if messages cannot be sent in this chat.
+ */
+ isReadOnly?: boolean;
+
/**
* Timestamp of last activity.
*/
@@ -189,6 +321,26 @@ export interface Chat {
* Local chat ID specific to this Beeper Desktop installation.
*/
localChatID?: string | null;
+
+ /**
+ * Disappearing-message timer in seconds when available.
+ */
+ messageExpirySeconds?: number | null;
+
+ /**
+ * Current reminder for this chat, or null when no reminder is set.
+ */
+ reminder?: Chat.Reminder | null;
+
+ /**
+ * Current snooze state for this chat, or null when no snooze is set.
+ */
+ snooze?: Chat.Snooze | null;
+
+ /**
+ * Number of unread messages that mention the authenticated user or @room.
+ */
+ unreadMentionsCount?: number;
}
export namespace Chat {
@@ -204,28 +356,502 @@ export namespace Chat {
/**
* Participants returned for this chat (limited by the request; may be a subset).
*/
- items: Array;
+ items: Array;
/**
* Total number of participants in the chat.
*/
total: number;
}
+
+ export namespace Participants {
+ /**
+ * A chat participant. Extends User with chat membership metadata.
+ */
+ export interface Item extends Shared.User {
+ /**
+ * True if this participant has admin privileges in the chat.
+ */
+ isAdmin?: boolean;
+
+ /**
+ * True if this participant represents a network or bridge bot.
+ */
+ isNetworkBot?: boolean;
+
+ /**
+ * True if this participant has been invited but has not joined yet.
+ */
+ isPending?: boolean;
+ }
+ }
+
+ /**
+ * Chat capabilities reported by the platform.
+ */
+ export interface Capabilities {
+ /**
+ * Allowed Unicode reactions. Omitted means all emoji reactions are allowed.
+ */
+ allowedReactions?: Array;
+
+ /**
+ * True if archive/unarchive is supported.
+ */
+ archive?: boolean;
+
+ /**
+ * Supported attachment message types and their per-type constraints, keyed by
+ * Matrix msgtype or pseudo-msgtype (for example m.image, m.video,
+ * org.matrix.msc3245.voice). Missing message types should be treated as rejected.
+ */
+ attachments?: { [key: string]: Capabilities.Attachments };
+
+ /**
+ * True if custom emoji reactions are supported.
+ */
+ customEmojiReactions?: boolean;
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ delete?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * True if deleting chats for the authenticated user is supported.
+ */
+ deleteChat?: boolean;
+
+ /**
+ * True if deleting chats for everyone is supported.
+ */
+ deleteChatForEveryone?: boolean;
+
+ /**
+ * True if deleting messages only for the authenticated user is supported.
+ */
+ deleteForMe?: boolean;
+
+ /**
+ * Maximum message age for delete-for-everyone, in seconds.
+ */
+ deleteMaxAge?: number;
+
+ /**
+ * Disappearing-message timer capabilities.
+ */
+ disappearingTimer?: Capabilities.DisappearingTimer;
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ edit?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * Maximum message age for edits, in seconds.
+ */
+ editMaxAge?: number;
+
+ /**
+ * Maximum number of edits allowed for one message.
+ */
+ editMaxCount?: number;
+
+ /**
+ * Supported rich-text formatting features keyed by feature name (for example bold,
+ * inline_code, code_block.syntax_highlighting). Omitted means no formatting
+ * support is advertised.
+ */
+ formatting?: { [key: string]: -2 | -1 | 0 | 1 | 2 };
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ locationMessage?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * True if marking chats unread is supported.
+ */
+ markAsUnread?: boolean;
+
+ /**
+ * Maximum length of normal text messages.
+ */
+ maxTextLength?: number;
+
+ /**
+ * Message request capabilities.
+ */
+ messageRequest?: Capabilities.MessageRequest;
+
+ /**
+ * Participant management capabilities.
+ */
+ participantActions?: Capabilities.ParticipantActions;
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ poll?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ reaction?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * Maximum number of reactions allowed on a single message.
+ */
+ reactionCount?: number;
+
+ /**
+ * True if read receipts are supported.
+ */
+ readReceipts?: boolean;
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ reply?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * Chat state update capabilities.
+ */
+ state?: Capabilities.State;
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ thread?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * True if typing notifications are supported.
+ */
+ typingNotifications?: boolean;
+ }
+
+ export namespace Capabilities {
+ /**
+ * Capabilities for one attachment message type.
+ */
+ export interface Attachments {
+ /**
+ * Supported MIME types or MIME patterns for this file message type. Missing MIME
+ * types should be treated as rejected.
+ */
+ mimeTypes: { [key: string]: -2 | -1 | 0 | 1 | 2 };
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ caption?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * Maximum caption length when captions are supported.
+ */
+ maxCaptionLength?: number;
+
+ /**
+ * Maximum audio or video duration in seconds.
+ */
+ maxDuration?: number;
+
+ /**
+ * Maximum image or video height in pixels.
+ */
+ maxHeight?: number;
+
+ /**
+ * Maximum file size in bytes.
+ */
+ maxSize?: number;
+
+ /**
+ * Maximum image or video width in pixels.
+ */
+ maxWidth?: number;
+
+ /**
+ * True if this file type can be sent as view-once media.
+ */
+ viewOnce?: boolean;
+ }
+
+ /**
+ * Disappearing-message timer capabilities.
+ */
+ export interface DisappearingTimer {
+ /**
+ * True if empty timer objects should be omitted from message content.
+ */
+ omitEmptyTimer?: boolean;
+
+ /**
+ * Allowed disappearing timer values in milliseconds. Omitted means any timer is
+ * allowed.
+ */
+ timers?: Array;
+
+ /**
+ * Supported disappearing timer types.
+ */
+ types?: Array<'afterRead' | 'afterSend'>;
+ }
+
+ /**
+ * Message request capabilities.
+ */
+ export interface MessageRequest {
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ acceptWithButton?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ acceptWithMessage?: -2 | -1 | 0 | 1 | 2;
+ }
+
+ /**
+ * Participant management capabilities.
+ */
+ export interface ParticipantActions {
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ ban?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ invite?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ kick?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ leave?: -2 | -1 | 0 | 1 | 2;
+
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ revokeInvite?: -2 | -1 | 0 | 1 | 2;
+ }
+
+ /**
+ * Chat state update capabilities.
+ */
+ export interface State {
+ /**
+ * Chat avatar state capability.
+ */
+ avatar?: State.Avatar;
+
+ /**
+ * Chat description/topic state capability.
+ */
+ description?: State.Description;
+
+ /**
+ * Disappearing-message timer state capability.
+ */
+ disappearingTimer?: State.DisappearingTimer;
+
+ /**
+ * Chat title state capability.
+ */
+ title?: State.Title;
+ }
+
+ export namespace State {
+ /**
+ * Chat avatar state capability.
+ */
+ export interface Avatar {
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ level: -2 | -1 | 0 | 1 | 2;
+ }
+
+ /**
+ * Chat description/topic state capability.
+ */
+ export interface Description {
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ level: -2 | -1 | 0 | 1 | 2;
+ }
+
+ /**
+ * Disappearing-message timer state capability.
+ */
+ export interface DisappearingTimer {
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ level: -2 | -1 | 0 | 1 | 2;
+ }
+
+ /**
+ * Chat title state capability.
+ */
+ export interface Title {
+ /**
+ * -2: rejected, -1: dropped, 0: unsupported, 1: partially supported, 2: fully
+ * supported.
+ */
+ level: -2 | -1 | 0 | 1 | 2;
+ }
+ }
+ }
+
+ /**
+ * Current draft object for this chat, or null when no draft is set.
+ */
+ export interface Draft {
+ /**
+ * Matrix HTML draft body.
+ */
+ text: string;
+
+ /**
+ * Draft attachments keyed by attachment ID.
+ */
+ attachments?: { [key: string]: Draft.Attachments };
+ }
+
+ export namespace Draft {
+ export interface Attachments {
+ /**
+ * Draft attachment identifier.
+ */
+ id: string;
+
+ /**
+ * Draft attachment type. GIF and recorded audio are mutually exclusive types.
+ */
+ type: 'file' | 'gif' | 'recorded_audio';
+
+ /**
+ * Audio duration in seconds if known.
+ */
+ audioDurationSeconds?: number;
+
+ /**
+ * Original filename if available.
+ */
+ fileName?: string;
+
+ /**
+ * Local filesystem path for the draft attachment.
+ */
+ filePath?: string;
+
+ /**
+ * File size in bytes if known.
+ */
+ fileSize?: number;
+
+ /**
+ * MIME type if known.
+ */
+ mimeType?: string;
+
+ /**
+ * Pixel dimensions of the attachment.
+ */
+ size?: Attachments.Size;
+
+ /**
+ * Sticker identifier if the draft attachment is a sticker.
+ */
+ stickerID?: string;
+ }
+
+ export namespace Attachments {
+ /**
+ * Pixel dimensions of the attachment.
+ */
+ export interface Size {
+ height?: number;
+
+ width?: number;
+ }
+ }
+ }
+
+ /**
+ * Current reminder for this chat, or null when no reminder is set.
+ */
+ export interface Reminder {
+ /**
+ * Cancel reminder if someone messages in the chat.
+ */
+ dismissOnIncomingMessage?: boolean;
+
+ /**
+ * Timestamp when the reminder should trigger.
+ */
+ remindAt?: string;
+ }
+
+ /**
+ * Current snooze state for this chat, or null when no snooze is set.
+ */
+ export interface Snooze {
+ /**
+ * Timestamp when the snooze expires.
+ */
+ snoozeUntil?: string;
+
+ /**
+ * Timestamp when the user set the snooze.
+ */
+ userSnoozedAt?: string;
+ }
}
-export interface ChatCreateResponse {
+export interface ChatCreateResponse extends Chat {
/**
- * Newly created chat ID.
+ * @deprecated DEPRECATED - use id instead. Compatibility alias for older clients.
*/
chatID: string;
/**
- * Only returned in start mode. 'existing' means an existing chat was reused;
- * 'created' means a new chat was created.
+ * @deprecated DEPRECATED - legacy start-chat status for older clients. New clients
+ * should inspect the returned Chat instead.
*/
status?: 'existing' | 'created';
}
+/**
+ * Chat with optional last message preview.
+ */
export interface ChatListResponse extends Chat {
/**
* Last message preview for this chat, if available.
@@ -233,6 +859,19 @@ export interface ChatListResponse extends Chat {
preview?: Shared.Message;
}
+export interface ChatStartResponse extends Chat {
+ /**
+ * @deprecated DEPRECATED - use id instead. Compatibility alias for older clients.
+ */
+ chatID: string;
+
+ /**
+ * @deprecated DEPRECATED - legacy start-chat status for older clients. New clients
+ * should inspect the returned Chat instead.
+ */
+ status?: 'existing' | 'created';
+}
+
export interface ChatCreateParams {
/**
* Account to create or start the chat on.
@@ -240,10 +879,15 @@ export interface ChatCreateParams {
accountID: string;
/**
- * Only used for mode='start'. Whether invite-based DM creation is allowed when
- * required by the platform.
+ * User IDs to include in the new chat.
*/
- allowInvite?: boolean;
+ participantIDs: Array;
+
+ /**
+ * 'single' requires exactly one participantID; 'group' supports multiple
+ * participants and optional title.
+ */
+ type: 'single' | 'group';
/**
* Optional first message content if the platform requires it to create the chat.
@@ -251,74 +895,141 @@ export interface ChatCreateParams {
messageText?: string;
/**
- * Operation mode. Use 'start' to resolve a user/contact and start a direct chat.
- * Omit or set 'create' to create a chat directly.
+ * Optional title for group chats; ignored for single chats on most networks.
*/
- mode?: 'start' | 'create';
+ title?: string;
+}
+export interface ChatRetrieveParams {
/**
- * Required for create mode. Provide exactly one user ID for 'single' chats and one
- * or more for 'group' chats.
+ * Maximum number of participants to return. Use -1 for all; otherwise 0-500.
+ * Defaults to 100. List and search endpoints return up to 20 participants per
+ * chat.
*/
- participantIDs?: Array;
+ maxParticipantCount?: number | null;
+}
+export interface ChatUpdateParams {
/**
- * Optional title for group chats; ignored for single chats on most networks.
+ * Group chat description/topic. Support depends on the chat account and chat
+ * permissions.
*/
- title?: string;
+ description?: string | null;
/**
- * Required for create mode. 'single' creates a direct message chat; 'group'
- * creates a group chat.
+ * Draft object to set or clear. Non-empty drafts are only accepted when the
+ * current draft is empty. Send draft=null to clear text and attachments together
+ * before setting a new draft.
*/
- type?: 'single' | 'group';
+ draft?: ChatUpdateParams.Draft | null;
/**
- * Required for mode='start'. Merged user-like contact payload used to resolve the
- * best identifier.
+ * Local filesystem path to a group chat avatar image. Support depends on the chat
+ * account and chat permissions.
*/
- user?: ChatCreateParams.User;
-}
+ imgURL?: string | null;
-export namespace ChatCreateParams {
/**
- * Required for mode='start'. Merged user-like contact payload used to resolve the
- * best identifier.
+ * Archive or unarchive the chat.
*/
- export interface User {
- /**
- * Known user ID when available.
- */
- id?: string;
+ isArchived?: boolean;
- /**
- * Email candidate.
- */
- email?: string;
+ /**
+ * Mark or unmark the chat as low priority when supported by the account.
+ */
+ isLowPriority?: boolean;
- /**
- * Display name hint used for ranking only.
- */
- fullName?: string;
+ /**
+ * Mute or unmute the chat.
+ */
+ isMuted?: boolean;
+ /**
+ * Pin or unpin the chat when supported by the account.
+ */
+ isPinned?: boolean;
+
+ /**
+ * Disappearing-message timer in seconds, or null to clear when supported.
+ */
+ messageExpirySeconds?: number | null;
+
+ /**
+ * Custom chat title. Support depends on the chat account and chat permissions.
+ */
+ title?: string | null;
+}
+
+export namespace ChatUpdateParams {
+ /**
+ * Draft object to set or clear. Non-empty drafts are only accepted when the
+ * current draft is empty. Send draft=null to clear text and attachments together
+ * before setting a new draft.
+ */
+ export interface Draft {
/**
- * Phone number candidate (E.164 preferred).
+ * Draft text. Plain text and Markdown are converted to Matrix HTML with the same
+ * rules used by send and edit.
*/
- phoneNumber?: string;
+ text: string;
/**
- * Username/handle candidate.
+ * Draft attachments keyed by attachment ID. Each attachment must reference an
+ * uploadID returned by the upload file endpoint.
*/
- username?: string;
+ attachments?: { [key: string]: Draft.Attachments };
}
-}
-export interface ChatRetrieveParams {
- /**
- * Maximum number of participants to return. Use -1 for all; otherwise 0–500.
- * Defaults to all (-1).
- */
- maxParticipantCount?: number | null;
+ export namespace Draft {
+ export interface Attachments {
+ /**
+ * Upload ID from uploadAsset endpoint. Required to reference uploaded files.
+ */
+ uploadID: string;
+
+ /**
+ * Optional draft attachment identifier. If omitted, a new identifier is generated.
+ */
+ id?: string;
+
+ /**
+ * Duration in seconds (optional override of cached value)
+ */
+ duration?: number;
+
+ /**
+ * Filename (optional override of cached value)
+ */
+ fileName?: string;
+
+ /**
+ * MIME type (optional override of cached value)
+ */
+ mimeType?: string;
+
+ /**
+ * Dimensions (optional override of cached value)
+ */
+ size?: Attachments.Size;
+
+ /**
+ * Attachment type hint (image, video, audio, file, gif, voice-note, sticker). If
+ * omitted, auto-detected from mimeType
+ */
+ type?: 'image' | 'video' | 'audio' | 'file' | 'gif' | 'voice-note' | 'sticker';
+ }
+
+ export namespace Attachments {
+ /**
+ * Dimensions (optional override of cached value)
+ */
+ export interface Size {
+ height: number;
+
+ width: number;
+ }
+ }
+ }
}
export interface ChatListParams extends CursorNoLimitParams {
@@ -335,6 +1046,22 @@ export interface ChatArchiveParams {
archived?: boolean;
}
+export interface ChatMarkReadParams {
+ /**
+ * Optional message ID to mark read through.
+ */
+ messageID?: string;
+}
+
+export interface ChatMarkUnreadParams {
+ /**
+ * Optional message ID to mark unread from.
+ */
+ messageID?: string;
+}
+
+export interface ChatNotifyAnywayParams {}
+
export interface ChatSearchParams extends CursorSearchParams {
/**
* Provide an array of account IDs to filter chats from specific messaging accounts
@@ -390,6 +1117,60 @@ export interface ChatSearchParams extends CursorSearchParams {
unreadOnly?: boolean | null;
}
+export interface ChatStartParams {
+ /**
+ * Account to create or start the chat on.
+ */
+ accountID: string;
+
+ /**
+ * Merged user-like contact payload used to resolve the best identifier.
+ */
+ user: ChatStartParams.User;
+
+ /**
+ * Whether invite-based DM creation is allowed when required by the platform.
+ */
+ allowInvite?: boolean;
+
+ /**
+ * Optional first message content if the platform requires it to create the chat.
+ */
+ messageText?: string;
+}
+
+export namespace ChatStartParams {
+ /**
+ * Merged user-like contact payload used to resolve the best identifier.
+ */
+ export interface User {
+ /**
+ * Known user ID when available.
+ */
+ id?: string;
+
+ /**
+ * Email candidate.
+ */
+ email?: string;
+
+ /**
+ * Display name hint used for ranking only.
+ */
+ fullName?: string;
+
+ /**
+ * Phone number candidate (E.164 preferred).
+ */
+ phoneNumber?: string;
+
+ /**
+ * Username/handle candidate.
+ */
+ username?: string;
+ }
+}
+
Chats.Reminders = Reminders;
Chats.BaseReminders = BaseReminders;
Chats.Messages = Messages;
@@ -400,13 +1181,19 @@ export declare namespace Chats {
type Chat as Chat,
type ChatCreateResponse as ChatCreateResponse,
type ChatListResponse as ChatListResponse,
+ type ChatStartResponse as ChatStartResponse,
type ChatListResponsesCursorNoLimit as ChatListResponsesCursorNoLimit,
type ChatsCursorSearch as ChatsCursorSearch,
type ChatCreateParams as ChatCreateParams,
type ChatRetrieveParams as ChatRetrieveParams,
+ type ChatUpdateParams as ChatUpdateParams,
type ChatListParams as ChatListParams,
type ChatArchiveParams as ChatArchiveParams,
+ type ChatMarkReadParams as ChatMarkReadParams,
+ type ChatMarkUnreadParams as ChatMarkUnreadParams,
+ type ChatNotifyAnywayParams as ChatNotifyAnywayParams,
type ChatSearchParams as ChatSearchParams,
+ type ChatStartParams as ChatStartParams,
};
export {
diff --git a/src/resources/chats/index.ts b/src/resources/chats/index.ts
index 48374162..9f08601f 100644
--- a/src/resources/chats/index.ts
+++ b/src/resources/chats/index.ts
@@ -6,11 +6,17 @@ export {
type Chat,
type ChatCreateResponse,
type ChatListResponse,
+ type ChatStartResponse,
type ChatCreateParams,
type ChatRetrieveParams,
+ type ChatUpdateParams,
type ChatListParams,
type ChatArchiveParams,
+ type ChatMarkReadParams,
+ type ChatMarkUnreadParams,
+ type ChatNotifyAnywayParams,
type ChatSearchParams,
+ type ChatStartParams,
type ChatListResponsesCursorNoLimit,
type ChatsCursorSearch,
} from './chats';
diff --git a/src/resources/chats/messages/reactions.ts b/src/resources/chats/messages/reactions.ts
index 38adf1d0..ae246981 100644
--- a/src/resources/chats/messages/reactions.ts
+++ b/src/resources/chats/messages/reactions.ts
@@ -21,25 +21,22 @@ export class BaseReactions extends APIResource {
* @example
* ```ts
* const reaction =
- * await client.chats.messages.reactions.delete(
- * 'messageID',
- * {
- * chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
- * reactionKey: 'x',
- * },
- * );
+ * await client.chats.messages.reactions.delete('x', {
+ * chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ * messageID: '1343993',
+ * });
* ```
*/
delete(
- messageID: string,
+ reactionKey: string,
params: ReactionDeleteParams,
options?: RequestOptions,
): APIPromise {
- const { chatID, reactionKey } = params;
- return this._client.delete(path`/v1/chats/${chatID}/messages/${messageID}/reactions`, {
- query: { reactionKey },
- ...options,
- });
+ const { chatID, messageID } = params;
+ return this._client.delete(
+ path`/v1/chats/${chatID}/messages/${messageID}/reactions/${reactionKey}`,
+ options,
+ );
}
/**
@@ -48,7 +45,7 @@ export class BaseReactions extends APIResource {
* @example
* ```ts
* const response = await client.chats.messages.reactions.add(
- * 'messageID',
+ * '1343993',
* {
* chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
* reactionKey: 'x',
@@ -72,7 +69,8 @@ export class Reactions extends BaseReactions {}
export interface ReactionDeleteResponse {
/**
- * Unique identifier of the chat.
+ * Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
+ * installation when available.
*/
chatID: string;
@@ -82,19 +80,21 @@ export interface ReactionDeleteResponse {
messageID: string;
/**
- * Reaction key that was removed
+ * Reaction key that was removed.
*/
reactionKey: string;
/**
- * Whether the reaction was successfully removed
+ * Always true. Indicates the reaction removal was queued; failures return an error
+ * response.
*/
success: true;
}
export interface ReactionAddResponse {
/**
- * Unique identifier of the chat.
+ * Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
+ * installation when available.
*/
chatID: string;
@@ -104,36 +104,39 @@ export interface ReactionAddResponse {
messageID: string;
/**
- * Reaction key that was added
+ * Reaction key that was added.
*/
reactionKey: string;
/**
- * Whether the reaction was successfully added
+ * Always true. Indicates the reaction was queued; failures return an error
+ * response.
*/
success: true;
/**
- * Transaction ID used for the reaction event
+ * Transaction ID used for send tracking.
*/
transactionID: string;
}
export interface ReactionDeleteParams {
/**
- * Path param: Unique identifier of the chat.
+ * Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
+ * installation when available.
*/
chatID: string;
/**
- * Query param: Reaction key to remove
+ * Message ID.
*/
- reactionKey: string;
+ messageID: string;
}
export interface ReactionAddParams {
/**
- * Path param: Unique identifier of the chat.
+ * Path param: Chat ID. Input routes also accept the local chat ID from this Beeper
+ * Desktop installation when available.
*/
chatID: string;
@@ -143,7 +146,7 @@ export interface ReactionAddParams {
reactionKey: string;
/**
- * Body param: Optional transaction ID for deduplication and local echo tracking
+ * Body param: Optional transaction ID for deduplication and send tracking
*/
transactionID?: string;
}
diff --git a/src/resources/chats/reminders.ts b/src/resources/chats/reminders.ts
index 60a4e196..ae959be1 100644
--- a/src/resources/chats/reminders.ts
+++ b/src/resources/chats/reminders.ts
@@ -22,7 +22,7 @@ export class BaseReminders extends APIResource {
* ```ts
* await client.chats.reminders.create(
* '!NCdzlIaMjZUmvmvyHU:beeper.com',
- * { reminder: { remindAtMs: 0 } },
+ * { reminder: { remindAt: '2025-08-31T23:30:12.520Z' } },
* );
* ```
*/
@@ -69,9 +69,9 @@ export namespace ReminderCreateParams {
*/
export interface Reminder {
/**
- * Unix timestamp in milliseconds when reminder should trigger
+ * Timestamp when the reminder should trigger.
*/
- remindAtMs: number;
+ remindAt: string;
/**
* Cancel reminder if someone messages in the chat
diff --git a/src/resources/index.ts b/src/resources/index.ts
index 8a444e9c..7e824186 100644
--- a/src/resources/index.ts
+++ b/src/resources/index.ts
@@ -19,11 +19,17 @@ export {
type Chat,
type ChatCreateResponse,
type ChatListResponse,
+ type ChatStartResponse,
type ChatCreateParams,
type ChatRetrieveParams,
+ type ChatUpdateParams,
type ChatListParams,
type ChatArchiveParams,
+ type ChatMarkReadParams,
+ type ChatMarkUnreadParams,
+ type ChatNotifyAnywayParams,
type ChatSearchParams,
+ type ChatStartParams,
type ChatListResponsesCursorNoLimit,
type ChatsCursorSearch,
} from './chats/chats';
@@ -33,8 +39,10 @@ export {
BaseMessages,
type MessageUpdateResponse,
type MessageSendResponse,
+ type MessageRetrieveParams,
type MessageUpdateParams,
type MessageListParams,
+ type MessageDeleteParams,
type MessageSearchParams,
type MessageSendParams,
} from './messages';
diff --git a/src/resources/info.ts b/src/resources/info.ts
index f324cbd6..f405a629 100644
--- a/src/resources/info.ts
+++ b/src/resources/info.ts
@@ -5,21 +5,21 @@ import { APIPromise } from '../core/api-promise';
import { RequestOptions } from '../internal/request-options';
/**
- * Control the Beeper Desktop application
+ * Server discovery and capability metadata. Use /v1/info before authentication setup.
*/
export class BaseInfo extends APIResource {
static override readonly _key: readonly ['info'] = Object.freeze(['info'] as const);
/**
- * Returns app, platform, server, and endpoint discovery metadata for this Beeper
- * Desktop instance.
+ * Returns app, platform, server, endpoint discovery, OAuth, and WebSocket metadata
+ * for this Beeper Desktop instance.
*/
retrieve(options?: RequestOptions): APIPromise {
return this._client.get('/v1/info', { ...options, __security: {} });
}
}
/**
- * Control the Beeper Desktop application
+ * Server discovery and capability metadata. Use /v1/info before authentication setup.
*/
export class Info extends BaseInfo {}
@@ -123,7 +123,7 @@ export namespace InfoRetrieveResponse {
export interface Server {
/**
- * Base URL of the Connect server
+ * Base URL of the Beeper Desktop API server
*/
base_url: string;
diff --git a/src/resources/messages.ts b/src/resources/messages.ts
index 9d9eb33a..607df984 100644
--- a/src/resources/messages.ts
+++ b/src/resources/messages.ts
@@ -11,6 +11,7 @@ import {
type CursorSearchParams,
PagePromise,
} from '../core/pagination';
+import { buildHeaders } from '../internal/headers';
import { RequestOptions } from '../internal/request-options';
import { path } from '../internal/utils/path';
@@ -20,13 +21,33 @@ import { path } from '../internal/utils/path';
export class BaseMessages extends APIResource {
static override readonly _key: readonly ['messages'] = Object.freeze(['messages'] as const);
+ /**
+ * Retrieve a message by final message ID, pendingMessageID, or Matrix event ID.
+ * Chat ID may be a Beeper chat ID or local chat ID.
+ *
+ * @example
+ * ```ts
+ * const message = await client.messages.retrieve('1343993', {
+ * chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ * });
+ * ```
+ */
+ retrieve(
+ messageID: string,
+ params: MessageRetrieveParams,
+ options?: RequestOptions,
+ ): APIPromise {
+ const { chatID } = params;
+ return this._client.get(path`/v1/chats/${chatID}/messages/${messageID}`, options);
+ }
+
/**
* Edit the text content of an existing message. Messages with attachments cannot
* be edited.
*
* @example
* ```ts
- * const message = await client.messages.update('messageID', {
+ * const message = await client.messages.update('1343993', {
* chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
* text: 'x',
* });
@@ -65,6 +86,26 @@ export class BaseMessages extends APIResource {
});
}
+ /**
+ * Delete a message by final message ID. Pending message IDs are not accepted
+ * because messages cannot be deleted while sending.
+ *
+ * @example
+ * ```ts
+ * await client.messages.delete('1343993', {
+ * chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ * });
+ * ```
+ */
+ delete(messageID: string, params: MessageDeleteParams, options?: RequestOptions): APIPromise {
+ const { chatID, forEveryone } = params;
+ return this._client.delete(path`/v1/chats/${chatID}/messages/${messageID}`, {
+ query: { forEveryone },
+ ...options,
+ headers: buildHeaders([{ Accept: '*/*' }, options?.headers]),
+ });
+ }
+
/**
* Search messages across chats.
*
@@ -110,38 +151,46 @@ export class BaseMessages extends APIResource {
*/
export class Messages extends BaseMessages {}
-export interface MessageUpdateResponse {
- /**
- * Unique identifier of the chat.
- */
- chatID: string;
-
+export interface MessageUpdateResponse extends Shared.Message {
/**
- * Message ID.
+ * @deprecated DEPRECATED - use id instead. Compatibility alias for older clients.
*/
messageID: string;
/**
- * Whether the message was successfully edited
+ * @deprecated DEPRECATED - compatibility field. Successful responses are already
+ * represented by the 200 status code.
*/
- success: boolean;
+ success: true;
}
export interface MessageSendResponse {
/**
- * Unique identifier of the chat.
+ * Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
+ * installation when available.
*/
chatID: string;
/**
- * Pending message ID
+ * Pending ID assigned to the message before the network confirms the send. Pass it
+ * to GET /v1/chats/{chatID}/messages/{messageID} to resolve, or wait for the
+ * matching message.upserted over the WebSocket.
*/
pendingMessageID: string;
}
+export interface MessageRetrieveParams {
+ /**
+ * Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
+ * installation when available.
+ */
+ chatID: string;
+}
+
export interface MessageUpdateParams {
/**
- * Path param: Unique identifier of the chat.
+ * Path param: Chat ID. Input routes also accept the local chat ID from this Beeper
+ * Desktop installation when available.
*/
chatID: string;
@@ -153,6 +202,20 @@ export interface MessageUpdateParams {
export interface MessageListParams extends CursorNoLimitParams {}
+export interface MessageDeleteParams {
+ /**
+ * Path param: Chat ID. Input routes also accept the local chat ID from this Beeper
+ * Desktop installation when available.
+ */
+ chatID: string;
+
+ /**
+ * Query param: True to request deletion for everyone when the network supports it;
+ * false to delete only for the authenticated user when supported.
+ */
+ forEveryone?: boolean | null;
+}
+
export interface MessageSearchParams extends CursorSearchParams {
/**
* Limit search to specific account IDs.
@@ -226,7 +289,8 @@ export interface MessageSendParams {
replyToMessageID?: string;
/**
- * Text content of the message you want to send. You may use markdown.
+ * Draft text. Plain text and Markdown are converted to Matrix HTML with the same
+ * rules used by send and edit.
*/
text?: string;
}
@@ -262,10 +326,10 @@ export namespace MessageSendParams {
size?: Attachment.Size;
/**
- * Special attachment type (gif, voiceNote, sticker). If omitted, auto-detected
- * from mimeType
+ * Attachment type hint (image, video, audio, file, gif, voice-note, sticker). If
+ * omitted, auto-detected from mimeType
*/
- type?: 'gif' | 'voiceNote' | 'sticker';
+ type?: 'image' | 'video' | 'audio' | 'file' | 'gif' | 'voice-note' | 'sticker';
}
export namespace Attachment {
@@ -284,8 +348,10 @@ export declare namespace Messages {
export {
type MessageUpdateResponse as MessageUpdateResponse,
type MessageSendResponse as MessageSendResponse,
+ type MessageRetrieveParams as MessageRetrieveParams,
type MessageUpdateParams as MessageUpdateParams,
type MessageListParams as MessageListParams,
+ type MessageDeleteParams as MessageDeleteParams,
type MessageSearchParams as MessageSearchParams,
type MessageSendParams as MessageSendParams,
};
diff --git a/src/resources/shared.ts b/src/resources/shared.ts
index c8fb137b..63fda684 100644
--- a/src/resources/shared.ts
+++ b/src/resources/shared.ts
@@ -9,8 +9,8 @@ export interface Attachment {
type: 'unknown' | 'img' | 'video' | 'audio';
/**
- * Attachment identifier (typically an mxc:// URL). Use with /v1/assets/download to
- * get a local file path.
+ * Attachment identifier (typically an mxc:// URL). Use the download file endpoint
+ * to get a local file path.
*/
id?: string;
@@ -61,10 +61,15 @@ export interface Attachment {
size?: Attachment.Size;
/**
- * Public URL or local file path to fetch the asset. May be temporary or local-only
+ * Public URL or local file path to fetch the file. May be temporary or local-only
* to this device; download promptly if durable access is needed.
*/
srcURL?: string;
+
+ /**
+ * Attachment transcription if available.
+ */
+ transcription?: Attachment.Transcription;
}
export namespace Attachment {
@@ -76,6 +81,26 @@ export namespace Attachment {
width?: number;
}
+
+ /**
+ * Attachment transcription if available.
+ */
+ export interface Transcription {
+ /**
+ * Transcription engine.
+ */
+ engine: string;
+
+ /**
+ * Transcribed text.
+ */
+ transcription: string;
+
+ /**
+ * Detected or selected language.
+ */
+ language?: string;
+ }
}
export interface Error {
@@ -138,12 +163,14 @@ export interface Message {
accountID: string;
/**
- * Unique identifier of the chat.
+ * Chat ID. Input routes also accept the local chat ID from this Beeper Desktop
+ * installation when available.
*/
chatID: string;
/**
- * Sender user ID.
+ * Matrix-style fully-qualified sender user ID, usually including a bridge prefix
+ * and homeserver.
*/
senderID: string;
@@ -162,6 +189,21 @@ export interface Message {
*/
attachments?: Array;
+ /**
+ * Timestamp when the message was edited, if known.
+ */
+ editedTimestamp?: string;
+
+ /**
+ * True if the message has been deleted.
+ */
+ isDeleted?: boolean;
+
+ /**
+ * True if the message is hidden from normal display.
+ */
+ isHidden?: boolean;
+
/**
* True if the authenticated user sent the message.
*/
@@ -177,19 +219,39 @@ export interface Message {
*/
linkedMessageID?: string;
+ /**
+ * Link previews included with this message, if any.
+ */
+ links?: Array;
+
+ /**
+ * Mentioned user IDs, @room, or null for legacy messages that require text
+ * scanning.
+ */
+ mentions?: Array | null;
+
/**
* Reactions to the message, if any.
*/
reactions?: Array;
+ /**
+ * Read receipt state for this message, when available.
+ */
+ seen?: boolean | string | { [key: string]: boolean | string };
+
/**
* Resolved sender display name (impersonator/full name/username/participant name).
*/
senderName?: string;
/**
- * Plain-text body if present. May include a JSON fallback with text entities for
- * rich messages.
+ * Message send status for this message, when reported by the bridge.
+ */
+ sendStatus?: Message.SendStatus;
+
+ /**
+ * Matrix HTML body if present.
*/
text?: string;
@@ -210,10 +272,101 @@ export interface Message {
| 'REACTION';
}
+export namespace Message {
+ /**
+ * Link preview included with a message.
+ */
+ export interface Link {
+ /**
+ * Link preview title.
+ */
+ title: string;
+
+ /**
+ * Resolved link URL.
+ */
+ url: string;
+
+ /**
+ * Favicon URL if available. May be temporary or local-only to this device;
+ * download promptly if durable access is needed.
+ */
+ favicon?: string;
+
+ /**
+ * Preview image URL if available. May be temporary or local-only to this device;
+ * download promptly if durable access is needed.
+ */
+ img?: string;
+
+ /**
+ * Preview image dimensions.
+ */
+ imgSize?: Link.ImgSize;
+
+ /**
+ * Original URL when the displayed URL is shortened or redirected.
+ */
+ originalURL?: string;
+
+ /**
+ * Link preview summary.
+ */
+ summary?: string;
+ }
+
+ export namespace Link {
+ /**
+ * Preview image dimensions.
+ */
+ export interface ImgSize {
+ height?: number;
+
+ width?: number;
+ }
+ }
+
+ /**
+ * Message send status for this message, when reported by the bridge.
+ */
+ export interface SendStatus {
+ /**
+ * Current status of the message send attempt.
+ */
+ status: 'SUCCESS' | 'PENDING' | 'FAIL_RETRIABLE' | 'FAIL_PERMANENT';
+
+ /**
+ * Timestamp for the send status event.
+ */
+ timestamp: string;
+
+ /**
+ * User IDs the message was delivered to, when reported by the network.
+ */
+ deliveredToUsers?: Array;
+
+ /**
+ * Internal bridge error detail. Intended for diagnostics, not end-user display.
+ */
+ internalError?: string;
+
+ /**
+ * Human-readable send status or failure message.
+ */
+ message?: string;
+
+ /**
+ * Machine-readable failure reason. Present when the send status is a failure.
+ */
+ reason?: string;
+ }
+}
+
export interface Reaction {
/**
- * Reaction ID, typically ${participantID}${reactionKey} if multiple reactions
- * allowed, or just participantID otherwise.
+ * Reaction ID. When a participant can react more than once, the ID is the
+ * participant ID concatenated with the reaction key; otherwise it equals the
+ * participant ID.
*/
id: string;
@@ -266,8 +419,9 @@ export interface User {
fullName?: string;
/**
- * Avatar image URL if available. May be temporary or local-only to this device;
- * download promptly if durable access is needed.
+ * Avatar image URL if available. This may be a remote URL, Matrix media URL, data
+ * URL, or local filesystem URL depending on source and endpoint. May be temporary
+ * or local-only to this device; download promptly if durable access is needed.
*/
imgURL?: string;
diff --git a/src/resources/top-level.ts b/src/resources/top-level.ts
index c7fb734e..0cd89f42 100644
--- a/src/resources/top-level.ts
+++ b/src/resources/top-level.ts
@@ -72,12 +72,12 @@ export interface FocusParams {
chatID?: string;
/**
- * Optional draft attachment path to populate in the message input field.
+ * Optional image path to populate in the message input field.
*/
draftAttachmentPath?: string;
/**
- * Optional draft text to populate in the message input field.
+ * Optional plain text to populate in the message input field.
*/
draftText?: string;
diff --git a/src/version.ts b/src/version.ts
index 5ae204f4..e156b0ea 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const VERSION = '4.8.0'; // x-release-please-version
+export const VERSION = '5.0.0'; // x-release-please-version
diff --git a/tests/api-resources/chats/chats.test.ts b/tests/api-resources/chats/chats.test.ts
index 34c27c25..646a7d73 100644
--- a/tests/api-resources/chats/chats.test.ts
+++ b/tests/api-resources/chats/chats.test.ts
@@ -18,7 +18,11 @@ const partialClient = createClient({
const runTests = (client: PartialBeeperDesktop<{ chats: BaseChats }>) => {
test('create: only required params', async () => {
- const responsePromise = client.chats.create({ accountID: 'accountID' });
+ const responsePromise = client.chats.create({
+ accountID: 'accountID',
+ participantIDs: ['string'],
+ type: 'single',
+ });
const rawResponse = await responsePromise.asResponse();
expect(rawResponse).toBeInstanceOf(Response);
const response = await responsePromise;
@@ -31,19 +35,10 @@ const runTests = (client: PartialBeeperDesktop<{ chats: BaseChats }>) => {
test('create: required and optional params', async () => {
const response = await client.chats.create({
accountID: 'accountID',
- allowInvite: true,
- messageText: 'messageText',
- mode: 'start',
participantIDs: ['string'],
- title: 'title',
type: 'single',
- user: {
- id: 'id',
- email: 'email',
- fullName: 'fullName',
- phoneNumber: 'phoneNumber',
- username: 'username',
- },
+ messageText: 'messageText',
+ title: 'title',
});
});
@@ -63,7 +58,52 @@ const runTests = (client: PartialBeeperDesktop<{ chats: BaseChats }>) => {
await expect(
client.chats.retrieve(
'!NCdzlIaMjZUmvmvyHU:beeper.com',
- { maxParticipantCount: 50 },
+ { maxParticipantCount: 100 },
+ { path: '/_stainless_unknown_path' },
+ ),
+ ).rejects.toThrow(BeeperDesktop.NotFoundError);
+ });
+
+ test('update', async () => {
+ const responsePromise = client.chats.update('!NCdzlIaMjZUmvmvyHU:beeper.com');
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('update: request options and params are passed correctly', async () => {
+ // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
+ await expect(
+ client.chats.update(
+ '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ {
+ description: 'description',
+ draft: {
+ text: 'text',
+ attachments: {
+ foo: {
+ uploadID: 'uploadID',
+ id: 'id',
+ duration: 0,
+ fileName: 'fileName',
+ mimeType: 'mimeType',
+ size: { height: 0, width: 0 },
+ type: 'image',
+ },
+ },
+ },
+ imgURL: 'imgURL',
+ isArchived: true,
+ isLowPriority: true,
+ isMuted: true,
+ isPinned: true,
+ messageExpirySeconds: 0,
+ title: 'title',
+ },
{ path: '/_stainless_unknown_path' },
),
).rejects.toThrow(BeeperDesktop.NotFoundError);
@@ -85,10 +125,7 @@ const runTests = (client: PartialBeeperDesktop<{ chats: BaseChats }>) => {
await expect(
client.chats.list(
{
- accountIDs: [
- 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc',
- 'local-instagram_ba_eRfQMmnSNy_p7Ih7HL7RduRpKFU',
- ],
+ accountIDs: ['matrix', 'discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'],
cursor: '1725489123456|c29tZUltc2dQYWdl',
direction: 'before',
},
@@ -119,6 +156,68 @@ const runTests = (client: PartialBeeperDesktop<{ chats: BaseChats }>) => {
).rejects.toThrow(BeeperDesktop.NotFoundError);
});
+ test('markRead', async () => {
+ const responsePromise = client.chats.markRead('!NCdzlIaMjZUmvmvyHU:beeper.com');
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('markRead: request options and params are passed correctly', async () => {
+ // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
+ await expect(
+ client.chats.markRead(
+ '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ { messageID: '1343993' },
+ { path: '/_stainless_unknown_path' },
+ ),
+ ).rejects.toThrow(BeeperDesktop.NotFoundError);
+ });
+
+ test('markUnread', async () => {
+ const responsePromise = client.chats.markUnread('!NCdzlIaMjZUmvmvyHU:beeper.com');
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('markUnread: request options and params are passed correctly', async () => {
+ // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
+ await expect(
+ client.chats.markUnread(
+ '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ { messageID: '1343993' },
+ { path: '/_stainless_unknown_path' },
+ ),
+ ).rejects.toThrow(BeeperDesktop.NotFoundError);
+ });
+
+ test('notifyAnyway', async () => {
+ const responsePromise = client.chats.notifyAnyway('!NCdzlIaMjZUmvmvyHU:beeper.com');
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('notifyAnyway: request options and params are passed correctly', async () => {
+ // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
+ await expect(
+ client.chats.notifyAnyway('!NCdzlIaMjZUmvmvyHU:beeper.com', {}, { path: '/_stainless_unknown_path' }),
+ ).rejects.toThrow(BeeperDesktop.NotFoundError);
+ });
+
test('search', async () => {
const responsePromise = client.chats.search();
const rawResponse = await responsePromise.asResponse();
@@ -135,10 +234,7 @@ const runTests = (client: PartialBeeperDesktop<{ chats: BaseChats }>) => {
await expect(
client.chats.search(
{
- accountIDs: [
- 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc',
- 'local-telegram_ba_QFrb5lrLPhO3OT5MFBeTWv0x4BI',
- ],
+ accountIDs: ['matrix', 'discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'],
cursor: '1725489123456|c29tZUltc2dQYWdl',
direction: 'before',
inbox: 'primary',
@@ -155,6 +251,35 @@ const runTests = (client: PartialBeeperDesktop<{ chats: BaseChats }>) => {
),
).rejects.toThrow(BeeperDesktop.NotFoundError);
});
+
+ test('start: only required params', async () => {
+ const responsePromise = client.chats.start({
+ accountID: 'accountID',
+ user: {},
+ });
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('start: required and optional params', async () => {
+ const response = await client.chats.start({
+ accountID: 'accountID',
+ user: {
+ id: 'id',
+ email: 'email',
+ fullName: 'fullName',
+ phoneNumber: 'phoneNumber',
+ username: 'username',
+ },
+ allowInvite: true,
+ messageText: 'messageText',
+ });
+ });
};
describe('resource chats', () => runTests(client));
describe('resource chats (tree shakable, base)', () => runTests(partialClient));
diff --git a/tests/api-resources/chats/messages/reactions.test.ts b/tests/api-resources/chats/messages/reactions.test.ts
index f0f81820..1468ec0d 100644
--- a/tests/api-resources/chats/messages/reactions.test.ts
+++ b/tests/api-resources/chats/messages/reactions.test.ts
@@ -25,9 +25,9 @@ const parentPartialClient = createClient({
const runTests = (client: PartialBeeperDesktop<{ chats: { messages: { reactions: BaseReactions } } }>) => {
test('delete: only required params', async () => {
- const responsePromise = client.chats.messages.reactions.delete('messageID', {
+ const responsePromise = client.chats.messages.reactions.delete('x', {
chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
- reactionKey: 'x',
+ messageID: '1343993',
});
const rawResponse = await responsePromise.asResponse();
expect(rawResponse).toBeInstanceOf(Response);
@@ -39,14 +39,14 @@ const runTests = (client: PartialBeeperDesktop<{ chats: { messages: { reactions:
});
test('delete: required and optional params', async () => {
- const response = await client.chats.messages.reactions.delete('messageID', {
+ const response = await client.chats.messages.reactions.delete('x', {
chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
- reactionKey: 'x',
+ messageID: '1343993',
});
});
test('add: only required params', async () => {
- const responsePromise = client.chats.messages.reactions.add('messageID', {
+ const responsePromise = client.chats.messages.reactions.add('1343993', {
chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
reactionKey: 'x',
});
@@ -60,7 +60,7 @@ const runTests = (client: PartialBeeperDesktop<{ chats: { messages: { reactions:
});
test('add: required and optional params', async () => {
- const response = await client.chats.messages.reactions.add('messageID', {
+ const response = await client.chats.messages.reactions.add('1343993', {
chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
reactionKey: 'x',
transactionID: 'transactionID',
diff --git a/tests/api-resources/chats/reminders.test.ts b/tests/api-resources/chats/reminders.test.ts
index 7feb3547..37697346 100644
--- a/tests/api-resources/chats/reminders.test.ts
+++ b/tests/api-resources/chats/reminders.test.ts
@@ -26,7 +26,7 @@ const parentPartialClient = createClient({
const runTests = (client: PartialBeeperDesktop<{ chats: { reminders: BaseReminders } }>) => {
test('create: only required params', async () => {
const responsePromise = client.chats.reminders.create('!NCdzlIaMjZUmvmvyHU:beeper.com', {
- reminder: { remindAtMs: 0 },
+ reminder: { remindAt: '2025-08-31T23:30:12.520Z' },
});
const rawResponse = await responsePromise.asResponse();
expect(rawResponse).toBeInstanceOf(Response);
@@ -39,7 +39,7 @@ const runTests = (client: PartialBeeperDesktop<{ chats: { reminders: BaseReminde
test('create: required and optional params', async () => {
const response = await client.chats.reminders.create('!NCdzlIaMjZUmvmvyHU:beeper.com', {
- reminder: { remindAtMs: 0, dismissOnIncomingMessage: true },
+ reminder: { remindAt: '2025-08-31T23:30:12.520Z', dismissOnIncomingMessage: true },
});
});
diff --git a/tests/api-resources/messages.test.ts b/tests/api-resources/messages.test.ts
index c434ee8b..085bd320 100644
--- a/tests/api-resources/messages.test.ts
+++ b/tests/api-resources/messages.test.ts
@@ -17,8 +17,23 @@ const partialClient = createClient({
});
const runTests = (client: PartialBeeperDesktop<{ messages: BaseMessages }>) => {
+ test('retrieve: only required params', async () => {
+ const responsePromise = client.messages.retrieve('1343993', { chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com' });
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('retrieve: required and optional params', async () => {
+ const response = await client.messages.retrieve('1343993', { chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com' });
+ });
+
test('update: only required params', async () => {
- const responsePromise = client.messages.update('messageID', {
+ const responsePromise = client.messages.update('1343993', {
chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
text: 'x',
});
@@ -32,7 +47,7 @@ const runTests = (client: PartialBeeperDesktop<{ messages: BaseMessages }>) => {
});
test('update: required and optional params', async () => {
- const response = await client.messages.update('messageID', {
+ const response = await client.messages.update('1343993', {
chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
text: 'x',
});
@@ -60,6 +75,24 @@ const runTests = (client: PartialBeeperDesktop<{ messages: BaseMessages }>) => {
).rejects.toThrow(BeeperDesktop.NotFoundError);
});
+ test('delete: only required params', async () => {
+ const responsePromise = client.messages.delete('1343993', { chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com' });
+ const rawResponse = await responsePromise.asResponse();
+ expect(rawResponse).toBeInstanceOf(Response);
+ const response = await responsePromise;
+ expect(response).not.toBeInstanceOf(Response);
+ const dataAndResponse = await responsePromise.withResponse();
+ expect(dataAndResponse.data).toBe(response);
+ expect(dataAndResponse.response).toBe(rawResponse);
+ });
+
+ test('delete: required and optional params', async () => {
+ const response = await client.messages.delete('1343993', {
+ chatID: '!NCdzlIaMjZUmvmvyHU:beeper.com',
+ forEveryone: true,
+ });
+ });
+
test('search', async () => {
const responsePromise = client.messages.search();
const rawResponse = await responsePromise.asResponse();
@@ -76,10 +109,7 @@ const runTests = (client: PartialBeeperDesktop<{ messages: BaseMessages }>) => {
await expect(
client.messages.search(
{
- accountIDs: [
- 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc',
- 'local-instagram_ba_eRfQMmnSNy_p7Ih7HL7RduRpKFU',
- ],
+ accountIDs: ['matrix', 'discordgo', 'local-whatsapp_ba_EvYDBBsZbRQAy3UOSWqG0LuTVkc'],
chatIDs: ['!NCdzlIaMjZUmvmvyHU:beeper.com', '1231073'],
chatType: 'group',
cursor: '1725489123456|c29tZUltc2dQYWdl',
@@ -121,7 +151,7 @@ const runTests = (client: PartialBeeperDesktop<{ messages: BaseMessages }>) => {
fileName: 'fileName',
mimeType: 'mimeType',
size: { height: 0, width: 0 },
- type: 'gif',
+ type: 'image',
},
replyToMessageID: 'replyToMessageID',
text: 'text',