chub-dev 0.1.0 → 0.1.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- package/src/mcp/tools.js +251 -0
|
@@ -0,0 +1,1347 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: confluence
|
|
3
|
+
description: "Confluence Cloud API coding guidelines for JavaScript/TypeScript using the confluence.js library"
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "javascript"
|
|
6
|
+
versions: "2.1.0"
|
|
7
|
+
updated-on: "2026-03-02"
|
|
8
|
+
source: maintainer
|
|
9
|
+
tags: "atlassian,confluence,wiki,documentation,collaboration"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Confluence Cloud API Coding Guidelines (JavaScript/TypeScript)
|
|
13
|
+
|
|
14
|
+
You are a **Confluence Cloud API coding expert**. Help me write correct, idiomatic JavaScript/TypeScript that calls the Atlassian Confluence Cloud REST API using the confluence.js library.
|
|
15
|
+
|
|
16
|
+
Use **only official Atlassian sources** for API behavior, fields, and constraints. This guide summarizes key patterns for both **Node.js** and **browser** applications.
|
|
17
|
+
|
|
18
|
+
> Ground truth: Atlassian Confluence Cloud REST API documentation at developer.atlassian.com/cloud/confluence/
|
|
19
|
+
|
|
20
|
+
## Golden Rule: Use confluence.js Library
|
|
21
|
+
|
|
22
|
+
**CRITICAL:** Use the `confluence.js` npm package (version 2.1.0 or later) for interacting with Confluence Cloud REST API. This is the most comprehensive and actively maintained community library for both Cloud and Server APIs.
|
|
23
|
+
|
|
24
|
+
**DO NOT use:**
|
|
25
|
+
- `confluence-api` (outdated, less comprehensive)
|
|
26
|
+
- `atlassian-confluence` (deprecated)
|
|
27
|
+
- Direct REST API calls without a client library (error-prone)
|
|
28
|
+
|
|
29
|
+
**Install:**
|
|
30
|
+
```bash
|
|
31
|
+
npm install confluence.js
|
|
32
|
+
# or
|
|
33
|
+
yarn add confluence.js
|
|
34
|
+
# or
|
|
35
|
+
pnpm add confluence.js
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The library supports Node.js v20.0.0+ and modern browsers with full TypeScript support.
|
|
39
|
+
|
|
40
|
+
## Authentication
|
|
41
|
+
|
|
42
|
+
Confluence Cloud supports multiple authentication methods. Choose based on your use case.
|
|
43
|
+
|
|
44
|
+
### API Token (Basic Auth) - Recommended for Scripts
|
|
45
|
+
|
|
46
|
+
Generate an API token from your Atlassian Account Settings (https://id.atlassian.com/manage-profile/security/api-tokens).
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { ConfluenceClient } from 'confluence.js';
|
|
50
|
+
|
|
51
|
+
const client = new ConfluenceClient({
|
|
52
|
+
host: 'https://your-domain.atlassian.net/wiki',
|
|
53
|
+
authentication: {
|
|
54
|
+
basic: {
|
|
55
|
+
email: 'your-email@example.com',
|
|
56
|
+
apiToken: process.env.CONFLUENCE_API_TOKEN,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Environment Variable Setup:**
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# .env file
|
|
66
|
+
CONFLUENCE_API_TOKEN=your_api_token_here
|
|
67
|
+
CONFLUENCE_EMAIL=your-email@example.com
|
|
68
|
+
CONFLUENCE_HOST=https://your-domain.atlassian.net/wiki
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Loading in code:**
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { config } from 'dotenv';
|
|
75
|
+
import { ConfluenceClient } from 'confluence.js';
|
|
76
|
+
|
|
77
|
+
config(); // Load .env file
|
|
78
|
+
|
|
79
|
+
const client = new ConfluenceClient({
|
|
80
|
+
host: process.env.CONFLUENCE_HOST!,
|
|
81
|
+
authentication: {
|
|
82
|
+
basic: {
|
|
83
|
+
email: process.env.CONFLUENCE_EMAIL!,
|
|
84
|
+
apiToken: process.env.CONFLUENCE_API_TOKEN!,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### OAuth 2.0 (3LO) - For Third-Party Apps
|
|
91
|
+
|
|
92
|
+
Use OAuth 2.0 three-legged authentication for apps that access Confluence on behalf of users.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { ConfluenceClient } from 'confluence.js';
|
|
96
|
+
|
|
97
|
+
const client = new ConfluenceClient({
|
|
98
|
+
host: 'https://your-domain.atlassian.net/wiki',
|
|
99
|
+
authentication: {
|
|
100
|
+
oauth2: {
|
|
101
|
+
accessToken: process.env.OAUTH_ACCESS_TOKEN!,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### JWT (Server-to-Server) - For Atlassian Connect Apps
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { ConfluenceClient } from 'confluence.js';
|
|
111
|
+
|
|
112
|
+
const client = new ConfluenceClient({
|
|
113
|
+
host: 'https://your-domain.atlassian.net/wiki',
|
|
114
|
+
authentication: {
|
|
115
|
+
jwt: {
|
|
116
|
+
issuer: process.env.JWT_ISSUER!,
|
|
117
|
+
secret: process.env.JWT_SECRET!,
|
|
118
|
+
expiryTimeSeconds: 180, // Optional, defaults to 180
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Initialization Patterns
|
|
125
|
+
|
|
126
|
+
### Basic Initialization
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { ConfluenceClient } from 'confluence.js';
|
|
130
|
+
|
|
131
|
+
const client = new ConfluenceClient({
|
|
132
|
+
host: 'https://your-domain.atlassian.net/wiki',
|
|
133
|
+
authentication: {
|
|
134
|
+
basic: {
|
|
135
|
+
email: 'your-email@example.com',
|
|
136
|
+
apiToken: 'your-api-token',
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### With Custom API Prefix
|
|
143
|
+
|
|
144
|
+
If your Confluence instance uses a non-standard API path:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
const client = new ConfluenceClient({
|
|
148
|
+
host: 'https://your-domain.atlassian.net',
|
|
149
|
+
apiPrefix: '/custom/api/path',
|
|
150
|
+
authentication: {
|
|
151
|
+
basic: {
|
|
152
|
+
email: 'your-email@example.com',
|
|
153
|
+
apiToken: 'your-api-token',
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Tree Shaking for Smaller Bundles
|
|
160
|
+
|
|
161
|
+
Import only the API modules you need:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { ContentClient } from 'confluence.js/out/api/content';
|
|
165
|
+
import { SpaceClient } from 'confluence.js/out/api/space';
|
|
166
|
+
|
|
167
|
+
// Use individual clients
|
|
168
|
+
const contentClient = new ContentClient({
|
|
169
|
+
host: 'https://your-domain.atlassian.net/wiki',
|
|
170
|
+
authentication: { /* ... */ },
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Pages API
|
|
175
|
+
|
|
176
|
+
### Get Page by ID
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
const page = await client.content.getContent({
|
|
180
|
+
id: '123456789',
|
|
181
|
+
expand: ['body.storage', 'version', 'space'],
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
console.log(page.title);
|
|
185
|
+
console.log(page.body?.storage?.value);
|
|
186
|
+
console.log(page.version?.number);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Get Page by Title and Space
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const results = await client.content.getContent({
|
|
193
|
+
spaceKey: 'DEMO',
|
|
194
|
+
title: 'Project Overview',
|
|
195
|
+
expand: ['body.storage', 'version'],
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (results.results && results.results.length > 0) {
|
|
199
|
+
const page = results.results[0];
|
|
200
|
+
console.log(page.id, page.title);
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Create a Page
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
const newPage = await client.content.createContent({
|
|
208
|
+
type: 'page',
|
|
209
|
+
title: 'Getting Started Guide',
|
|
210
|
+
space: { key: 'DEMO' },
|
|
211
|
+
body: {
|
|
212
|
+
storage: {
|
|
213
|
+
value: '<h1>Welcome</h1><p>This is the introduction to our project.</p>',
|
|
214
|
+
representation: 'storage',
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
console.log(`Created page: ${newPage.id}`);
|
|
220
|
+
console.log(`URL: ${newPage._links?.webui}`);
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Create a Child Page
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
const childPage = await client.content.createContent({
|
|
227
|
+
type: 'page',
|
|
228
|
+
title: 'Installation Instructions',
|
|
229
|
+
space: { key: 'DEMO' },
|
|
230
|
+
ancestors: [{ id: '123456789' }], // Parent page ID
|
|
231
|
+
body: {
|
|
232
|
+
storage: {
|
|
233
|
+
value: '<p>Follow these steps to install...</p>',
|
|
234
|
+
representation: 'storage',
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Update a Page
|
|
241
|
+
|
|
242
|
+
**IMPORTANT:** Always increment the version number when updating.
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
// First, get the current page to retrieve version
|
|
246
|
+
const currentPage = await client.content.getContent({
|
|
247
|
+
id: '123456789',
|
|
248
|
+
expand: ['version'],
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Update with incremented version
|
|
252
|
+
const updatedPage = await client.content.updateContent({
|
|
253
|
+
id: '123456789',
|
|
254
|
+
type: 'page',
|
|
255
|
+
title: 'Updated Title',
|
|
256
|
+
version: {
|
|
257
|
+
number: currentPage.version!.number! + 1,
|
|
258
|
+
},
|
|
259
|
+
body: {
|
|
260
|
+
storage: {
|
|
261
|
+
value: '<h1>Updated Content</h1><p>New information here.</p>',
|
|
262
|
+
representation: 'storage',
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
console.log(`Updated to version ${updatedPage.version?.number}`);
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Update Only Page Content (Keep Title)
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
const currentPage = await client.content.getContent({
|
|
274
|
+
id: '123456789',
|
|
275
|
+
expand: ['version', 'space'],
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const updatedPage = await client.content.updateContent({
|
|
279
|
+
id: '123456789',
|
|
280
|
+
type: 'page',
|
|
281
|
+
title: currentPage.title, // Keep existing title
|
|
282
|
+
space: { key: currentPage.space?.key },
|
|
283
|
+
version: {
|
|
284
|
+
number: currentPage.version!.number! + 1,
|
|
285
|
+
},
|
|
286
|
+
body: {
|
|
287
|
+
storage: {
|
|
288
|
+
value: '<p>Only the content changed.</p>',
|
|
289
|
+
representation: 'storage',
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Delete a Page
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
await client.content.deleteContent({ id: '123456789' });
|
|
299
|
+
console.log('Page deleted successfully');
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Get Page Children
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
const children = await client.content.getContentChildren({
|
|
306
|
+
id: '123456789',
|
|
307
|
+
expand: ['page'],
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
if (children.page?.results) {
|
|
311
|
+
for (const child of children.page.results) {
|
|
312
|
+
console.log(`Child: ${child.title} (${child.id})`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Get Page History
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
const history = await client.content.getHistory({ id: '123456789' });
|
|
321
|
+
|
|
322
|
+
console.log(`Created by: ${history.createdBy?.displayName}`);
|
|
323
|
+
console.log(`Created at: ${history.createdDate}`);
|
|
324
|
+
console.log(`Latest version: ${history.latest?.number}`);
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Get Page Versions
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
const versions = await client.content.getContentVersions({
|
|
331
|
+
id: '123456789',
|
|
332
|
+
limit: 10,
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
for (const version of versions.results || []) {
|
|
336
|
+
console.log(`Version ${version.number} by ${version.by?.displayName}`);
|
|
337
|
+
console.log(` When: ${version.when}`);
|
|
338
|
+
console.log(` Message: ${version.message || 'No message'}`);
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Spaces API
|
|
343
|
+
|
|
344
|
+
### Get Space by Key
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
const space = await client.space.getSpace({
|
|
348
|
+
spaceKey: 'DEMO',
|
|
349
|
+
expand: ['description.plain', 'homepage'],
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
console.log(space.name);
|
|
353
|
+
console.log(space.description?.plain?.value);
|
|
354
|
+
console.log(space.homepage?.id);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Get All Spaces
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
const spaces = await client.space.getSpaces({
|
|
361
|
+
limit: 50,
|
|
362
|
+
expand: ['description.plain'],
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
for (const space of spaces.results || []) {
|
|
366
|
+
console.log(`${space.key}: ${space.name}`);
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Get Spaces with Pagination
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
let start = 0;
|
|
374
|
+
const limit = 25;
|
|
375
|
+
let hasMore = true;
|
|
376
|
+
|
|
377
|
+
while (hasMore) {
|
|
378
|
+
const spaces = await client.space.getSpaces({
|
|
379
|
+
start,
|
|
380
|
+
limit,
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
for (const space of spaces.results || []) {
|
|
384
|
+
console.log(`${space.key}: ${space.name}`);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
start += limit;
|
|
388
|
+
hasMore = (spaces.results?.length || 0) === limit;
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Create a Space
|
|
393
|
+
|
|
394
|
+
```ts
|
|
395
|
+
const newSpace = await client.space.createSpace({
|
|
396
|
+
key: 'PROJ',
|
|
397
|
+
name: 'Project Galaxy',
|
|
398
|
+
description: {
|
|
399
|
+
plain: {
|
|
400
|
+
value: 'Documentation for Project Galaxy',
|
|
401
|
+
representation: 'plain',
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
console.log(`Created space: ${newSpace.key}`);
|
|
407
|
+
console.log(`URL: ${newSpace._links?.webui}`);
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Create a Private Space
|
|
411
|
+
|
|
412
|
+
```ts
|
|
413
|
+
const privateSpace = await client.space.createPrivateSpace({
|
|
414
|
+
key: 'PRIV',
|
|
415
|
+
name: 'Private Team Space',
|
|
416
|
+
description: {
|
|
417
|
+
plain: {
|
|
418
|
+
value: 'Internal team documentation',
|
|
419
|
+
representation: 'plain',
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
});
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Update a Space
|
|
426
|
+
|
|
427
|
+
```ts
|
|
428
|
+
const updatedSpace = await client.space.updateSpace({
|
|
429
|
+
spaceKey: 'DEMO',
|
|
430
|
+
name: 'Demo Space - Updated',
|
|
431
|
+
description: {
|
|
432
|
+
plain: {
|
|
433
|
+
value: 'Updated description',
|
|
434
|
+
representation: 'plain',
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Delete a Space
|
|
441
|
+
|
|
442
|
+
```ts
|
|
443
|
+
const task = await client.space.deleteSpace({ spaceKey: 'OLDSPACE' });
|
|
444
|
+
console.log('Space deletion initiated');
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Get Space Content
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
const content = await client.space.getSpaceContent({
|
|
451
|
+
spaceKey: 'DEMO',
|
|
452
|
+
expand: ['body.storage'],
|
|
453
|
+
limit: 50,
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
for (const item of content.page?.results || []) {
|
|
457
|
+
console.log(`Page: ${item.title} (${item.id})`);
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
## Search API
|
|
462
|
+
|
|
463
|
+
### Search Content (CQL)
|
|
464
|
+
|
|
465
|
+
Use Confluence Query Language (CQL) for powerful searches:
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
const results = await client.search.search({
|
|
469
|
+
cql: 'type=page AND space=DEMO AND title~"getting started"',
|
|
470
|
+
limit: 20,
|
|
471
|
+
expand: ['content.space', 'content.version'],
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
for (const result of results.results || []) {
|
|
475
|
+
console.log(`${result.content?.title} - ${result.content?.id}`);
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Common CQL Patterns
|
|
480
|
+
|
|
481
|
+
```ts
|
|
482
|
+
// Find pages in a space
|
|
483
|
+
const pagesInSpace = await client.search.search({
|
|
484
|
+
cql: 'type=page AND space=DEMO',
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// Find pages by creator
|
|
488
|
+
const myPages = await client.search.search({
|
|
489
|
+
cql: 'type=page AND creator=currentUser()',
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// Find recently modified content
|
|
493
|
+
const recent = await client.search.search({
|
|
494
|
+
cql: 'type=page AND lastModified > now("-7d") ORDER BY lastModified DESC',
|
|
495
|
+
limit: 10,
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
// Find pages with specific label
|
|
499
|
+
const tagged = await client.search.search({
|
|
500
|
+
cql: 'type=page AND label="documentation"',
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// Complex search with AND/OR
|
|
504
|
+
const complex = await client.search.search({
|
|
505
|
+
cql: 'type=page AND space IN (DEMO, PROJ) AND (title~"guide" OR text~"tutorial")',
|
|
506
|
+
});
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
### Search by Title
|
|
510
|
+
|
|
511
|
+
```ts
|
|
512
|
+
const results = await client.search.searchContent({
|
|
513
|
+
title: 'Installation',
|
|
514
|
+
limit: 10,
|
|
515
|
+
});
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## Attachments API
|
|
519
|
+
|
|
520
|
+
### Get Attachments for Page
|
|
521
|
+
|
|
522
|
+
```ts
|
|
523
|
+
const attachments = await client.content.getAttachments({
|
|
524
|
+
id: '123456789',
|
|
525
|
+
limit: 50,
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
for (const attachment of attachments.results || []) {
|
|
529
|
+
console.log(`${attachment.title} - ${attachment.extensions?.fileSize} bytes`);
|
|
530
|
+
console.log(`Download: ${attachment._links?.download}`);
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Upload an Attachment
|
|
535
|
+
|
|
536
|
+
```ts
|
|
537
|
+
import fs from 'fs';
|
|
538
|
+
import FormData from 'form-data';
|
|
539
|
+
|
|
540
|
+
const form = new FormData();
|
|
541
|
+
form.append('file', fs.createReadStream('/path/to/file.pdf'), 'file.pdf');
|
|
542
|
+
form.append('comment', 'Uploaded via API');
|
|
543
|
+
|
|
544
|
+
const attachment = await client.content.createAttachment({
|
|
545
|
+
id: '123456789',
|
|
546
|
+
formData: form,
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
console.log(`Uploaded: ${attachment.results?.[0]?.title}`);
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Update an Existing Attachment
|
|
553
|
+
|
|
554
|
+
```ts
|
|
555
|
+
const form = new FormData();
|
|
556
|
+
form.append('file', fs.createReadStream('/path/to/updated-file.pdf'), 'file.pdf');
|
|
557
|
+
form.append('comment', 'Updated version');
|
|
558
|
+
|
|
559
|
+
const updated = await client.content.updateAttachment({
|
|
560
|
+
id: '123456789',
|
|
561
|
+
attachmentId: '987654321',
|
|
562
|
+
formData: form,
|
|
563
|
+
});
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### Download an Attachment
|
|
567
|
+
|
|
568
|
+
```ts
|
|
569
|
+
// Get attachment metadata first
|
|
570
|
+
const attachments = await client.content.getAttachments({
|
|
571
|
+
id: '123456789',
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
const attachment = attachments.results?.[0];
|
|
575
|
+
if (attachment) {
|
|
576
|
+
const downloadUrl = `https://your-domain.atlassian.net${attachment._links?.download}`;
|
|
577
|
+
// Use fetch or axios to download
|
|
578
|
+
console.log(`Download from: ${downloadUrl}`);
|
|
579
|
+
}
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
## Labels API
|
|
583
|
+
|
|
584
|
+
### Get Labels for Content
|
|
585
|
+
|
|
586
|
+
```ts
|
|
587
|
+
const labels = await client.content.getLabels({ id: '123456789' });
|
|
588
|
+
|
|
589
|
+
for (const label of labels.results || []) {
|
|
590
|
+
console.log(`Label: ${label.name}`);
|
|
591
|
+
}
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
### Add Labels to Content
|
|
595
|
+
|
|
596
|
+
```ts
|
|
597
|
+
const newLabels = await client.content.addLabels({
|
|
598
|
+
id: '123456789',
|
|
599
|
+
labels: [
|
|
600
|
+
{ name: 'documentation' },
|
|
601
|
+
{ name: 'getting-started' },
|
|
602
|
+
{ name: 'tutorial' },
|
|
603
|
+
],
|
|
604
|
+
});
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
### Remove a Label
|
|
608
|
+
|
|
609
|
+
```ts
|
|
610
|
+
await client.content.removeLabel({
|
|
611
|
+
id: '123456789',
|
|
612
|
+
label: 'old-label',
|
|
613
|
+
});
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
## Comments API
|
|
617
|
+
|
|
618
|
+
### Get Comments for Page
|
|
619
|
+
|
|
620
|
+
```ts
|
|
621
|
+
const comments = await client.content.getContentComments({
|
|
622
|
+
id: '123456789',
|
|
623
|
+
expand: ['body.storage'],
|
|
624
|
+
limit: 50,
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
for (const comment of comments.results || []) {
|
|
628
|
+
console.log(`Comment by ${comment.history?.createdBy?.displayName}:`);
|
|
629
|
+
console.log(comment.body?.storage?.value);
|
|
630
|
+
}
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### Add a Comment
|
|
634
|
+
|
|
635
|
+
```ts
|
|
636
|
+
const comment = await client.content.createContent({
|
|
637
|
+
type: 'comment',
|
|
638
|
+
container: { id: '123456789', type: 'page' },
|
|
639
|
+
body: {
|
|
640
|
+
storage: {
|
|
641
|
+
value: '<p>This is a helpful comment!</p>',
|
|
642
|
+
representation: 'storage',
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
});
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
## User and Group APIs
|
|
649
|
+
|
|
650
|
+
### Get Current User
|
|
651
|
+
|
|
652
|
+
```ts
|
|
653
|
+
const user = await client.user.getCurrentUser();
|
|
654
|
+
console.log(`Logged in as: ${user.displayName} (${user.email})`);
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
### Get User by Account ID
|
|
658
|
+
|
|
659
|
+
```ts
|
|
660
|
+
const user = await client.user.getUser({
|
|
661
|
+
accountId: '5a1234567890123456789012',
|
|
662
|
+
});
|
|
663
|
+
console.log(user.displayName);
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
### Get Group Members
|
|
667
|
+
|
|
668
|
+
```ts
|
|
669
|
+
const members = await client.group.getMembers({
|
|
670
|
+
name: 'confluence-administrators',
|
|
671
|
+
limit: 50,
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
for (const member of members.results || []) {
|
|
675
|
+
console.log(member.displayName);
|
|
676
|
+
}
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
## Content Properties (Metadata)
|
|
680
|
+
|
|
681
|
+
### Get Content Property
|
|
682
|
+
|
|
683
|
+
```ts
|
|
684
|
+
const property = await client.content.getContentProperty({
|
|
685
|
+
id: '123456789',
|
|
686
|
+
key: 'custom-metadata',
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
console.log(property.value);
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
### Set Content Property
|
|
693
|
+
|
|
694
|
+
```ts
|
|
695
|
+
await client.content.createContentProperty({
|
|
696
|
+
id: '123456789',
|
|
697
|
+
key: 'custom-metadata',
|
|
698
|
+
value: {
|
|
699
|
+
lastReviewed: '2025-11-07',
|
|
700
|
+
reviewer: 'john.doe@example.com',
|
|
701
|
+
status: 'approved',
|
|
702
|
+
},
|
|
703
|
+
});
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### Update Content Property
|
|
707
|
+
|
|
708
|
+
```ts
|
|
709
|
+
const current = await client.content.getContentProperty({
|
|
710
|
+
id: '123456789',
|
|
711
|
+
key: 'custom-metadata',
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
await client.content.updateContentProperty({
|
|
715
|
+
id: '123456789',
|
|
716
|
+
key: 'custom-metadata',
|
|
717
|
+
version: {
|
|
718
|
+
number: current.version!.number! + 1,
|
|
719
|
+
},
|
|
720
|
+
value: {
|
|
721
|
+
...current.value,
|
|
722
|
+
status: 'needs-review',
|
|
723
|
+
},
|
|
724
|
+
});
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### Delete Content Property
|
|
728
|
+
|
|
729
|
+
```ts
|
|
730
|
+
await client.content.deleteContentProperty({
|
|
731
|
+
id: '123456789',
|
|
732
|
+
key: 'custom-metadata',
|
|
733
|
+
});
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
## Advanced Content Operations
|
|
737
|
+
|
|
738
|
+
### Get Content Descendants
|
|
739
|
+
|
|
740
|
+
```ts
|
|
741
|
+
const descendants = await client.content.getContentDescendants({
|
|
742
|
+
id: '123456789',
|
|
743
|
+
expand: ['page'],
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
console.log('All descendant pages:');
|
|
747
|
+
for (const page of descendants.page?.results || []) {
|
|
748
|
+
console.log(` ${page.title} (${page.id})`);
|
|
749
|
+
}
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
### Copy a Page
|
|
753
|
+
|
|
754
|
+
```ts
|
|
755
|
+
const original = await client.content.getContent({
|
|
756
|
+
id: '123456789',
|
|
757
|
+
expand: ['body.storage', 'space'],
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
const copy = await client.content.createContent({
|
|
761
|
+
type: 'page',
|
|
762
|
+
title: `${original.title} (Copy)`,
|
|
763
|
+
space: { key: original.space?.key },
|
|
764
|
+
body: original.body,
|
|
765
|
+
});
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
### Move a Page to Different Parent
|
|
769
|
+
|
|
770
|
+
```ts
|
|
771
|
+
const page = await client.content.getContent({
|
|
772
|
+
id: '123456789',
|
|
773
|
+
expand: ['version', 'space', 'ancestors'],
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
await client.content.updateContent({
|
|
777
|
+
id: '123456789',
|
|
778
|
+
type: 'page',
|
|
779
|
+
title: page.title,
|
|
780
|
+
space: { key: page.space?.key },
|
|
781
|
+
version: {
|
|
782
|
+
number: page.version!.number! + 1,
|
|
783
|
+
},
|
|
784
|
+
ancestors: [{ id: 'new-parent-id' }],
|
|
785
|
+
body: page.body,
|
|
786
|
+
});
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
### Archive a Page
|
|
790
|
+
|
|
791
|
+
```ts
|
|
792
|
+
await client.content.archivePage({ id: '123456789' });
|
|
793
|
+
console.log('Page archived');
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
### Restore from Trash
|
|
797
|
+
|
|
798
|
+
```ts
|
|
799
|
+
const restored = await client.content.restore({
|
|
800
|
+
id: '123456789',
|
|
801
|
+
});
|
|
802
|
+
console.log('Page restored');
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
## Content Restrictions (Permissions)
|
|
806
|
+
|
|
807
|
+
### Get Content Restrictions
|
|
808
|
+
|
|
809
|
+
```ts
|
|
810
|
+
const restrictions = await client.content.getContentRestrictions({
|
|
811
|
+
id: '123456789',
|
|
812
|
+
expand: ['read.restrictions.user', 'update.restrictions.user'],
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
console.log('Read restrictions:', restrictions.read);
|
|
816
|
+
console.log('Update restrictions:', restrictions.update);
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
### Add Read Restriction
|
|
820
|
+
|
|
821
|
+
```ts
|
|
822
|
+
await client.content.addContentRestriction({
|
|
823
|
+
id: '123456789',
|
|
824
|
+
operation: 'read',
|
|
825
|
+
restrictions: {
|
|
826
|
+
user: [
|
|
827
|
+
{ accountId: '5a1234567890123456789012' },
|
|
828
|
+
],
|
|
829
|
+
group: [
|
|
830
|
+
{ name: 'confluence-users' },
|
|
831
|
+
],
|
|
832
|
+
},
|
|
833
|
+
});
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### Remove All Restrictions
|
|
837
|
+
|
|
838
|
+
```ts
|
|
839
|
+
await client.content.deleteContentRestriction({
|
|
840
|
+
id: '123456789',
|
|
841
|
+
operation: 'read',
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
await client.content.deleteContentRestriction({
|
|
845
|
+
id: '123456789',
|
|
846
|
+
operation: 'update',
|
|
847
|
+
});
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
## Macros in Content
|
|
851
|
+
|
|
852
|
+
### Insert a Table of Contents Macro
|
|
853
|
+
|
|
854
|
+
```ts
|
|
855
|
+
const page = await client.content.createContent({
|
|
856
|
+
type: 'page',
|
|
857
|
+
title: 'Documentation Index',
|
|
858
|
+
space: { key: 'DEMO' },
|
|
859
|
+
body: {
|
|
860
|
+
storage: {
|
|
861
|
+
value: `
|
|
862
|
+
<h1>Table of Contents</h1>
|
|
863
|
+
<ac:structured-macro ac:name="toc" ac:schema-version="1">
|
|
864
|
+
<ac:parameter ac:name="maxLevel">3</ac:parameter>
|
|
865
|
+
</ac:structured-macro>
|
|
866
|
+
<h2>Section 1</h2>
|
|
867
|
+
<p>Content here...</p>
|
|
868
|
+
`,
|
|
869
|
+
representation: 'storage',
|
|
870
|
+
},
|
|
871
|
+
},
|
|
872
|
+
});
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
### Insert a Status Macro
|
|
876
|
+
|
|
877
|
+
```ts
|
|
878
|
+
const content = `
|
|
879
|
+
<p>Project status:
|
|
880
|
+
<ac:structured-macro ac:name="status" ac:schema-version="1">
|
|
881
|
+
<ac:parameter ac:name="colour">Green</ac:parameter>
|
|
882
|
+
<ac:parameter ac:name="title">Active</ac:parameter>
|
|
883
|
+
</ac:structured-macro>
|
|
884
|
+
</p>
|
|
885
|
+
`;
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
### Insert a Code Block Macro
|
|
889
|
+
|
|
890
|
+
```ts
|
|
891
|
+
const content = `
|
|
892
|
+
<ac:structured-macro ac:name="code" ac:schema-version="1">
|
|
893
|
+
<ac:parameter ac:name="language">javascript</ac:parameter>
|
|
894
|
+
<ac:plain-text-body><![CDATA[
|
|
895
|
+
const greeting = 'Hello, World!';
|
|
896
|
+
console.log(greeting);
|
|
897
|
+
]]></ac:plain-text-body>
|
|
898
|
+
</ac:structured-macro>
|
|
899
|
+
`;
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
### Insert an Info Panel Macro
|
|
903
|
+
|
|
904
|
+
```ts
|
|
905
|
+
const content = `
|
|
906
|
+
<ac:structured-macro ac:name="info" ac:schema-version="1">
|
|
907
|
+
<ac:rich-text-body>
|
|
908
|
+
<p>This is important information for users to know.</p>
|
|
909
|
+
</ac:rich-text-body>
|
|
910
|
+
</ac:structured-macro>
|
|
911
|
+
`;
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
## Error Handling
|
|
915
|
+
|
|
916
|
+
### Basic Error Handling
|
|
917
|
+
|
|
918
|
+
```ts
|
|
919
|
+
try {
|
|
920
|
+
const page = await client.content.getContent({ id: 'invalid-id' });
|
|
921
|
+
} catch (error) {
|
|
922
|
+
if (error.response) {
|
|
923
|
+
console.error('Status:', error.response.status);
|
|
924
|
+
console.error('Message:', error.response.data?.message);
|
|
925
|
+
} else {
|
|
926
|
+
console.error('Error:', error.message);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
### Common HTTP Status Codes
|
|
932
|
+
|
|
933
|
+
- `400 Bad Request`: Invalid parameters or request body
|
|
934
|
+
- `401 Unauthorized`: Invalid or missing authentication
|
|
935
|
+
- `403 Forbidden`: Insufficient permissions
|
|
936
|
+
- `404 Not Found`: Content or space does not exist
|
|
937
|
+
- `409 Conflict`: Version conflict (update with wrong version number)
|
|
938
|
+
- `429 Too Many Requests`: Rate limit exceeded
|
|
939
|
+
|
|
940
|
+
### Handling Version Conflicts
|
|
941
|
+
|
|
942
|
+
```ts
|
|
943
|
+
async function updatePageWithRetry(pageId: string, newContent: string, maxRetries = 3) {
|
|
944
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
945
|
+
try {
|
|
946
|
+
const page = await client.content.getContent({
|
|
947
|
+
id: pageId,
|
|
948
|
+
expand: ['version', 'space'],
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
const updated = await client.content.updateContent({
|
|
952
|
+
id: pageId,
|
|
953
|
+
type: 'page',
|
|
954
|
+
title: page.title,
|
|
955
|
+
space: { key: page.space?.key },
|
|
956
|
+
version: {
|
|
957
|
+
number: page.version!.number! + 1,
|
|
958
|
+
},
|
|
959
|
+
body: {
|
|
960
|
+
storage: {
|
|
961
|
+
value: newContent,
|
|
962
|
+
representation: 'storage',
|
|
963
|
+
},
|
|
964
|
+
},
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
return updated;
|
|
968
|
+
} catch (error) {
|
|
969
|
+
if (error.response?.status === 409 && i < maxRetries - 1) {
|
|
970
|
+
console.log(`Conflict detected, retrying (${i + 1}/${maxRetries})...`);
|
|
971
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
throw error;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
### Rate Limit Handling
|
|
981
|
+
|
|
982
|
+
```ts
|
|
983
|
+
async function apiCallWithBackoff<T>(
|
|
984
|
+
apiCall: () => Promise<T>,
|
|
985
|
+
maxRetries = 5
|
|
986
|
+
): Promise<T> {
|
|
987
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
988
|
+
try {
|
|
989
|
+
return await apiCall();
|
|
990
|
+
} catch (error) {
|
|
991
|
+
if (error.response?.status === 429 && i < maxRetries - 1) {
|
|
992
|
+
const retryAfter = error.response.headers['retry-after'] || Math.pow(2, i);
|
|
993
|
+
console.log(`Rate limited, waiting ${retryAfter}s before retry...`);
|
|
994
|
+
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
throw error;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
throw new Error('Max retries exceeded');
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// Usage
|
|
1004
|
+
const page = await apiCallWithBackoff(() =>
|
|
1005
|
+
client.content.getContent({ id: '123456789' })
|
|
1006
|
+
);
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
## Bulk Operations
|
|
1010
|
+
|
|
1011
|
+
### Get Multiple Pages by IDs
|
|
1012
|
+
|
|
1013
|
+
```ts
|
|
1014
|
+
async function getPagesByIds(pageIds: string[]) {
|
|
1015
|
+
const pages = await Promise.all(
|
|
1016
|
+
pageIds.map(id =>
|
|
1017
|
+
client.content.getContent({
|
|
1018
|
+
id,
|
|
1019
|
+
expand: ['body.storage', 'version']
|
|
1020
|
+
})
|
|
1021
|
+
)
|
|
1022
|
+
);
|
|
1023
|
+
return pages;
|
|
1024
|
+
}
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
### Batch Create Pages
|
|
1028
|
+
|
|
1029
|
+
```ts
|
|
1030
|
+
async function createMultiplePages(
|
|
1031
|
+
spaceKey: string,
|
|
1032
|
+
pageData: Array<{ title: string; content: string }>
|
|
1033
|
+
) {
|
|
1034
|
+
const created = [];
|
|
1035
|
+
|
|
1036
|
+
for (const { title, content } of pageData) {
|
|
1037
|
+
const page = await client.content.createContent({
|
|
1038
|
+
type: 'page',
|
|
1039
|
+
title,
|
|
1040
|
+
space: { key: spaceKey },
|
|
1041
|
+
body: {
|
|
1042
|
+
storage: {
|
|
1043
|
+
value: content,
|
|
1044
|
+
representation: 'storage',
|
|
1045
|
+
},
|
|
1046
|
+
},
|
|
1047
|
+
});
|
|
1048
|
+
created.push(page);
|
|
1049
|
+
|
|
1050
|
+
// Rate limit protection: wait between requests
|
|
1051
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
return created;
|
|
1055
|
+
}
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
### Batch Update Pages
|
|
1059
|
+
|
|
1060
|
+
```ts
|
|
1061
|
+
async function updateMultiplePages(
|
|
1062
|
+
updates: Array<{ id: string; title?: string; content: string }>
|
|
1063
|
+
) {
|
|
1064
|
+
const results = [];
|
|
1065
|
+
|
|
1066
|
+
for (const { id, title, content } of updates) {
|
|
1067
|
+
const current = await client.content.getContent({
|
|
1068
|
+
id,
|
|
1069
|
+
expand: ['version', 'space'],
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
const updated = await client.content.updateContent({
|
|
1073
|
+
id,
|
|
1074
|
+
type: 'page',
|
|
1075
|
+
title: title || current.title,
|
|
1076
|
+
space: { key: current.space?.key },
|
|
1077
|
+
version: {
|
|
1078
|
+
number: current.version!.number! + 1,
|
|
1079
|
+
},
|
|
1080
|
+
body: {
|
|
1081
|
+
storage: {
|
|
1082
|
+
value: content,
|
|
1083
|
+
representation: 'storage',
|
|
1084
|
+
},
|
|
1085
|
+
},
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
results.push(updated);
|
|
1089
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
return results;
|
|
1093
|
+
}
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
## Performance and Optimization
|
|
1097
|
+
|
|
1098
|
+
### Use Expand Wisely
|
|
1099
|
+
|
|
1100
|
+
Only expand fields you need to reduce response size:
|
|
1101
|
+
|
|
1102
|
+
```ts
|
|
1103
|
+
// Bad - expands everything
|
|
1104
|
+
const page = await client.content.getContent({
|
|
1105
|
+
id: '123456789',
|
|
1106
|
+
expand: ['body.storage', 'body.view', 'body.editor', 'version', 'space', 'history', 'ancestors', 'descendants', 'container'],
|
|
1107
|
+
});
|
|
1108
|
+
|
|
1109
|
+
// Good - only what you need
|
|
1110
|
+
const page = await client.content.getContent({
|
|
1111
|
+
id: '123456789',
|
|
1112
|
+
expand: ['body.storage', 'version'],
|
|
1113
|
+
});
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
### Pagination Best Practices
|
|
1117
|
+
|
|
1118
|
+
```ts
|
|
1119
|
+
async function getAllPagesInSpace(spaceKey: string) {
|
|
1120
|
+
const allPages = [];
|
|
1121
|
+
let start = 0;
|
|
1122
|
+
const limit = 100; // Max recommended: 100
|
|
1123
|
+
|
|
1124
|
+
while (true) {
|
|
1125
|
+
const response = await client.content.getContent({
|
|
1126
|
+
spaceKey,
|
|
1127
|
+
type: 'page',
|
|
1128
|
+
start,
|
|
1129
|
+
limit,
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
if (!response.results || response.results.length === 0) {
|
|
1133
|
+
break;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
allPages.push(...response.results);
|
|
1137
|
+
|
|
1138
|
+
if (response.results.length < limit) {
|
|
1139
|
+
break;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
start += limit;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
return allPages;
|
|
1146
|
+
}
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
### Caching Strategies
|
|
1150
|
+
|
|
1151
|
+
```ts
|
|
1152
|
+
class ConfluenceCache {
|
|
1153
|
+
private cache = new Map<string, { data: any; expires: number }>();
|
|
1154
|
+
|
|
1155
|
+
constructor(private client: ConfluenceClient, private ttlMs = 60000) {}
|
|
1156
|
+
|
|
1157
|
+
async getPage(id: string) {
|
|
1158
|
+
const cached = this.cache.get(id);
|
|
1159
|
+
|
|
1160
|
+
if (cached && Date.now() < cached.expires) {
|
|
1161
|
+
return cached.data;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
const page = await this.client.content.getContent({ id });
|
|
1165
|
+
|
|
1166
|
+
this.cache.set(id, {
|
|
1167
|
+
data: page,
|
|
1168
|
+
expires: Date.now() + this.ttlMs,
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
return page;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
invalidate(id: string) {
|
|
1175
|
+
this.cache.delete(id);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
## TypeScript Support
|
|
1181
|
+
|
|
1182
|
+
### Typed Responses
|
|
1183
|
+
|
|
1184
|
+
```ts
|
|
1185
|
+
import { Content, Space, User } from 'confluence.js/out/api/models';
|
|
1186
|
+
|
|
1187
|
+
const page: Content = await client.content.getContent({ id: '123456789' });
|
|
1188
|
+
const space: Space = await client.space.getSpace({ spaceKey: 'DEMO' });
|
|
1189
|
+
const user: User = await client.user.getCurrentUser();
|
|
1190
|
+
```
|
|
1191
|
+
|
|
1192
|
+
### Type-Safe Page Creation
|
|
1193
|
+
|
|
1194
|
+
```ts
|
|
1195
|
+
import { ContentCreate } from 'confluence.js/out/api/models';
|
|
1196
|
+
|
|
1197
|
+
const pageData: ContentCreate = {
|
|
1198
|
+
type: 'page',
|
|
1199
|
+
title: 'Type-Safe Page',
|
|
1200
|
+
space: { key: 'DEMO' },
|
|
1201
|
+
body: {
|
|
1202
|
+
storage: {
|
|
1203
|
+
value: '<p>Content</p>',
|
|
1204
|
+
representation: 'storage',
|
|
1205
|
+
},
|
|
1206
|
+
},
|
|
1207
|
+
};
|
|
1208
|
+
|
|
1209
|
+
const created = await client.content.createContent(pageData);
|
|
1210
|
+
```
|
|
1211
|
+
|
|
1212
|
+
## Common Patterns
|
|
1213
|
+
|
|
1214
|
+
### Create or Update Page
|
|
1215
|
+
|
|
1216
|
+
```ts
|
|
1217
|
+
async function createOrUpdatePage(
|
|
1218
|
+
spaceKey: string,
|
|
1219
|
+
title: string,
|
|
1220
|
+
content: string
|
|
1221
|
+
) {
|
|
1222
|
+
// Try to find existing page
|
|
1223
|
+
const searchResult = await client.content.getContent({
|
|
1224
|
+
spaceKey,
|
|
1225
|
+
title,
|
|
1226
|
+
expand: ['version', 'space'],
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
if (searchResult.results && searchResult.results.length > 0) {
|
|
1230
|
+
// Update existing
|
|
1231
|
+
const existing = searchResult.results[0];
|
|
1232
|
+
return await client.content.updateContent({
|
|
1233
|
+
id: existing.id!,
|
|
1234
|
+
type: 'page',
|
|
1235
|
+
title,
|
|
1236
|
+
space: { key: spaceKey },
|
|
1237
|
+
version: {
|
|
1238
|
+
number: existing.version!.number! + 1,
|
|
1239
|
+
},
|
|
1240
|
+
body: {
|
|
1241
|
+
storage: {
|
|
1242
|
+
value: content,
|
|
1243
|
+
representation: 'storage',
|
|
1244
|
+
},
|
|
1245
|
+
},
|
|
1246
|
+
});
|
|
1247
|
+
} else {
|
|
1248
|
+
// Create new
|
|
1249
|
+
return await client.content.createContent({
|
|
1250
|
+
type: 'page',
|
|
1251
|
+
title,
|
|
1252
|
+
space: { key: spaceKey },
|
|
1253
|
+
body: {
|
|
1254
|
+
storage: {
|
|
1255
|
+
value: content,
|
|
1256
|
+
representation: 'storage',
|
|
1257
|
+
},
|
|
1258
|
+
},
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
```
|
|
1263
|
+
|
|
1264
|
+
### Build Table of Contents
|
|
1265
|
+
|
|
1266
|
+
```ts
|
|
1267
|
+
async function buildTableOfContents(spaceKey: string) {
|
|
1268
|
+
const pages = await client.content.getContent({
|
|
1269
|
+
spaceKey,
|
|
1270
|
+
type: 'page',
|
|
1271
|
+
limit: 100,
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
let tocHtml = '<h2>Table of Contents</h2><ul>';
|
|
1275
|
+
|
|
1276
|
+
for (const page of pages.results || []) {
|
|
1277
|
+
tocHtml += `<li><ac:link><ri:page ri:content-title="${page.title}"/></ac:link></li>`;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
tocHtml += '</ul>';
|
|
1281
|
+
|
|
1282
|
+
return tocHtml;
|
|
1283
|
+
}
|
|
1284
|
+
```
|
|
1285
|
+
|
|
1286
|
+
### Export Page to Different Format
|
|
1287
|
+
|
|
1288
|
+
```ts
|
|
1289
|
+
async function exportPageAsPDF(pageId: string) {
|
|
1290
|
+
const page = await client.content.getContent({
|
|
1291
|
+
id: pageId,
|
|
1292
|
+
expand: ['body.export_view'],
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1295
|
+
// Export view provides HTML optimized for export
|
|
1296
|
+
return page.body?.export_view?.value;
|
|
1297
|
+
}
|
|
1298
|
+
```
|
|
1299
|
+
|
|
1300
|
+
## Content Body Formats
|
|
1301
|
+
|
|
1302
|
+
Confluence supports multiple body representations:
|
|
1303
|
+
|
|
1304
|
+
- `storage`: XHTML format used for storage (required for create/update)
|
|
1305
|
+
- `view`: HTML for rendering in browser
|
|
1306
|
+
- `export_view`: HTML optimized for export
|
|
1307
|
+
- `editor`: Format used in the editor
|
|
1308
|
+
- `anonymous_export_view`: HTML for anonymous viewing
|
|
1309
|
+
|
|
1310
|
+
```ts
|
|
1311
|
+
const page = await client.content.getContent({
|
|
1312
|
+
id: '123456789',
|
|
1313
|
+
expand: ['body.storage', 'body.view', 'body.export_view'],
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
console.log('Storage format:', page.body?.storage?.value);
|
|
1317
|
+
console.log('View format:', page.body?.view?.value);
|
|
1318
|
+
console.log('Export format:', page.body?.export_view?.value);
|
|
1319
|
+
```
|
|
1320
|
+
|
|
1321
|
+
## Common Mistakes to Avoid
|
|
1322
|
+
|
|
1323
|
+
- **Not incrementing version number** when updating content (causes 409 conflict)
|
|
1324
|
+
- **Forgetting to expand fields** needed in response (results in undefined properties)
|
|
1325
|
+
- **Using wrong representation** for body content (use 'storage' for create/update)
|
|
1326
|
+
- **Not handling pagination** when fetching multiple items (misses items beyond first page)
|
|
1327
|
+
- **Hardcoding credentials** instead of using environment variables (security risk)
|
|
1328
|
+
- **Not handling rate limits** in bulk operations (causes 429 errors)
|
|
1329
|
+
- **Creating pages without checking if they exist** (creates duplicates)
|
|
1330
|
+
- **Not URL-encoding** special characters in space keys or titles
|
|
1331
|
+
- **Assuming synchronous operations** (all API calls are async and return promises)
|
|
1332
|
+
- **Not catching errors** properly (unhandled rejections crash Node.js apps)
|
|
1333
|
+
- **Using outdated packages** like confluence-api instead of confluence.js
|
|
1334
|
+
- **Forgetting authentication object** in ConfluenceClient initialization
|
|
1335
|
+
- **Not escaping HTML** in content values (causes malformed XML in storage format)
|
|
1336
|
+
|
|
1337
|
+
## Reference Links
|
|
1338
|
+
|
|
1339
|
+
- **confluence.js Documentation**: https://mrrefactoring.github.io/confluence.js/
|
|
1340
|
+
- **Confluence Cloud REST API v2**: https://developer.atlassian.com/cloud/confluence/rest/v2/
|
|
1341
|
+
- **Confluence Cloud REST API v1**: https://developer.atlassian.com/cloud/confluence/rest/v1/
|
|
1342
|
+
- **CQL (Confluence Query Language)**: https://developer.atlassian.com/cloud/confluence/advanced-searching-using-cql/
|
|
1343
|
+
- **Storage Format Guide**: https://confluence.atlassian.com/doc/confluence-storage-format-790796544.html
|
|
1344
|
+
- **Atlassian API Tokens**: https://id.atlassian.com/manage-profile/security/api-tokens
|
|
1345
|
+
- **Rate Limits**: https://developer.atlassian.com/cloud/confluence/rate-limiting/
|
|
1346
|
+
- **OAuth 2.0 (3LO)**: https://developer.atlassian.com/cloud/confluence/oauth-2-3lo-apps/
|
|
1347
|
+
- **Atlassian Connect (JWT)**: https://developer.atlassian.com/cloud/confluence/authentication-for-apps/
|