0
# Post Management
1
2
Create, edit, and delete posts using modern NPF (Neue Post Format) with media upload support and legacy post format compatibility.
3
4
## Capabilities
5
6
### Create NPF Post
7
8
Create new posts or reblog existing posts using Tumblr's modern NPF (Neue Post Format).
9
10
```javascript { .api }
11
/**
12
* Create or reblog an NPF post
13
* @param blogIdentifier - Blog name or URL
14
* @param params - Post creation parameters
15
* @returns Promise resolving to created post data
16
*/
17
createPost(blogIdentifier: string, params: NpfPostParams | NpfReblogParams): Promise<any>;
18
```
19
20
**Usage Examples:**
21
22
```javascript
23
const fs = require('fs');
24
25
// Create a simple text post
26
await client.createPost('myblog', {
27
content: [
28
{
29
type: 'text',
30
text: 'Hello, Tumblr! This is my first NPF post.'
31
}
32
],
33
tags: ['first-post', 'hello-world']
34
});
35
36
// Create an image post with media upload
37
await client.createPost('myblog', {
38
content: [
39
{
40
type: 'image',
41
media: fs.createReadStream('./photo.jpg'),
42
alt_text: 'A beautiful sunset photo'
43
},
44
{
45
type: 'text',
46
text: 'Captured this amazing sunset today!'
47
}
48
],
49
state: 'published',
50
tags: ['photography', 'sunset']
51
});
52
53
// Create a multi-media post
54
await client.createPost('myblog', {
55
content: [
56
{
57
type: 'text',
58
text: 'Check out this amazing song and video!'
59
},
60
{
61
type: 'audio',
62
media: fs.createReadStream('./song.mp3')
63
},
64
{
65
type: 'video',
66
media: fs.createReadStream('./video.mp4')
67
}
68
],
69
layout: [
70
{
71
type: 'rows',
72
display: [
73
{ blocks: [0] }, // Text block
74
{ blocks: [1, 2] } // Audio and video side by side
75
]
76
}
77
]
78
});
79
80
// Reblog an existing post
81
await client.createPost('myblog', {
82
parent_tumblelog_uuid: 'original-blog-uuid',
83
parent_post_id: '12345',
84
reblog_key: 'reblog-key-from-api',
85
content: [
86
{
87
type: 'text',
88
text: 'This is so cool! Adding my thoughts.'
89
}
90
],
91
tags: ['reblog', 'interesting']
92
});
93
```
94
95
### Edit NPF Post
96
97
Edit existing NPF posts with updated content and metadata.
98
99
```javascript { .api }
100
/**
101
* Edit an NPF post
102
* @param blogIdentifier - Blog name or URL
103
* @param postId - Post ID to edit
104
* @param params - Updated post parameters
105
* @returns Promise resolving to updated post data
106
*/
107
editPost(blogIdentifier: string, postId: string, params: NpfPostParams | NpfReblogParams): Promise<any>;
108
```
109
110
**Usage Examples:**
111
112
```javascript
113
// Edit post content
114
await client.editPost('myblog', '67890', {
115
content: [
116
{
117
type: 'text',
118
text: 'Updated post content with new information!'
119
}
120
],
121
tags: ['updated', 'new-info']
122
});
123
124
// Add media to existing post
125
await client.editPost('myblog', '67890', {
126
content: [
127
{
128
type: 'text',
129
text: 'Original text content'
130
},
131
{
132
type: 'image',
133
media: fs.createReadStream('./new-image.jpg'),
134
alt_text: 'Newly added image'
135
}
136
]
137
});
138
```
139
140
### Delete Post
141
142
Delete posts from a blog.
143
144
```javascript { .api }
145
/**
146
* Delete a post
147
* @param blogIdentifier - Blog name or URL
148
* @param postId - Post ID to delete
149
* @returns Promise resolving to deletion confirmation
150
*/
151
deletePost(blogIdentifier: string, postId: string): Promise<any>;
152
```
153
154
**Usage Examples:**
155
156
```javascript
157
// Delete a post
158
await client.deletePost('myblog', '12345');
159
console.log('Post deleted successfully');
160
```
161
162
### Legacy Post Methods (Deprecated)
163
164
Legacy post creation methods for backwards compatibility.
165
166
```javascript { .api }
167
/**
168
* Create a legacy post
169
* @deprecated Use createPost with NPF format
170
* @param blogIdentifier - Blog name or URL
171
* @param params - Legacy post parameters
172
* @returns Promise resolving to created post data
173
*/
174
createLegacyPost(blogIdentifier: string, params: Record<string, any>): Promise<any>;
175
176
/**
177
* Edit a legacy post
178
* @deprecated Use editPost with NPF format
179
* @param blogIdentifier - Blog name or URL
180
* @param params - Legacy post parameters
181
* @returns Promise resolving to updated post data
182
*/
183
editLegacyPost(blogIdentifier: string, params: Record<string, any>): Promise<any>;
184
185
/**
186
* Reblog a legacy post
187
* @deprecated Use createPost with NPF reblog parameters
188
* @param blogIdentifier - Blog name or URL
189
* @param params - Legacy reblog parameters
190
* @returns Promise resolving to reblog data
191
*/
192
reblogPost(blogIdentifier: string, params: Record<string, any>): Promise<any>;
193
```
194
195
## NPF Content Types
196
197
### Content Blocks
198
199
All NPF content is structured as an array of content blocks:
200
201
```javascript { .api }
202
type NpfContentBlock = AudioBlock | ImageBlock | LinkBlock | PaywallBlock | TextBlock | VideoBlock;
203
204
interface TextBlock {
205
type: 'text';
206
text?: string;
207
subtype?: 'heading1' | 'heading2' | 'quirky' | 'quote' | 'indented' | 'chat';
208
formatting?: TextFormatting[];
209
[prop: string]: any;
210
}
211
212
interface ImageBlock {
213
type: 'image';
214
media: ReadStream | MediaObject;
215
alt_text?: string;
216
caption?: string;
217
[prop: string]: any;
218
}
219
220
interface VideoBlock {
221
type: 'video';
222
media: ReadStream | MediaObject;
223
poster?: MediaObject;
224
[prop: string]: any;
225
}
226
227
interface AudioBlock {
228
type: 'audio';
229
media: ReadStream | MediaObject;
230
title?: string;
231
artist?: string;
232
album?: string;
233
[prop: string]: any;
234
}
235
236
interface LinkBlock {
237
type: 'link';
238
url: string;
239
title?: string;
240
description?: string;
241
author?: string;
242
site_name?: string;
243
poster?: MediaObject;
244
[prop: string]: any;
245
}
246
247
interface PaywallBlock {
248
type: 'paywall';
249
title?: string;
250
[prop: string]: any;
251
}
252
```
253
254
### Media Objects
255
256
Media can be provided as Node.js ReadStream for uploads or MediaObject for existing media:
257
258
```javascript { .api }
259
interface MediaObject {
260
/** The canonical URL of the media asset */
261
url: string;
262
/** The MIME type of the media asset */
263
type?: string;
264
/** The width of the media asset */
265
width?: number;
266
/** The height of the media asset */
267
height?: number;
268
/** For display purposes, indicates whether dimensions are defaults */
269
original_dimensions_missing?: boolean;
270
/** Indicates whether this media object has the same dimensions as the original */
271
has_original_dimensions?: boolean;
272
/** Indicates whether this media object is a cropped version of the original */
273
cropped?: boolean;
274
}
275
```
276
277
### Layout Blocks
278
279
Control the visual layout of content blocks:
280
281
```javascript { .api }
282
type NpfLayoutBlock = NpfLayoutAsk | NpfLayoutRows;
283
284
interface NpfLayoutRows {
285
type: 'rows';
286
display: Array<{
287
blocks: number[]; // Array of content block indices
288
mode?: { type: string };
289
}>;
290
truncate_after?: 1;
291
}
292
293
interface NpfLayoutAsk {
294
type: 'ask';
295
blocks: number[];
296
attribution: any;
297
}
298
```
299
300
## Post Parameters
301
302
### NPF Post Parameters
303
304
Parameters for creating new NPF posts:
305
306
```javascript { .api }
307
interface NpfPostParams {
308
/** Array of NPF content blocks */
309
content: NpfContentBlock[];
310
/** Array of NPF layout objects */
311
layout?: NpfLayoutBlock[];
312
/** Initial state of the post */
313
state?: PostState;
314
/** Future publish date (ISO 8601 format) */
315
publish_on?: string;
316
/** Backdate the post (ISO 8601 format) */
317
date?: string;
318
/** Tags to associate with the post */
319
tags?: string[];
320
/** Source attribution URL */
321
source_url?: string;
322
/** Whether this should be a private answer */
323
is_private?: boolean;
324
/** Custom URL slug */
325
slug?: string;
326
/** Who can interact when reblogging */
327
interactability_reblog?: 'everyone' | 'noone';
328
}
329
330
type PostState = 'published' | 'queue' | 'draft' | 'private' | 'unapproved';
331
```
332
333
### NPF Reblog Parameters
334
335
Parameters for reblogging posts using NPF:
336
337
```javascript { .api }
338
interface NpfReblogParams extends NpfPostParams {
339
/** The unique public identifier of the Tumblelog being reblogged from */
340
parent_tumblelog_uuid: string;
341
/** The unique public post ID being reblogged */
342
parent_post_id: string;
343
/** The unique per-post hash validating this is a genuine reblog action */
344
reblog_key: string;
345
/** Whether to hide the reblog trail */
346
hide_trail?: boolean;
347
/** Array of reblog trail item indexes to exclude */
348
exclude_trail_items?: boolean;
349
}
350
```
351
352
## Media Upload Process
353
354
### File Upload Example
355
356
```javascript
357
const fs = require('fs');
358
359
// Upload multiple media files
360
await client.createPost('myblog', {
361
content: [
362
{
363
type: 'text',
364
text: 'My vacation photos!'
365
},
366
{
367
type: 'image',
368
media: fs.createReadStream('./beach.jpg'),
369
alt_text: 'Beautiful beach scene'
370
},
371
{
372
type: 'image',
373
media: fs.createReadStream('./sunset.jpg'),
374
alt_text: 'Gorgeous sunset'
375
},
376
{
377
type: 'video',
378
media: fs.createReadStream('./waves.mp4')
379
}
380
],
381
layout: [
382
{
383
type: 'rows',
384
display: [
385
{ blocks: [0] }, // Text
386
{ blocks: [1, 2] }, // Two images side by side
387
{ blocks: [3] } // Video
388
]
389
}
390
]
391
});
392
```
393
394
### Media Processing
395
396
When media files are provided as ReadStreams:
397
398
1. The library automatically detects media upload requirement
399
2. Content is sent as multipart/form-data
400
3. Media streams are mapped to identifiers
401
4. JSON metadata is sent in a special 'json' field
402
5. Media streams are sent as separate form fields
403
404
## Error Handling
405
406
Common post management errors and handling:
407
408
```javascript
409
try {
410
await client.createPost('myblog', {
411
content: [
412
{
413
type: 'text',
414
text: 'My post content'
415
}
416
]
417
});
418
} catch (error) {
419
if (error.message.includes('401')) {
420
console.error('Authentication required - check your OAuth credentials');
421
} else if (error.message.includes('403')) {
422
console.error('Forbidden - check blog permissions');
423
} else if (error.message.includes('413')) {
424
console.error('Media file too large');
425
} else {
426
console.error('Post creation failed:', error.message);
427
}
428
}
429
```