Upload a theme
const url = 'http://localhost:8000/clusters/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/themes';
const form = new FormData();
form.append('name', 'example');
form.append('description', 'example');
form.append('version', 'example');
form.append('theme_file', 'file');
form.append('theme_types', 'example');
const options = {
method: 'POST',
headers: {'API-Version': '2026-06-01.beta', 'API-Key': '<API-Key>'}
};
options.body = form;
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}curl --request POST \
--url http://localhost:8000/clusters/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/themes \
--header 'API-Key: <API-Key>' \
--header 'API-Version: 2026-06-01.beta' \
--header 'Content-Type: multipart/form-data' \
--form name=example \
--form description=example \
--form version=example \
--form theme_file=@file \
--form theme_types=exampleUploads a Keycloak-compatible theme archive (ZIP or JAR) and makes it available in the cluster’s Keycloak theme registry.
JAR archives produced by Keycloakify and other Keycloak-native theme packaging tools are accepted.
Returns with status: deploying; poll GET to confirm completion.
To activate the theme, assign it to a realm or application client with PUT /themes on that resource once deployed.
theme_types restricts which types from the package are deployed — omit to deploy all detected types.
Maximum body size is 50 MB.
Required scope: themes:write
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Header Parameters
Section titled “Header Parameters”Request Bodyrequired
Section titled “Request Bodyrequired”object
Version label for the theme, e.g. v2.4 or 2026-08-12. Free text, up to 50 characters; it is shown in the library and never interpreted.
Keycloak-compatible theme archive. Content type must be application/zip (ZIP) or application/java-archive (Keycloakify JAR).
Comma-separated subset of theme types to deploy from the package (e.g. login,account).
Omit to deploy all detected types. Sent as a single plain-text multipart field, not JSON -
oapi-codegen would otherwise infer application/json encoding for an array-typed part,
which does not match how this field is actually written and read.
Responses
Section titled “Responses”The request has succeeded and a new resource has been created as a result.
A custom Keycloak theme package deployed to a cluster.
object
Keycloak theme types detected in the uploaded ZIP package.
Size of the uploaded theme archive in bytes.
Timestamp of the most recent successful deployment to Keycloak.
Error detail from the last failed deployment.
Directory name Keycloak serves this theme from. Use this exact value when setting a realm or application theme outside Skycloak. Valid once status is deployed. It changes when the theme content is replaced: realm theme settings that name the previous directory are moved automatically, but application themes set outside Skycloak are not and must be updated after a replace.
True when the theme’s content was replaced under its exact name and Keycloak has not restarted since, so the previous content may still be served. Only workspaces that keep exact theme names replace content in place; for every other theme this is false.
Example
{
"theme_types": [
"login"
],
"status": "deploying"
}The server could not understand the request due to invalid syntax.
RFC 9457 Problem Details object.
object
Example generated
{
"type": "https://example.com",
"title": "example",
"status": 1,
"detail": "example",
"instance": "https://example.com"
}Access is unauthorized.
RFC 9457 Problem Details object.
object
Example generated
{
"type": "https://example.com",
"title": "example",
"status": 1,
"detail": "example",
"instance": "https://example.com"
}Client error
Returned when a workspace plan quota would be exceeded. required_plan indicates the minimum plan needed to complete the request.
object
Machine-readable identifier of the quota that was hit, so a client can tell
quotas apart without parsing detail. Known values include cluster_limit
(the workspace’s total cluster allowance), cluster_size_quota (the
allowance for the requested cluster size) and cluster_size (the requested
size is not offered on the plan at all). New values may be added.
How many of the resource the workspace currently has.
Maximum allowed by the current plan. 0 means the plan does not include the resource at all.
Example generated
{
"type": "https://example.com",
"title": "example",
"status": 1,
"detail": "example",
"instance": "https://example.com",
"current_plan": "example",
"required_plan": "example",
"code": "example",
"current_usage": 1,
"current_limit": 1
}Access is forbidden.
RFC 9457 Problem Details object.
object
Example generated
{
"type": "https://example.com",
"title": "example",
"status": 1,
"detail": "example",
"instance": "https://example.com"
}The server cannot find the requested resource.
RFC 9457 Problem Details object.
object
Example generated
{
"type": "https://example.com",
"title": "example",
"status": 1,
"detail": "example",
"instance": "https://example.com"
}Client error
object
object
Example generated
{
"type": "https://example.com",
"title": "example",
"status": 1,
"detail": "example",
"instance": "https://example.com",
"errors": [
{
"field": "example",
"detail": "example",
"code": "example",
"value": "example"
}
]
}Client error
RFC 9457 Problem Details object.
object
Example generated
{
"type": "https://example.com",
"title": "example",
"status": 1,
"detail": "example",
"instance": "https://example.com"
}Headers
Section titled “Headers”Server error
RFC 9457 Problem Details object.
object
Example generated
{
"type": "https://example.com",
"title": "example",
"status": 1,
"detail": "example",
"instance": "https://example.com"
}Server error
RFC 9457 Problem Details object.
object
Example generated
{
"type": "https://example.com",
"title": "example",
"status": 1,
"detail": "example",
"instance": "https://example.com"
}