Get the bundle

Embed
Preview of the card
<a href="https://forumulate.com/reference/forumulate-api-publicthemeregistry-bundle"><img src="https://forumulate.com/reference/_api/public/orgs/046a9bc0-d530-43ae-9bae-af7945008cda/spaces/8042f004-e3d9-445f-b7a4-b6c1f86836e6/pages/96686142-b30a-46ce-8ab8-5f06b3b05e54/social-card.svg?v=3e7ae33eec8b" alt="Get the bundle" width="600" height="315"></a>
GEThttps://forumulate.com/api/registry/themes/{slug}/bundle

The bundle file of a free listing, for hand-importing or version control. A paid listing answers 402: it is installed after purchase, never downloaded.

Parameters
slugstringpathrequired
forumulate-versionstringheader
API version negotiation. Send forumulate-version: 2026-12-01 on every request from a new integration; it is optional only so that integrations written before versioning keep working. Each version includes everything before it:
Code samples
cURL
curl -X GET "https://forumulate.com/api/registry/themes/{slug}/bundle" \
  -H "forumulate-version: <value>"
TypeScript
const res = await fetch('https://forumulate.com/api/registry/themes/{slug}/bundle', {
  method: 'GET',
  headers: {
    'forumulate-version': '<value>',
  },
});
const data = await res.json();
Python
import requests

res = requests.request(
    "GET",
    "https://forumulate.com/api/registry/themes/{slug}/bundle",
    headers={
    "forumulate-version": "<value>",
    },
)
data = res.json()
Go
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://forumulate.com/api/registry/themes/{slug}/bundle", nil)
	req.Header.Set("forumulate-version", "<value>")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)
	fmt.Println(string(body))
}
PHP
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://forumulate.com/api/registry/themes/{slug}/bundle');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['forumulate-version: <value>']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Ruby
require 'net/http'
require 'uri'

uri = URI('https://forumulate.com/api/registry/themes/{slug}/bundle')
req = Net::HTTP::Get.new(uri)
req['forumulate-version'] = '<value>'
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
  http.request(req)
end
puts res.body
Java
import java.net.URI;
import java.net.http.*;
import java.net.http.HttpRequest.BodyPublishers;

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://forumulate.com/api/registry/themes/{slug}/bundle"))
    .header("forumulate-version", "<value>")
    .GET()
    .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
