Themes
View .md
Endpoints for browsing and editing a blog’s custom themes, plug-in templates, built-in theme templates, and custom CSS. Send an app token in the Authorization header. Reading needs the read scope and changing anything needs write; the post scopes (create, update, delete, media) don’t grant access. Full-access app tokens work for both.
404.
site_id query parameter and use the user’s default blog when it’s left out. A site_id for a blog the user doesn’t own returns 404 with {"error": "Blog not found."}.
POST bodies are JSON, sent with Content-Type: application/json, up to 2 MiB. Other content types return 415, larger bodies return 413, malformed JSON returns 400, and so does a body with fields the endpoint doesn’t accept.
{"error": "Theme not found."}. A missing token returns 401 with the text Token required, and a token without the needed scope returns 403 with the text Token missing required scope.
OPTIONS preflight requests with 204. Responses are sent with Cache-Control: no-store. Writes signed in with a browser cookie instead of a token must come from a Micro.blog page.
https://micro.blog
/themes
Returns the blog, the user’s custom themes, and the plug-ins installed on that blog. Start here to find the theme_id for the template endpoints.
Example Request
curl -X GET "https://micro.blog/themes?site_id=5678" \
--header "Authorization: Bearer ABCDEF1234567890ABCD"
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
site_id
|
integer | Optional |
Blog to describe. Defaults to the user’s default blog. themes is the same for every blog; site and plugins change with it.
Example:
5678
|
Header Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization
|
string | Required |
Bearer token for authentication. Format: Bearer {token}.
|
Response
200 OK application/json
site.root_theme_id is the custom theme the blog uses, or null when it uses a built-in theme. site.built_in_theme names the built-in theme the blog is set to, such as marfa. themes lists every custom theme the user owns, sorted by title. plugins lists the plug-ins installed on this blog, including disabled ones. Theme site_id is null for custom themes and is the blog’s ID for plug-ins. Empty text fields are "".
Example Response
{
"site": {
"id": 5678,
"url": "https://example.com/",
"root_theme_id": 4321,
"built_in_theme": "marfa"
},
"themes": [
{
"id": 4321,
"site_id": null,
"title": "Custom Marfa",
"description": "",
"version": "",
"author_username": "",
"github_url": "",
"engine": "hugo",
"is_plugin": false,
"is_disabled": false,
"is_card": false,
"updated_at": "2026-10-20T09:30:00+00:00"
}
],
"plugins": [
{
"id": 4322,
"site_id": 5678,
"title": "Example Plug-in",
"description": "Adds a reading list page.",
"version": "1.2",
"author_username": "example",
"github_url": "https://github.com/example/example-plugin",
"engine": "hugo",
"is_plugin": true,
"is_disabled": false,
"is_card": false,
"updated_at": "2026-10-18T14:05:00+00:00"
}
]
}
/themes/defaults/templates
Lists the read-only templates a blog falls back to when its custom theme doesn’t override them. These come from the blog’s built-in theme and from Micro.blog’s blank theme, with the built-in theme winning when both have the same path. When the blog has an enabled plug-in that is a full theme, only the blank templates are listed.
Example Request
curl -X GET "https://micro.blog/themes/defaults/templates" \
--header "Authorization: Bearer ABCDEF1234567890ABCD"
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
site_id
|
integer | Optional |
Blog whose built-in theme to list. Defaults to the user’s default blog.
Example:
5678
|
Header Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization
|
string | Required |
Bearer token for authentication. Format: Bearer {token}.
|
Response
200 OK application/json
Returns templates sorted by path, without their contents. Built-in templates have no ID, so id, theme_id, and updated_at are null. read_only is always true and source names the built-in theme the file comes from. is_text is false for images, fonts, and other files that can’t be read as text. Copy a built-in template into a custom theme by reading it and creating a template with the same path.
Example Response
{
"templates": [
{
"id": null,
"theme_id": null,
"path": "config.json",
"is_text": true,
"read_only": true,
"updated_at": null,
"source": "marfa"
},
{
"id": null,
"theme_id": null,
"path": "layouts/index.html",
"is_text": true,
"read_only": true,
"updated_at": null,
"source": "marfa"
},
{
"id": null,
"theme_id": null,
"path": "layouts/robots.txt",
"is_text": true,
"read_only": true,
"updated_at": null,
"source": "blank"
}
]
}
/themes/defaults/template
Returns one built-in template with its contents.
Example Request
curl -G "https://micro.blog/themes/defaults/template" \
--header "Authorization: Bearer ABCDEF1234567890ABCD" \
--data-urlencode "path=layouts/index.html"
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
path
|
string | Required |
Template path exactly as listed by GET /themes/defaults/templates. Missing or empty returns 400; a path not in that list returns 404.
Example:
layouts/index.html
|
site_id
|
integer | Optional |
Blog whose built-in theme to read from. Defaults to the user’s default blog.
Example:
5678
|
Header Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization
|
string | Required |
Bearer token for authentication. Format: Bearer {token}.
|
Response
200 OK application/json
Returns the template with text. Returns 422 for a file that isn’t text, such as an image.
Example Response
{
"id": null,
"theme_id": null,
"path": "layouts/index.html",
"is_text": true,
"read_only": true,
"updated_at": null,
"source": "marfa",
"text": "{{ define \"main\" }}\n <div class=\"posts\">\n ...\n{{ end }}\n"
}
/themes/css
Returns the blog’s custom CSS, the same CSS as Edit CSS on the Design page.
Example Request
curl -X GET "https://micro.blog/themes/css?site_id=5678" \
--header "Authorization: Bearer ABCDEF1234567890ABCD"
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
site_id
|
integer | Optional |
Blog to read. Defaults to the user’s default blog.
Example:
5678
|
Header Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization
|
string | Required |
Bearer token for authentication. Format: Bearer {token}.
|
Response
200 OK application/json
Returns the blog ID and its CSS. css is "" when the blog has none.
Example Response
{
"site_id": 5678,
"css": "body {\n font-family: Georgia, serif;\n}\n"
}
/themes/css
Replaces the blog’s custom CSS and uploads it to the blog as /custom.css straight away, without rebuilding the blog.
Example Request
curl -X POST "https://micro.blog/themes/css?site_id=5678" \
--header "Authorization: Bearer ABCDEF1234567890ABCD" \
--header "Content-Type: application/json" \
--data '{"css": "body { font-family: Georgia, serif; }"}'
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
site_id
|
integer | Optional |
Blog to change. Defaults to the user’s default blog. Send it in the query string; the JSON body only accepts css.
Example:
5678
|
Header Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization
|
string | Required |
Bearer token for authentication. Format: Bearer {token}.
|
Content-Type
|
string | Required |
Must be application/json.
|
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
css
|
string | Required |
The complete CSS, replacing what’s there. Send "" to clear it. Anything other than a string returns 400.
Example:
body { font-family: Georgia, serif; }
|
Response
200 OK application/jsonReturns the blog ID and the saved CSS.
Example Response
{
"site_id": 5678,
"css": "body { font-family: Georgia, serif; }"
}
/themes/{theme_id}
Returns one of the user’s custom themes or plug-ins.
Example Request
curl -X GET "https://micro.blog/themes/4321" \
--header "Authorization: Bearer ABCDEF1234567890ABCD"
Header Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization
|
string | Required |
Bearer token for authentication. Format: Bearer {token}.
|
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
theme_id
|
integer | Required |
Theme or plug-in ID from GET /themes.
Example:
4321
|
Response
200 OK application/json
Returns the theme with the same fields as the entries in GET /themes.
Example Response
{
"id": 4321,
"site_id": null,
"title": "Custom Marfa",
"description": "",
"version": "",
"author_username": "",
"github_url": "",
"engine": "hugo",
"is_plugin": false,
"is_disabled": false,
"is_card": false,
"updated_at": "2026-10-20T09:30:00+00:00"
}
/themes/{theme_id}/templates
Lists the templates in a custom theme or plug-in, without their contents.
Example Request
curl -X GET "https://micro.blog/themes/4321/templates" \
--header "Authorization: Bearer ABCDEF1234567890ABCD"
Header Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization
|
string | Required |
Bearer token for authentication. Format: Bearer {token}.
|
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
theme_id
|
integer | Required |
Theme or plug-in ID from GET /themes.
Example:
4321
|
Response
200 OK application/json
Returns templates sorted by path. is_text is false for images, fonts, and other files that can’t be read or edited here. read_only is always false for theme templates.
Example Response
{
"templates": [
{
"id": 98765,
"theme_id": 4321,
"path": "layouts/index.html",
"is_text": true,
"read_only": false,
"updated_at": "2026-10-20T09:30:00+00:00"
},
{
"id": 98766,
"theme_id": 4321,
"path": "static/images/header.png",
"is_text": false,
"read_only": false,
"updated_at": "2026-10-12T16:45:00+00:00"
}
]
}
/themes/{theme_id}/templates/{id}
Returns one template with its contents.
Example Request
curl -X GET "https://micro.blog/themes/4321/templates/98765" \
--header "Authorization: Bearer ABCDEF1234567890ABCD"
Header Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization
|
string | Required |
Bearer token for authentication. Format: Bearer {token}.
|
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
theme_id
|
integer | Required |
Theme or plug-in ID.
Example:
4321
|
id
|
integer | Required |
Template ID from GET /themes/{theme_id}/templates. A template from a different theme returns 404.
Example:
98765
|
Response
200 OK application/json
Returns the template with text. Returns 422 when is_text is false.
Example Response
{
"id": 98765,
"theme_id": 4321,
"path": "layouts/index.html",
"is_text": true,
"read_only": false,
"updated_at": "2026-10-20T09:30:00+00:00",
"text": "{{ define \"main\" }}\n <h1>Hello</h1>\n{{ end }}\n"
}
/themes/{theme_id}/templates
Adds a text template to a custom theme or plug-in, then rebuilds every one of the user’s blogs that uses the theme.
.html, .txt, .md, .markdown, .css, .scss, .json, .coffee, .svg, .toml, .map, .yml, .xml, .js, .ts, or .webmanifest, and files named LICENSE or README. Other paths return 422. Hugo’s readFile and readDir are removed from the saved text, so the returned text can differ from what you sent.
Example Request
curl -X POST "https://micro.blog/themes/4321/templates" \
--header "Authorization: Bearer ABCDEF1234567890ABCD" \
--header "Content-Type: application/json" \
--data '{"path": "layouts/partials/head.html", "text": "<link rel=\"stylesheet\" href=\"/custom.css\">"}'
Header Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization
|
string | Required |
Bearer token for authentication. Format: Bearer {token}.
|
Content-Type
|
string | Required |
Must be application/json.
|
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
path
|
string | Required |
Relative path inside the theme, at most 250 characters, using letters, digits, spaces, ., -, _, and /. A leading /, empty segments, ., and .. return 400. A path that already exists in the theme returns 409.
Example:
layouts/partials/head.html
|
text
|
string | Required |
Template contents. May be "".
Example:
<link rel="stylesheet" href="/custom.css">
|
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
theme_id
|
integer | Required |
Theme or plug-in ID.
Example:
4321
|
Response
201 OK application/json
Returns the new template with text, and the IDs of the blogs being rebuilt in publishing_site_ids. The Location header holds the template’s path, such as /themes/4321/templates/98767.
Example Response
{
"template": {
"id": 98767,
"theme_id": 4321,
"path": "layouts/partials/head.html",
"is_text": true,
"read_only": false,
"updated_at": "2026-10-28T10:15:00+00:00",
"text": "<link rel=\"stylesheet\" href=\"/custom.css\">"
},
"publishing_site_ids": [5678]
}
/themes/{theme_id}/templates/{id}
Renames a template, replaces its contents, or both, then rebuilds every one of the user’s blogs that uses the theme. Fields you leave out keep their current values.
422. Renaming to a path another template in the theme already uses returns 409.
Example Request
curl -X POST "https://micro.blog/themes/4321/templates/98765" \
--header "Authorization: Bearer ABCDEF1234567890ABCD" \
--header "Content-Type: application/json" \
--data '{"text": "{{ define \"main\" }}<h1>Hello again</h1>{{ end }}"}'
Header Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization
|
string | Required |
Bearer token for authentication. Format: Bearer {token}.
|
Content-Type
|
string | Required |
Must be application/json.
|
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
path
|
string | Optional |
New relative path for the template. Send path, text, or both.
Example:
layouts/index.html
|
text
|
string | Optional |
New contents, replacing the old ones. May be "".
Example:
{{ define "main" }}<h1>Hello again</h1>{{ end }}
|
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
theme_id
|
integer | Required |
Theme or plug-in ID.
Example:
4321
|
id
|
integer | Required |
Template ID.
Example:
98765
|
Response
200 OK application/json
Returns the updated template with text, and the IDs of the blogs being rebuilt in publishing_site_ids.
Example Response
{
"template": {
"id": 98765,
"theme_id": 4321,
"path": "layouts/index.html",
"is_text": true,
"read_only": false,
"updated_at": "2026-10-28T10:20:00+00:00",
"text": "{{ define \"main\" }}<h1>Hello again</h1>{{ end }}"
},
"publishing_site_ids": [5678]
}
/themes/{theme_id}/templates/{id}
Deletes a template from a custom theme or plug-in, then rebuilds every one of the user’s blogs that uses the theme. Binary files such as images can be deleted too.
Example Request
curl -X DELETE "https://micro.blog/themes/4321/templates/98766" \
--header "Authorization: Bearer ABCDEF1234567890ABCD"
Header Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization
|
string | Required |
Bearer token for authentication. Format: Bearer {token}.
|
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
theme_id
|
integer | Required |
Theme or plug-in ID.
Example:
4321
|
id
|
integer | Required |
Template ID.
Example:
98766
|
Response
200 OK application/json
Confirms the deletion and returns the IDs of the blogs being rebuilt in publishing_site_ids.
Example Response
{
"deleted": true,
"publishing_site_ids": [5678]
}