Build with Micro.blog logo
Build with Micro.blog
API Documentation

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.

Note: Only the owner of a blog, theme, or template can use it here. Being a member of a team blog doesn’t grant access. Unknown IDs, IDs that aren’t numbers, and IDs that belong to someone else all return 404.
Note: The discovery, built-in template, and CSS endpoints take an optional 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."}.
Note: 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.
Note: Errors from these endpoints are JSON objects such as {"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.
Note: Every endpoint allows cross-origin requests and answers 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.
BASE URL https://micro.blog
GET /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"
    }
  ]
}
GET /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"
    }
  ]
}
GET /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"
}
GET /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"
}
POST /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/json

Returns the blog ID and the saved CSS.

Example Response

{
  "site_id": 5678,
  "css": "body { font-family: Georgia, serif; }"
}
GET /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"
}
GET /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"
    }
  ]
}
GET /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"
}
POST /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.

Note: Only text files can be created: paths ending in .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]
}
POST /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.

Note: The same path and text rules apply as when creating a template. Templates that aren’t text, and renames to a path that isn’t text, return 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]
}
DELETE /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]
}