Responses
200 Success.
Example
{
  "components": [
    {
      "css": "string",
      "format": "forumulate.theme-component",
      "formatVersion": 1,
      "meta": {
        "author": {
          "name": null,
          "url": null
        },
        "description": "string",
        "homepage": "string",
        "license": "string",
        "name": "string",
        "previewImages": [
          null
        ],
        "slug": "string",
        "spaceTypes": [
          null
        ],
        "tags": [
          null
        ],
        "version": "string"
      },
      "outlets": {
        "above-main": {},
        "below-main": {},
        "blog-index-above-list": {},
        "blog-post-after": {},
        "blog-post-before": {},
        "blog-post-end": {},
        "careers-above-list": {},
        "category-header": {},
        "changelog-above-list": {},
        "courses-above-list": {},
        "courses-certificate-footer": {},
        "courses-landing-hero-after": {},
        "courses-lesson-above-body": {},
        "courses-lesson-after-body": {},
        "docs-page-footer": {},
        "docs-page-header": {},
        "docs-sidebar-end": {},
        "docs-sidebar-start": {},
        "events-above-list": {},
        "events-card-footer": {},
        "events-page-above-body": {},
        "events-page-after-body": {},
        "events-page-aside": {},
        "faq-above-list": {},
        "footer-after": {},
        "footer-before": {},
        "forum-categories-above": {},
        "forum-index-above-list": {},
        "forum-index-below-list": {},
        "forum-post-after-body": {},
        "forum-post-author-after": {},
        "forum-sidebar-bottom": {},
        "forum-sidebar-top": {},
        "forum-topic-above-title": {},
        "forum-topic-below-first-post": {},
        "forum-topic-below-title": {},
        "forum-topic-bottom": {},
        "forum-topic-row-after-title": {},
        "front-page-bottom": {},
        "front-page-top": {},
        "header-after": {},
        "header-before": {},
        "roadmap-above-board": {},
        "status-above-board": {},
        "user-card": {}
      },
      "settings": {},
      "strings": {}
    }
  ],
  "format": "forumulate.theme-bundle",
  "formatVersion": 2,
  "frontPages": [
    {
      "aside": {
        "doc": {
          "content": null,
          "type": null
        },
        "phone": "after",
        "width": "default"
      },
      "customization": {
        "customCss": "string",
        "jsonLd": "string",
        "ogDescription": "string",
        "ogTitle": "string",
        "shareImageUrl": "string",
        "theme": {
          "accentColor": null,
          "bodyFontFamily": null,
          "changelog": null,
          "colorScheme": null,
          "components": null,
          "contentWidth": null,
          "customFontFamily": null,
          "font": null,
          "headingFontFamily": null,
          "layout": null,
          "monoFontFamily": null,
          "motion": null,
          "palette": null,
          "paletteDark": null,
          "post": null,
          "primaryColor": null,
          "typography": null
        },
        "twitterCard": "summary"
      },
      "doc": {
        "content": [
          null
        ],
        "type": "doc"
      },
      "gap": "lg",
      "hideTitle": true,
      "paddingBottom": "string",
      "paddingTop": "string",
      "preset": "string",
      "sidebar": {
        "divider": {
          "color": null,
          "style": null,
          "width": null
        },
        "doc": {
          "content": null,
          "type": null
        },
        "gap": null,
        "phone": null,
        "position": "left",
        "source": "doc",
        "startAfter": 0,
        "width": "default"
      },
      "target": "BLOG",
      "width": 0
    }
  ],
  "meta": {
    "author": {
      "name": "string",
      "url": "string"
    },
    "description": "string",
    "homepage": "string",
    "license": "string",
    "name": "string",
    "previewImages": [
      {
        "alt": "string",
        "scheme": "dark",
        "url": "string"
      }
    ],
    "slug": "string",
    "spaceTypes": [
      "BLOG"
    ],
    "tags": [
      "string"
    ],
    "version": "string"
  },
  "requiredOrigins": {
    "fonts": [
      "string"
    ],
    "images": [
      "string"
    ]
  },
  "theme": {}
}
Schema
componentsobject[]
Show child parameters
cssstring
formatstringrequired
forumulate.theme-component
formatVersionnumberrequired
1
metaobjectrequired
Show child parameters
authorobjectrequired
descriptionstring
homepagestring
licensestring
namestringrequired
previewImagesobject[]
slugstringrequired
spaceTypesstring[]
tagsstring[]
versionstringrequired
outletsobject
Show child parameters
above-main
below-main
blog-index-above-list
blog-post-after
blog-post-before
blog-post-end
careers-above-list
category-header
changelog-above-list
courses-above-list
courses-certificate-footer
courses-landing-hero-after
courses-lesson-above-body
courses-lesson-after-body
docs-page-footer
docs-page-header
docs-sidebar-end
docs-sidebar-start
events-above-list
events-card-footer
events-page-above-body
events-page-after-body
events-page-aside
faq-above-list
footer-after
footer-before
forum-categories-above
forum-index-above-list
forum-index-below-list
forum-post-after-body
forum-post-author-after
forum-sidebar-bottom
forum-sidebar-top
forum-topic-above-title
forum-topic-below-first-post
forum-topic-below-title
forum-topic-bottom
forum-topic-row-after-title
front-page-bottom
front-page-top
header-after
header-before
roadmap-above-board
status-above-board
user-card
settingsobject
stringsobject
formatstringrequired
forumulate.theme-bundle
formatVersionnumberrequired
frontPagesobject[]
Show child parameters
asideobject
Show child parameters
docobjectrequired
phonestring
after | hide
widthstring
default | narrow | wide
customizationobject
Show child parameters
customCssstring
jsonLdstring
ogDescriptionstring
ogTitlestring
shareImageUrlstring
themeobject
twitterCardstring
summary | summary_large_image
docobjectrequired
Show child parameters
contentobject[]required
typestringrequired
doc
gapstring
lg | md | none | sm
hideTitleboolean
paddingBottomstring | number
paddingTopstring | number | object
presetstring
sidebarobject
Show child parameters
dividerobject
docobjectrequired
gapstring | number
phonestring | object
positionstringrequired
left | none | right
sourcestring
doc | docs-nav
startAfternumber
widthstring
default | narrow | wide
targetstringrequired
BLOG | CAREERS | CHANGELOG | CONTACT | CONTENT | COURSES | CUSTOM | DOCS | EVENTS | FAQ | FORUM | LEGAL | ROADMAP | SITE | STATUS
widthnumber | string
metaobjectrequired
Show child parameters
authorobjectrequired
Show child parameters
namestringrequired
urlstring
descriptionstring
homepagestring
licensestring
namestringrequired
previewImagesobject[]
Show child parameters
altstring
schemestring
dark | light
urlstringrequired
slugstringrequired
spaceTypesstring[]
tagsstring[]
versionstringrequired
requiredOriginsobjectrequired
Show child parameters
fontsstring[]
imagesstring[]
themeobjectrequired
A structurally large type (a deeply nested or recursive union). Its full shape is served by the endpoint that produces it; GET /docs/block-schema covers the document block model.
400 The request could not be understood — a malformed header, path or query value (request.malformed). A value that was understood but is not acceptable is a 422 validation.failed.
Example
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {},
    "docs": "string",
    "reason": "string",
    "slugs": [
      "string"
    ],
    "conflict": {
      "etag": "string",
      "ops": [
        null
      ]
    }
  },
  "requestId": "string"
}
Schema
errorobjectrequired
Show child parameters
codestringrequired
Stable, machine-readable error code, e.g. rbac.forbidden.
messagestringrequired
Human-readable explanation.
detailsobject
Field-level validation problems, keyed by field path (422 only).
docsstring
Where the vocabulary or format this error is about is published — an absolute URL, or an API route such as GET /space-types/{type}/presets. Present on errors over a controlled list, so the answer to "what am I allowed to send?" is in the error itself.
reasonstring
Why a credential was refused, as one stable word, where one status has several causes — e.g. an external_jwt redeem answers iat_too_old, iat_in_future, bad_signature, bad_issuer, bad_audience, expired and more. Branch on this, not on message.
slugsstring[]
The exact slugs that collided, on a slug-conflict 409 — space.slug_taken, page.slug_taken, collection.slug_taken, site.slug_taken or badge.slug_taken.
conflictobject
On a 409 from a content entry field write (PATCH …/entries/{entryId}/fields) whose If-Match was stale.
Show child parameters
etagstringrequired
The entry's current fields etag.
opsarrayrequired
The field operations since your If-Match that touch a path your own operations touch. Empty: none did — retry the batch unchanged under etag. null: the history no longer reaches your tag — re-read the entry first.
requestIdstring
The id of this request — the same value as the x-request-id and x-forumulate-request-id response headers. Quote it in a bug report: it is the only thing that finds your exact request in our logs. It is in the body because that is the part that survives — an SDK that throws the parsed body, a serverless log that keeps the body and drops the headers, a ticket with the JSON pasted in.
404 No such thing — one of the ids in the path does not resolve, or resolves to something this caller may not see (*.not_found).
Example
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {},
    "docs": "string",
    "reason": "string",
    "slugs": [
      "string"
    ],
    "conflict": {
      "etag": "string",
      "ops": [
        null
      ]
    }
  },
  "requestId": "string"
}
Schema
errorobjectrequired
Show child parameters
codestringrequired
Stable, machine-readable error code, e.g. rbac.forbidden.
messagestringrequired
Human-readable explanation.
detailsobject
Field-level validation problems, keyed by field path (422 only).
docsstring
Where the vocabulary or format this error is about is published — an absolute URL, or an API route such as GET /space-types/{type}/presets. Present on errors over a controlled list, so the answer to "what am I allowed to send?" is in the error itself.
reasonstring
Why a credential was refused, as one stable word, where one status has several causes — e.g. an external_jwt redeem answers iat_too_old, iat_in_future, bad_signature, bad_issuer, bad_audience, expired and more. Branch on this, not on message.
slugsstring[]
The exact slugs that collided, on a slug-conflict 409 — space.slug_taken, page.slug_taken, collection.slug_taken, site.slug_taken or badge.slug_taken.
conflictobject
On a 409 from a content entry field write (PATCH …/entries/{entryId}/fields) whose If-Match was stale.
Show child parameters
etagstringrequired
The entry's current fields etag.
opsarrayrequired
The field operations since your If-Match that touch a path your own operations touch. Empty: none did — retry the batch unchanged under etag. null: the history no longer reaches your tag — re-read the entry first.
requestIdstring
The id of this request — the same value as the x-request-id and x-forumulate-request-id response headers. Quote it in a bug report: it is the only thing that finds your exact request in our logs. It is in the body because that is the part that survives — an SDK that throws the parsed body, a serverless log that keeps the body and drops the headers, a ticket with the JSON pasted in.
429 Rate limited — see the RateLimit-* headers.
Example
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {},
    "docs": "string",
    "reason": "string",
    "slugs": [
      "string"
    ],
    "conflict": {
      "etag": "string",
      "ops": [
        null
      ]
    }
  },
  "requestId": "string"
}
Schema
errorobjectrequired
Show child parameters
codestringrequired
Stable, machine-readable error code, e.g. rbac.forbidden.
messagestringrequired
Human-readable explanation.
detailsobject
Field-level validation problems, keyed by field path (422 only).
docsstring
Where the vocabulary or format this error is about is published — an absolute URL, or an API route such as GET /space-types/{type}/presets. Present on errors over a controlled list, so the answer to "what am I allowed to send?" is in the error itself.
reasonstring
Why a credential was refused, as one stable word, where one status has several causes — e.g. an external_jwt redeem answers iat_too_old, iat_in_future, bad_signature, bad_issuer, bad_audience, expired and more. Branch on this, not on message.
slugsstring[]
The exact slugs that collided, on a slug-conflict 409 — space.slug_taken, page.slug_taken, collection.slug_taken, site.slug_taken or badge.slug_taken.
conflictobject
On a 409 from a content entry field write (PATCH …/entries/{entryId}/fields) whose If-Match was stale.
Show child parameters
etagstringrequired
The entry's current fields etag.
opsarrayrequired
The field operations since your If-Match that touch a path your own operations touch. Empty: none did — retry the batch unchanged under etag. null: the history no longer reaches your tag — re-read the entry first.
requestIdstring
The id of this request — the same value as the x-request-id and x-forumulate-request-id response headers. Quote it in a bug report: it is the only thing that finds your exact request in our logs. It is in the body because that is the part that survives — an SDK that throws the parsed body, a serverless log that keeps the body and drops the headers, a ticket with the JSON pasted in.
default Error — the stable { error: { code, message } } envelope.
Example
{
  "error": {
    "code": "string",
    "message": "string",
    "details": {},
    "docs": "string",
    "reason": "string",
    "slugs": [
      "string"
    ],
    "conflict": {
      "etag": "string",
      "ops": [
        null
      ]
    }
  },
  "requestId": "string"
}
Schema
errorobjectrequired
Show child parameters
codestringrequired
Stable, machine-readable error code, e.g. rbac.forbidden.
messagestringrequired
Human-readable explanation.
detailsobject
Field-level validation problems, keyed by field path (422 only).
docsstring
Where the vocabulary or format this error is about is published — an absolute URL, or an API route such as GET /space-types/{type}/presets. Present on errors over a controlled list, so the answer to "what am I allowed to send?" is in the error itself.
reasonstring
Why a credential was refused, as one stable word, where one status has several causes — e.g. an external_jwt redeem answers iat_too_old, iat_in_future, bad_signature, bad_issuer, bad_audience, expired and more. Branch on this, not on message.
slugsstring[]
The exact slugs that collided, on a slug-conflict 409 — space.slug_taken, page.slug_taken, collection.slug_taken, site.slug_taken or badge.slug_taken.
conflictobject
On a 409 from a content entry field write (PATCH …/entries/{entryId}/fields) whose If-Match was stale.
Show child parameters
etagstringrequired
The entry's current fields etag.
opsarrayrequired
The field operations since your If-Match that touch a path your own operations touch. Empty: none did — retry the batch unchanged under etag. null: the history no longer reaches your tag — re-read the entry first.
requestIdstring
The id of this request — the same value as the x-request-id and x-forumulate-request-id response headers. Quote it in a bug report: it is the only thing that finds your exact request in our logs. It is in the body because that is the part that survives — an SDK that throws the parsed body, a serverless log that keeps the body and drops the headers, a ticket with the JSON pasted in.
The Forumulate teamUpdated