Guides

Publishing Posts

This guide covers everything about publishing posts in Blogree — from clicking Publish Now to understanding the full delivery pipeline, multi-site publishing, and handling failures.

The Publishing Pipeline

When you publish a post in Blogree (via dashboard or API), here's exactly what happens:

1
Post finalized
Blogree locks the post content, generates the final HTML/Markdown/JSON output, and stamps the published_at timestamp.
2
Delivery dispatched
For each selected site, a delivery job is queued. The dispatcher sends a signed HMAC-SHA256 webhook to each site's endpoint simultaneously.
3
Confirmation received
Your site's webhook handler processes the post, returns 200 OK, and optionally calls POST /api/pull/confirm to acknowledge delivery.
4
Status updated
Blogree marks each site's delivery as 'delivered'. If any site returns non-200, retry logic kicks in (30s → 5min → 30min).
5
Analytics recorded
Delivery metrics are recorded in the analytics dashboard: delivery time, response code, attempt count, and final status.

Publishing from the Dashboard

Instant Publish (Publish Now)

  1. Open or create a post
  2. In the right sidebar, select the site(s) to publish to using the site checkboxes
  3. Verify the SEO title and meta description in the metadata fields
  4. Click Publish Now
  5. A progress indicator shows real-time delivery status per site

Multi-Site Publish

To publish to multiple sites simultaneously, check multiple sites in the site selector before clicking Publish Now. Blogree dispatches deliveries to all selected sites in parallel — not sequentially. Your analytics will show individual delivery status per site.

You can publish one post to all connected sites at once by clicking Select All in the site selector. Great for syndicating content across multiple properties.

Publishing via API

// Publish immediately to all connected sites POST /api/posts/post_xyz789/publish Authorization: Bearer <your_jwt_token> {} // empty body = publish to all sites // Publish to specific sites only { "site_ids": ["site_abc123", "site_def456"] }
// Create and publish in one request POST /api/posts Authorization: Bearer <your_jwt_token> { "title": "My New Post", "body": { "html": "<p>Content...</p>", "markdown": "Content..." }, "meta": { "title": "SEO Title", "description": "Meta desc..." }, "tags": ["AI", "blogging"], "status": "published", "site_ids": ["site_abc123"] }

Delivery Confirmation

After receiving a webhook, your site should call the confirmation endpoint to explicitly log delivery success or failure. This is optional but gives you more accurate analytics.

// In your webhook handler, after successfully processing the post: await fetch('https://api.blogree.com/api/pull/confirm', { method: 'POST', headers: { 'X-API-Key': process.env.BLOGREE_API_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ post_id: payload.post.id, site_id: payload.site.id, status: 'delivered', // or 'failed' if you couldn't process it metadata: { wordpress_post_id: 1234, // optional: any extra data }, }), });

Re-publishing Updated Posts

If you update a post's content after publishing, use the re-publish flow to push the updated version to all sites. The post's version number increments with each update.

// Update post content PATCH /api/posts/post_xyz789 { "title": "Updated Title", "body": { "html": "..." } } // Then re-publish to push the update POST /api/posts/post_xyz789/publish { "site_ids": ["site_abc123"] } // Your webhook handler receives the full updated payload // The payload includes version: 2 (or higher) so you can handle updates
⚠️
Build your webhook handler to handle updates by slug — use upsert (update-or-insert) operations so re-publishing an existing post updates it rather than creating a duplicate.

Unpublishing Posts

Blogree does not automatically remove posts from your connected sites when you delete or unpublish them in the Blogree dashboard. You must remove them from your site manually, or build a delete handler for the post.deleted webhook event.

// post.deleted event payload { "event": "post.deleted", "post": { "id": "post_xyz789", "slug": "my-blog-post" }, "site": { "id": "site_abc123" } } // In your webhook handler: if (payload.event === 'post.deleted') { await db.posts.delete({ where: { slug: payload.post.slug } }); await revalidatePath('/blog'); }