diff --git a/api/static-export/generate-bundle.mdx b/api/static-export/generate-bundle.mdx new file mode 100644 index 0000000000..5f8eb219be --- /dev/null +++ b/api/static-export/generate-bundle.mdx @@ -0,0 +1,10 @@ +--- +title: "Generate export bundle" +openapi: "/static-export-openapi.json POST /static-export/jobs/{jobId}/bundle" +keywords: ["static export", "bundle", "s3", "download", "enterprise"] +--- + + + Static export requires an [Enterprise plan](https://mintlify.com/pricing?ref=static-export). + + diff --git a/api/static-export/get-job-status.mdx b/api/static-export/get-job-status.mdx new file mode 100644 index 0000000000..97e0d49558 --- /dev/null +++ b/api/static-export/get-job-status.mdx @@ -0,0 +1,10 @@ +--- +title: "Get static export job status" +openapi: "/static-export-openapi.json GET /static-export/jobs/{jobId}" +keywords: ["static export", "job", "status", "progress", "enterprise"] +--- + + + Static export requires an [Enterprise plan](https://mintlify.com/pricing?ref=static-export). + + diff --git a/api/static-export/overview.mdx b/api/static-export/overview.mdx new file mode 100644 index 0000000000..f6bcef8bbc --- /dev/null +++ b/api/static-export/overview.mdx @@ -0,0 +1,136 @@ +--- +title: "Static export" +description: "Generate a self-contained static export of your documentation and download it as a single bundle through the Mintlify REST API." +keywords: ["static export", "static site", "bundle", "self-host", "enterprise"] +--- + + + Static export requires an [Enterprise plan](https://mintlify.com/pricing?ref=static-export). + + +Use the static export API to programmatically pre-render your site into a self-contained set of static files and download the result as a single bundle. The exported bundle is pure HTML, CSS, and JavaScript with no runtime dependencies, so you can host it on any static file storage or CDN. + +## How static export works + +A static export runs as an asynchronous job. You start the job, poll for its status, and then generate a downloadable bundle once the job completes. + + + + Call [Start static export job](/api/static-export/start-job) with the domain you want to export. The API queues the job and returns a `jobId`. + + + Poll [Get static export job status](/api/static-export/get-job-status) with the `jobId` until `status` is `completed`. The response includes live `progress` and `pageCount` while the job runs. + + + Call [Generate export bundle](/api/static-export/generate-bundle) with the `jobId`. The API packages the export into a single archive and returns `bundleUrl`, a presigned S3 link to the static export bundle. Download it before the link expires. + + + +## Feature support by deployment + +Which features are available depends on how you host your deployment. Air-gapped deployments have no outbound network access, so anything that relies on Mintlify's cloud services is unavailable. Features labeled **Configurable** have different availability depending on your environment's setup. + +| Feature | Cloud | Client-hosted | Air-gapped | +| --- | :---: | :---: | :---: | +| Documentation search | | Configurable | | +| AI assistant | | Configurable | | +| Web analytics | | Configurable | | +| API playground ("Try it") | | | Configurable | +| Static export bundle | | | | + +## Endpoints + +- [Start static export job](/api/static-export/start-job): Start a static export job for a deployment. +- [Get static export job status](/api/static-export/get-job-status): Poll the status and progress of a running job. +- [Generate export bundle](/api/static-export/generate-bundle): Package a completed job and return a single S3 link to the bundle. + +## Authentication + +Authenticate requests with your admin API key. Generate an admin API key on the [API keys page](https://app.mintlify.com/settings/organization/api-keys) in your dashboard. Admin API keys begin with the `mint_` prefix and are server-side secrets--do not expose them in client-side code. + +## Deploy the bundle to your Enterprise Helm chart + +Self-hosted Mintlify is deployed with the Helm chart in the [`mintlify/enterprise`](https://github.com/mintlify/enterprise) repository. Once a static export job produces a bundle, you point the chart at the bundle and the deployment serves it from your own infrastructure. + + + + Set the static export fields in your `values.yaml` to the `bundleUrl` returned by [Generate export bundle](/api/static-export/generate-bundle). The chart fetches the bundle on startup and serves it as the active version. + + ```yaml values.yaml + staticExport: + enabled: true + # Presigned S3 link returned by the Generate export bundle endpoint. + bundleUrl: "https://mintlify-static-exports.s3.amazonaws.com/se_3f9a2c1b8e7d4a06/bundle.tar.gz" + # Optional: pin to a specific export version for reproducible rollouts. + version: "2024-06-01" + ``` + + + Apply the updated values with a `helm upgrade`. The deployment downloads the bundle, swaps it in as the live site, and serves it from your cluster. + + ```bash + helm upgrade --install mintlify mintlify/enterprise \ + --namespace mintlify \ + --create-namespace \ + -f values.yaml + ``` + + + +Because presigned links expire, regenerate the bundle and re-run the upgrade whenever you publish new content or automate the loop with GitHub Actions. + +## Automate with a GitHub Action + +The following template workflow runs the full export loop on a schedule or on demand. It starts a job, polls until the export completes, generates a bundle, and rolls the new `bundleUrl` into the Helm chart. + +```yaml .github/workflows/static-export.yml +name: Publish static export + +on: + workflow_dispatch: + schedule: + - cron: "0 6 * * *" # Daily at 06:00 UTC + +jobs: + export: + runs-on: ubuntu-latest + steps: + - name: Start static export job + id: start + run: | + JOB_ID=$(curl -s -X POST https://api.mintlify.com/v1/static-export/jobs \ + -H "Authorization: Bearer ${{ secrets.MINTLIFY_ADMIN_KEY }}" \ + -H "Content-Type: application/json" \ + -d '{"domain": "docs.example.com"}' | jq -r '.jobId') + echo "job_id=$JOB_ID" >> "$GITHUB_OUTPUT" + + - name: Wait for the job to complete + run: | + for i in $(seq 1 60); do + STATUS=$(curl -s https://api.mintlify.com/v1/static-export/jobs/${{ steps.start.outputs.job_id }} \ + -H "Authorization: Bearer ${{ secrets.MINTLIFY_ADMIN_KEY }}" | jq -r '.status') + echo "status=$STATUS" + [ "$STATUS" = "completed" ] && exit 0 + [ "$STATUS" = "failed" ] && exit 1 + sleep 10 + done + echo "Timed out waiting for the export job to complete." >&2 + exit 1 + + - name: Generate the bundle + id: bundle + run: | + BUNDLE_URL=$(curl -s -X POST \ + https://api.mintlify.com/v1/static-export/jobs/${{ steps.start.outputs.job_id }}/bundle \ + -H "Authorization: Bearer ${{ secrets.MINTLIFY_ADMIN_KEY }}" | jq -r '.bundleUrl') + echo "bundle_url=$BUNDLE_URL" >> "$GITHUB_OUTPUT" + + - name: Deploy to the Helm chart + run: | + helm upgrade --install mintlify mintlify/enterprise \ + --namespace mintlify \ + --set staticExport.enabled=true \ + --set staticExport.bundleUrl="${{ steps.bundle.outputs.bundle_url }}" +``` + +Store your admin API key as the `MINTLIFY_ADMIN_KEY` repository secret, and configure cluster credentials (for example, with `azure/setup-helm` and your kubeconfig) before the deploy step. diff --git a/api/static-export/start-job.mdx b/api/static-export/start-job.mdx new file mode 100644 index 0000000000..7c91a586bf --- /dev/null +++ b/api/static-export/start-job.mdx @@ -0,0 +1,10 @@ +--- +title: "Start static export job" +openapi: "/static-export-openapi.json POST /static-export/jobs" +keywords: ["static export", "job", "start", "bundle", "enterprise"] +--- + + + Static export requires an [Enterprise plan](https://mintlify.com/pricing?ref=static-export). + + diff --git a/docs.json b/docs.json index 1e916f1a5d..54c230f4ff 100644 --- a/docs.json +++ b/docs.json @@ -359,7 +359,8 @@ "guides/developer-documentation", "guides/knowledge-base", "guides/help-center", - "guides/custom-frontend" + "guides/custom-frontend", + "api/static-export/overview" ] } ] @@ -413,6 +414,15 @@ "api/analytics/views", "api/analytics/visitors" ] + }, + { + "group": "Static export", + "icon": "package", + "pages": [ + "api/static-export/start-job", + "api/static-export/get-job-status", + "api/static-export/generate-bundle" + ] } ] }, diff --git a/static-export-openapi.json b/static-export-openapi.json new file mode 100644 index 0000000000..83cb61a293 --- /dev/null +++ b/static-export-openapi.json @@ -0,0 +1,304 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Mintlify Static Export API", + "description": "Programmatically generate a self-contained static export of your documentation and download it as a single bundle. Available on Enterprise plans.", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://api.mintlify.com/v1" + } + ], + "security": [ + { + "bearerAuth": [] + } + ], + "paths": { + "/static-export/jobs": { + "post": { + "summary": "Start static export job", + "description": "Start a static export job for a deployment. The job pre-renders your documentation into a self-contained set of static HTML, RSC, and asset files. Returns a job ID you can use to poll status and, once complete, generate a downloadable bundle.\n\nStatic export is available on Enterprise plans.", + "operationId": "startStaticExportJob", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StartStaticExportRequest" + } + } + } + }, + "responses": { + "202": { + "description": "The export job was accepted and queued.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StaticExportJob" + } + } + } + }, + "401": { + "description": "Authentication failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "400": { + "description": "The request body is invalid. Check that `domain` is a reachable hostname and that `paths` entries are valid page paths.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "403": { + "description": "Static export is not enabled for this organization. Contact sales to enable it on an Enterprise plan.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/static-export/jobs/{jobId}": { + "get": { + "summary": "Get static export job status", + "description": "Retrieve the current status and progress of a static export job. Poll this endpoint after starting a job until `status` is `completed` (or `failed`).\n\nStatic export is available on Enterprise plans.", + "operationId": "getStaticExportJob", + "parameters": [ + { + "name": "jobId", + "in": "path", + "description": "The ID of the static export job returned by `Start static export job`.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The current state of the export job.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StaticExportJob" + } + } + } + }, + "401": { + "description": "Authentication failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "404": { + "description": "No job exists with the provided ID.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/static-export/jobs/{jobId}/bundle": { + "post": { + "summary": "Generate export bundle", + "description": "Package a completed static export job into a single archive and return a download link. The link is a presigned S3 URL — download it before `expiresAt`.\n\nThe job must have a `status` of `completed` before a bundle can be generated.\n\nStatic export is available on Enterprise plans.", + "operationId": "generateStaticExportBundle", + "parameters": [ + { + "name": "jobId", + "in": "path", + "description": "The ID of a completed static export job.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A presigned S3 link to the static export bundle.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BundleResponse" + } + } + } + }, + "401": { + "description": "Authentication failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "404": { + "description": "No job exists with the provided ID.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "409": { + "description": "The job has not finished yet, so a bundle cannot be generated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "description": "Admin API key. Generate one on the API keys page in your dashboard." + } + }, + "schemas": { + "StartStaticExportRequest": { + "type": "object", + "required": ["domain"], + "properties": { + "domain": { + "type": "string", + "description": "The domain of the deployment to export, for example `docs.example.com`.", + "example": "docs.example.com" + }, + "version": { + "type": "string", + "description": "An optional version label to tag this export. Defaults to the latest published version.", + "example": "2024-06-01" + }, + "paths": { + "type": "array", + "description": "An optional list of page paths to include. When omitted, every published page is exported.", + "items": { + "type": "string" + }, + "example": ["index", "guides/getting-started", "api-reference/introduction"] + } + } + }, + "StaticExportJob": { + "type": "object", + "required": ["jobId", "status", "progress", "pageCount", "createdAt", "updatedAt"], + "properties": { + "jobId": { + "type": "string", + "description": "Unique identifier for the static export job.", + "example": "se_3f9a2c1b8e7d4a06" + }, + "status": { + "type": "string", + "description": "The current state of the job.", + "enum": ["queued", "running", "completed", "failed"], + "example": "running" + }, + "progress": { + "type": "number", + "description": "Completion percentage from 0 to 100.", + "minimum": 0, + "maximum": 100, + "example": 42 + }, + "pageCount": { + "type": "integer", + "description": "The number of pages exported so far.", + "example": 128 + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "When the job was created." + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "description": "When the job was last updated." + }, + "error": { + "type": "string", + "description": "A human-readable error message. Present only when `status` is `failed`.", + "nullable": true + } + } + }, + "BundleResponse": { + "type": "object", + "required": ["jobId", "bundleUrl", "sizeBytes", "expiresAt"], + "properties": { + "jobId": { + "type": "string", + "description": "The ID of the job this bundle was generated for.", + "example": "se_3f9a2c1b8e7d4a06" + }, + "bundleUrl": { + "type": "string", + "format": "uri", + "description": "A presigned S3 link to the static export bundle archive. Download it before the link expires.", + "example": "https://mintlify-static-exports.s3.amazonaws.com/se_3f9a2c1b8e7d4a06/bundle.tar.gz?X-Amz-Signature=..." + }, + "sizeBytes": { + "type": "integer", + "description": "The size of the bundle in bytes.", + "example": 18432000 + }, + "expiresAt": { + "type": "string", + "format": "date-time", + "description": "When the presigned link expires." + } + } + }, + "Error": { + "type": "object", + "properties": { + "error": { + "type": "string", + "description": "A human-readable description of the error." + } + } + } + } + } +}