Stand with Ukraine 🇺🇦

Integrate Open API (Swagger) — Confluence Cloud MacroNew

external-content

This Confluence user macro dynamically loads and renders an OpenAPI (Swagger) specification using Swagger UI.

The macro retrieves the specification URL from macro parameters, validates its availability and JSON structure, and initializes SwaggerUIBundle with deep linking and persistent authorization enabled. It includes loading states with AUI spinner feedback, graceful error handling for missing or invalid specifications, and custom dark-mode styling aligned with Atlassian design tokens to ensure seamless integration with Confluence themes.

Try for free

User Parameters

URL

Enter a URL to fetch data from

Template

<script>
  window.AP.theming.initializeTheming();
</script>

#set ($!swaggerUrl = $parameters["url"])

<link
  rel="stylesheet"
  href="https://unpkg.com/swagger-ui-dist/swagger-ui.css"
/>

<style>
  html[data-color-mode="dark"] .swagger-ui .scheme-container,
  html[data-color-mode="dark"] #servers {
    background: #1c1c21;
  }

  html[data-color-mode="dark"] .swagger-ui .checkbox p,
  html[data-color-mode="dark"] .swagger-ui .dialog-ux .modal-ux-content h4,
  html[data-color-mode="dark"] .swagger-ui .dialog-ux .modal-ux-content p,
  html[data-color-mode="dark"] .swagger-ui .dialog-ux .modal-ux-header h3,
  html[data-color-mode="dark"] .swagger-ui .errors-wrapper .errors h4,
  html[data-color-mode="dark"] .swagger-ui .errors-wrapper hgroup h4,
  html[data-color-mode="dark"] .swagger-ui .info .base-url,
  html[data-color-mode="dark"] .swagger-ui .info .title,
  html[data-color-mode="dark"] .swagger-ui .info h1,
  html[data-color-mode="dark"] .swagger-ui .info h2,
  html[data-color-mode="dark"] .swagger-ui .info h3,
  html[data-color-mode="dark"] .swagger-ui .info h4,
  html[data-color-mode="dark"] .swagger-ui .info h5,
  html[data-color-mode="dark"] .swagger-ui .info li,
  html[data-color-mode="dark"] .swagger-ui .info p,
  html[data-color-mode="dark"] .swagger-ui .info table,
  html[data-color-mode="dark"] .swagger-ui .loading-container .loading::after,
  html[data-color-mode="dark"] .swagger-ui .model,
  html[data-color-mode="dark"] .swagger-ui .opblock .opblock-section-header h4,
  html[data-color-mode="dark"] .swagger-ui .opblock .opblock-section-header > label,
  html[data-color-mode="dark"] .swagger-ui .opblock .opblock-summary-description,
  html[data-color-mode="dark"] .swagger-ui .opblock .opblock-summary-operation-id,
  html[data-color-mode="dark"] .swagger-ui .opblock .opblock-summary-path,
  html[data-color-mode="dark"]
    .swagger-ui
    .opblock
    .opblock-summary-path__deprecated,
  html[data-color-mode="dark"] .swagger-ui .opblock-description-wrapper,
  html[data-color-mode="dark"] .swagger-ui .opblock-description-wrapper h4,
  html[data-color-mode="dark"] .swagger-ui .opblock-description-wrapper p,
  html[data-color-mode="dark"] .swagger-ui .opblock-external-docs-wrapper,
  html[data-color-mode="dark"] .swagger-ui .opblock-external-docs-wrapper h4,
  html[data-color-mode="dark"] .swagger-ui .opblock-external-docs-wrapper p,
  html[data-color-mode="dark"] .swagger-ui .opblock-tag small,
  html[data-color-mode="dark"] .swagger-ui .opblock-title_normal,
  html[data-color-mode="dark"] .swagger-ui .opblock-title_normal h4,
  html[data-color-mode="dark"] .swagger-ui .opblock-title_normal p,
  html[data-color-mode="dark"] .swagger-ui .parameter__name,
  html[data-color-mode="dark"] .swagger-ui .parameter__type,
  html[data-color-mode="dark"] .swagger-ui .response-col_links,
  html[data-color-mode="dark"] .swagger-ui .response-col_status,
  html[data-color-mode="dark"] .swagger-ui .responses-inner h4,
  html[data-color-mode="dark"] .swagger-ui .responses-inner h5,
  html[data-color-mode="dark"] .swagger-ui .scheme-container .schemes > label,
  html[data-color-mode="dark"] .swagger-ui .scopes h2,
  html[data-color-mode="dark"] .swagger-ui .servers > label,
  html[data-color-mode="dark"] .swagger-ui .tab li,
  html[data-color-mode="dark"] .swagger-ui label,
  html[data-color-mode="dark"] .swagger-ui select,
  html[data-color-mode="dark"] .swagger-ui table.headers td {
    color: var(--ds-text);
  }

  .swagger-status {
    border: 1px solid #dfe1e6;
    border-radius: 6px;
    background: #f7f8f9;
    padding: 10px;
    display: flex;
    align-items: center;
    gap: 8px;
  }

  #swagger-ui-container {
    display: none;
    border: 1px solid #dfe1e6;
    border-radius: 6px;
  }
</style>

<div id="swagger-status">
  <aui-spinner size="small"></aui-spinner>
  <div class="aui-message aui-message-info">
    <p class="title">
      <strong>Loading OpenAPI specification…</strong>
    </p>
  </div>
</div>

<div id="swagger-ui-container"></div>
<div id="swagger-error-container"></div>
<div id="swagger-data" data-url="$swaggerUrl"></div>

<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js"></script>
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-standalone-preset.js"></script>

<script>
  (() => {
    const ready = (fn) => {
      if (document.readyState !== "loading") {
        fn();
      } else {
        document.addEventListener("DOMContentLoaded", fn);
      }
    };

    ready(async () => {
      const status = document.getElementById("swagger-status");
      const container = document.getElementById("swagger-ui-container");
      const errorContainer = document.getElementById(
        "swagger-error-container"
      );
      const swaggerUrl = document
        .getElementById("swagger-data")
        .dataset.url.trim();

      const renderError = (message) => {
        status.style.display = "none";

        errorContainer.innerHTML = `
          <div
            role="note"
            aria-labelledby="swagger-error-title"
            class="aui-message aui-message-error"
          >
            <p id="swagger-error-title" aria-hidden="true" class="title">
              <strong hidden>Error: </strong>
              <strong>Could not reach URL</strong>
            </p>
            <p>
              No OpenAPI / Swagger JSON found at this link.<br/>
              Please verify that the URL points directly to a valid
              OpenAPI / Swagger JSON file.
            </p>
          </div>
        `;
      };

      try {
        if (!swaggerUrl) {
          renderError("Missing specification URL.");
          return;
        }

        const res = await fetch(swaggerUrl);

        if (!res.ok) {
          renderError("HTTP " + res.status + " returned.");
          return;
        }

        const text = await res.text();

        try {
          JSON.parse(text);
        } catch {
          renderError("Invalid JSON document.");
          return;
        }

        status.style.display = "none";
        container.style.display = "block";

        SwaggerUIBundle({
          url: swaggerUrl,
          dom_id: "#swagger-ui-container",
          deepLinking: true,
          presets: [
            SwaggerUIBundle.presets.apis,
            SwaggerUIStandalonePreset,
          ],
          layout: "BaseLayout",
          persistAuthorization: true,
        });
      } catch (err) {
        renderError();
      }
    });
  })();
</script>