or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

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

post-management.mddocs/

Post Management

Create, edit, and delete posts using modern NPF (Neue Post Format) with media upload support and legacy post format compatibility.

Capabilities

Create NPF Post

Create new posts or reblog existing posts using Tumblr's modern NPF (Neue Post Format).

/**
 * Create or reblog an NPF post
 * @param blogIdentifier - Blog name or URL
 * @param params - Post creation parameters
 * @returns Promise resolving to created post data
 */
createPost(blogIdentifier: string, params: NpfPostParams | NpfReblogParams): Promise<any>;

Usage Examples:

const fs = require('fs');

// Create a simple text post
await client.createPost('myblog', {
  content: [
    {
      type: 'text',
      text: 'Hello, Tumblr! This is my first NPF post.'
    }
  ],
  tags: ['first-post', 'hello-world']
});

// Create an image post with media upload
await client.createPost('myblog', {
  content: [
    {
      type: 'image',
      media: fs.createReadStream('./photo.jpg'),
      alt_text: 'A beautiful sunset photo'
    },
    {
      type: 'text',
      text: 'Captured this amazing sunset today!'
    }
  ],
  state: 'published',
  tags: ['photography', 'sunset']
});

// Create a multi-media post
await client.createPost('myblog', {
  content: [
    {
      type: 'text',
      text: 'Check out this amazing song and video!'
    },
    {
      type: 'audio',
      media: fs.createReadStream('./song.mp3')
    },
    {
      type: 'video',
      media: fs.createReadStream('./video.mp4')
    }
  ],
  layout: [
    {
      type: 'rows',
      display: [
        { blocks: [0] },  // Text block
        { blocks: [1, 2] } // Audio and video side by side
      ]
    }
  ]
});

// Reblog an existing post
await client.createPost('myblog', {
  parent_tumblelog_uuid: 'original-blog-uuid',
  parent_post_id: '12345',
  reblog_key: 'reblog-key-from-api',
  content: [
    {
      type: 'text',
      text: 'This is so cool! Adding my thoughts.'
    }
  ],
  tags: ['reblog', 'interesting']
});

Edit NPF Post

Edit existing NPF posts with updated content and metadata.

/**
 * Edit an NPF post
 * @param blogIdentifier - Blog name or URL
 * @param postId - Post ID to edit
 * @param params - Updated post parameters
 * @returns Promise resolving to updated post data
 */
editPost(blogIdentifier: string, postId: string, params: NpfPostParams | NpfReblogParams): Promise<any>;

Usage Examples:

// Edit post content
await client.editPost('myblog', '67890', {
  content: [
    {
      type: 'text',
      text: 'Updated post content with new information!'
    }
  ],
  tags: ['updated', 'new-info']
});

// Add media to existing post
await client.editPost('myblog', '67890', {
  content: [
    {
      type: 'text',
      text: 'Original text content'
    },
    {
      type: 'image',
      media: fs.createReadStream('./new-image.jpg'),
      alt_text: 'Newly added image'
    }
  ]
});

Delete Post

Delete posts from a blog.

/**
 * Delete a post
 * @param blogIdentifier - Blog name or URL
 * @param postId - Post ID to delete
 * @returns Promise resolving to deletion confirmation
 */
deletePost(blogIdentifier: string, postId: string): Promise<any>;

Usage Examples:

// Delete a post
await client.deletePost('myblog', '12345');
console.log('Post deleted successfully');

Legacy Post Methods (Deprecated)

Legacy post creation methods for backwards compatibility.

/**
 * Create a legacy post
 * @deprecated Use createPost with NPF format
 * @param blogIdentifier - Blog name or URL
 * @param params - Legacy post parameters
 * @returns Promise resolving to created post data
 */
createLegacyPost(blogIdentifier: string, params: Record<string, any>): Promise<any>;

/**
 * Edit a legacy post
 * @deprecated Use editPost with NPF format
 * @param blogIdentifier - Blog name or URL
 * @param params - Legacy post parameters
 * @returns Promise resolving to updated post data
 */
editLegacyPost(blogIdentifier: string, params: Record<string, any>): Promise<any>;

/**
 * Reblog a legacy post
 * @deprecated Use createPost with NPF reblog parameters
 * @param blogIdentifier - Blog name or URL
 * @param params - Legacy reblog parameters
 * @returns Promise resolving to reblog data
 */
reblogPost(blogIdentifier: string, params: Record<string, any>): Promise<any>;

NPF Content Types

Content Blocks

All NPF content is structured as an array of content blocks:

type NpfContentBlock = AudioBlock | ImageBlock | LinkBlock | PaywallBlock | TextBlock | VideoBlock;

interface TextBlock {
  type: 'text';
  text?: string;
  subtype?: 'heading1' | 'heading2' | 'quirky' | 'quote' | 'indented' | 'chat';
  formatting?: TextFormatting[];
  [prop: string]: any;
}

