or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

blog-content.mdclient-configuration.mdcontent-discovery.mdhttp-requests.mdindex.mdpost-management.mdsocial-interactions.mduser-management.md

post-management.mddocs/

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

```