Get a released blog
GET /api/consumer/blogs/{blogId} Returns one released blog with its full content payload — markdown, frontmatter, structured outline, and uploaded media assets — ready for rendering on your site.
Path parameters
| Name | Type | Description |
|---|---|---|
blogId | string | The blog identifier returned as id from GET /api/consumer/blogs. |
Headers
Same as the list endpoint: pass the workspace API
key via x-api-key or Authorization: Bearer.
Response
200 OK with the released blog detail. 404 Not Found if the ID does not match a released blog in your workspace —
either the ID is wrong, the blog was never released, or it belongs to a
different workspace.
Response shape
type GetReleasedBlogResponse = {
id: string; // same as the blogId you requested
releaseNotes: string | null;
releasedAt: string; // ISO 8601 timestamp
version: {
id: string;
versionNumber: number;
renderedTitle: string;
renderedSlug: string;
metaTitle: string | null;
metaDescription: string | null;
markdown: string; // full article body
frontmatter: Record<string, string> | null;
structuredContent: unknown | null; // outline JSON, when available
reviewSummary: string | null;
updatedAt: string;
assets: Array<{
id: string;
assetRole: "cover_image" | "inline_image" | "diagram" | "illustration";
publicUrl: string | null;
signedUrl: string | null;
altText: string | null;
caption: string | null;
width: number | null;
height: number | null;
metadata: unknown;
}>;
};
}; Field notes
markdownis the canonical body. It already contains inline image references using the asset URLs below — render it with any standard CommonMark / GFM renderer.frontmattermirrors the YAML-style metadata the editor signed off on (e.g.tags,category). Keys and values are both strings.structuredContentis a structured outline of the article (typed asunknownso the schema can evolve). Use it if you want to render the outline as a TOC; ignore it if you only need the markdown.assetsare uploaded media. PreferpublicUrl; fall back tosignedUrlfor buckets fronted by short-lived URLs. Filter byassetRoleto find the cover image (cover_imageis always at most one entry per release).
Markdown features used
The markdown field uses GitHub-Flavored Markdown (GFM) in addition
to CommonMark. Pick a renderer that supports GFM (e.g. react-markdown with remark-gfm, markdown-it with the gfm preset, or any renderer
GitHub itself uses). You may encounter:
- Tables for comparisons and criteria.
- Task lists (
- [ ],- [x]) for checklists. - Strikethrough (
~~text~~). - Footnotes (
[^1]) for inline citations. - Fenced code blocks with language tags (
```ts,```bash, etc.). Pair your renderer with a syntax highlighter (Prism, Shiki, Highlight.js) if you want highlighted code. - Callout-style blockquotes that lead with a bold label
(
> **Note:** …,> **Tip:** …,> **Warning:** …,> **Example:** …). These render as plain styled blockquotes on any CommonMark renderer; if you want stronger per-type styling (icon, background colour) you can detect the leading<strong>text in the blockquote and theme accordingly.
A vanilla CommonMark renderer will degrade gracefully — tables fall back to plain text rows, task lists become regular bullets, footnotes appear inline. For the cleanest reading experience, use GFM.
Example
curl -sS https://api.contentpilot.uixlabs.co/api/consumer/blogs/ck1234567890
-H "x-api-key: $CONTENT_PILOT_API_KEY" {
"id": "ck1234567890",
"releaseNotes": "First public release after editor sign-off.",
"releasedAt": "2026-05-08T09:14:00.000Z",
"version": {
"id": "cv0987654321",
"versionNumber": 1,
"renderedTitle": "How ATS optimisation actually works in 2026",
"renderedSlug": "ats-optimisation-2026",
"metaTitle": "ATS optimisation in 2026",
"metaDescription": "What today's ATS systems really score, and the practical levers that move the needle.",
"markdown": "# How ATS optimisation actually works in 2026\n\nMost candidates...",
"frontmatter": {
"category": "career",
"readingTime": "8 min"
},
"structuredContent": null,
"reviewSummary": "Quality recommendation: ship.",
"updatedAt": "2026-05-08T09:13:42.000Z",
"assets": [
{
"id": "ca555",
"assetRole": "cover_image",
"publicUrl": "https://content-pilot.s3.ap-south-1.amazonaws.com/ck1234567890/cover.png",
"signedUrl": null,
"altText": "Recruiter screen showing ATS scoring",
"caption": null,
"width": 1600,
"height": 900,
"metadata": null
}
]
}
} TypeScript helper
async function fetchReleasedBlog(blogId: string) {
const response = await fetch(
`https://api.contentpilot.uixlabs.co/api/consumer/blogs/${encodeURIComponent(blogId)}`,
{ headers: { "x-api-key": process.env.CONTENT_PILOT_API_KEY! } },
);
if (response.status === 404) return null;
if (!response.ok) throw new Error(`Failed: ${response.status}`);
return (await response.json()) as GetReleasedBlogResponse;
} Updates and republishing
When an editor releases a new version of the same blog, the response under
the same blogId updates in place — version.versionNumber increments and releasedAt advances. Treat the response as the current released version;
use version.versionNumber and version.updatedAt to invalidate any cached
copy on your side.
Errors
See Errors. On this endpoint specifically:
401 Unauthorized— missing or invalid API key.404 Not Found— the ID is not a released blog in your workspace.500 Internal Server Error— retry with backoff.