interface ImageBlock {
  type: 'image';
  media: ReadStream | MediaObject;
  alt_text?: string;
  caption?: string;
  [prop: string]: any;
}

interface VideoBlock {
  type: 'video';
  media: ReadStream | MediaObject;
  poster?: MediaObject;
  [prop: string]: any;
}

interface AudioBlock {
  type: 'audio';
  media: ReadStream | MediaObject;
  title?: string;
  artist?: string;
  album?: string;
  [prop: string]: any;
}

interface LinkBlock {
  type: 'link';
  url: string;
  title?: string;
  description?: string;
  author?: string;
  site_name?: string;
  poster?: MediaObject;
  [prop: string]: any;
}

interface PaywallBlock {
  type: 'paywall';
  title?: string;
  [prop: string]: any;
}

Media Objects

Media can be provided as Node.js ReadStream for uploads or MediaObject for existing media:

interface MediaObject {
  /** The canonical URL of the media asset */
  url: string;
  /** The MIME type of the media asset */
  type?: string;
  /** The width of the media asset */
  width?: number;
  /** The height of the media asset */
  height?: number;
  /** For display purposes, indicates whether dimensions are defaults */
  original_dimensions_missing?: boolean;
  /** Indicates whether this media object has the same dimensions as the original */
  has_original_dimensions?: boolean;
  /** Indicates whether this media object is a cropped version of the original */
  cropped?: boolean;
}

Layout Blocks

Control the visual layout of content blocks:

type NpfLayoutBlock = NpfLayoutAsk | NpfLayoutRows;

interface NpfLayoutRows {
  type: 'rows';
  display: Array<{
    blocks: number[];  // Array of content block indices
    mode?: { type: string };
  }>;
  truncate_after?: 1;
}

interface NpfLayoutAsk {
  type: 'ask';
  blocks: number[];
  attribution: any;
}

Post Parameters

NPF Post Parameters

Parameters for creating new NPF posts:

interface NpfPostParams {
  /** Array of NPF content blocks */
  content: NpfContentBlock[];
  /** Array of NPF layout objects */
  layout?: NpfLayoutBlock[];
  /** Initial state of the post */
  state?: PostState;
  /** Future publish date (ISO 8601 format) */
  publish_on?: string;
  /** Backdate the post (ISO 8601 format) */
  date?: string;
  /** Tags to associate with the post */
  tags?: string[];
  /** Source attribution URL */
  source_url?: string;
  /** Whether this should be a private answer */
  is_private?: boolean;
  /** Custom URL slug */
  slug?: string;
  /** Who can interact when reblogging */
  interactability_reblog?: 'everyone' | 'noone';
}

type PostState = 'published' | 'queue' | 'draft' | 'private' | 'unapproved';

NPF Reblog Parameters

Parameters for reblogging posts using NPF:

interface NpfReblogParams extends NpfPostParams {
  /** The unique public identifier of the Tumblelog being reblogged from */
  parent_tumblelog_uuid: string;
  /** The unique public post ID being reblogged */
  parent_post_id: string;
  /** The unique per-post hash validating this is a genuine reblog action */
  reblog_key: string;
  /** Whether to hide the reblog trail */
  hide_trail?: boolean;
  /** Array of reblog trail item indexes to exclude */
  exclude_trail_items?: boolean;
}

Media Upload Process

File Upload Example

const fs = require('fs');

// Upload multiple media files
await client.createPost('myblog', {
  content: [
    {
      type: 'text',
      text: 'My vacation photos!'
    },
    {
      type: 'image',
      media: fs.createReadStream('./beach.jpg'),
      alt_text: 'Beautiful beach scene'
    },
    {
      type: 'image', 
      media: fs.createReadStream('./sunset.jpg'),
      alt_text: 'Gorgeous sunset'
    },
    {
      type: 'video',
      media: fs.createReadStream('./waves.mp4')
    }
  ],
  layout: [
    {
      type: 'rows',
      display: [
        { blocks: [0] },      // Text
        { blocks: [1, 2] },   // Two images side by side
        { blocks: [3] }       // Video
      ]
    }
  ]
});

Media Processing

When media files are provided as ReadStreams:

  1. The library automatically detects media upload requirement
  2. Content is sent as multipart/form-data
  3. Media streams are mapped to identifiers
  4. JSON metadata is sent in a special 'json' field
  5. Media streams are sent as separate form fields

Error Handling

Common post management errors and handling:

try {
  await client.createPost('myblog', {
    content: [
      {
        type: 'text',
        text: 'My post content'
      }
    ]
  });
} catch (error) {
  if (error.message.includes('401')) {
    console.error('Authentication required - check your OAuth credentials');
  } else if (error.message.includes('403')) {
    console.error('Forbidden - check blog permissions');
  } else if (error.message.includes('413')) {
    console.error('Media file too large');
  } else {
    console.error('Post creation failed:', error.message);
  }
}