> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mixpeek.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Apps

> Deploy your own frontend code — React, vanilla JS, or any static site — connected to your Mixpeek retrievers. Served on your custom domain with auth built in.

> [!NOTE]
>
> Canvas Apps require an **Enterprise** plan. [Contact us](https://mixpeek.com/contact) to upgrade.

## Overview

Canvas is Mixpeek's **application hosting platform**. Build a web app using any frontend framework — React, Vue, Svelte, vanilla JS, or plain HTML — and deploy it as a zip bundle. Mixpeek handles hosting, CDN, auth, versioning, and the entire backend.

Your app is live at `{slug}.mxp.co` the moment you deploy. No servers to manage, no infrastructure to configure, no API keys in your frontend code.

### How it works

You write frontend code. Mixpeek provides everything else:

* **Hosting** — your built assets (HTML, JS, CSS, images) are stored on S3 and served via CDN with immutable caching
* **Backend proxy** — all `/api/*` requests are forwarded to `api.mixpeek.com` with your org's API key and namespace injected server-side. Your credentials never reach the browser.
* **Runtime injection** — the Canvas runtime injects `window.__MIXPEEK__` into your HTML at serve time with auth config, environment variables, and monitoring hooks
* **Auth** — optionally enable Clerk-powered sign-in (Google, GitHub, email) with per-app user management. The auth SDK is auto-injected — no libraries to install.
* **Environments** — deploy to staging (`staging-{slug}.mxp.co`) or production (`{slug}.mxp.co`) independently
* **Versioning** — every deploy creates an immutable version with content hash, commit message, and source files. Rollback instantly.
* **Monitoring** — error boundaries, Sentry, and PostHog are auto-injected for crash detection and usage analytics

You bring the UI, Mixpeek brings search, retrieval, ingestion, and multimodal AI.

> [card] [Deploy from code](https://docs.mixpeek.com/docs/canvas/apps/deploy.md)
>
> Upload any React, vanilla JS, or static site. Mixpeek serves it from S3 with CDN caching.

> [card] [Custom domains](https://docs.mixpeek.com/docs/canvas/apps/domains.md)
>
> Default domain at `{slug}.mxp.co`. Add your own subdomain via CNAME with auto TLS.

> [card] [Built-in auth](https://docs.mixpeek.com/docs/canvas/apps/authentication.md)
>
> Clerk-powered sign-in (Google, GitHub, email) — auto-injected into your app with zero config.

> [card] [User management](https://docs.mixpeek.com/docs/canvas/apps/users.md)
>
> Invite members, assign roles, and control per-app access — powered by Clerk organizations.

> [card] [Git-like version control](https://docs.mixpeek.com/docs/canvas/apps/versions.md)
>
> Every publish and deploy creates an immutable version with a content hash, message, and diffable snapshot.

***

## Architecture

When a user visits `{slug}.mxp.co`, here's the request lifecycle:

1. **DNS** — resolves to the Canvas runtime (Cloudflare-proxied, DDoS protected)
2. **Routing** — the Express server maps the hostname to your `app_id` (cached in Redis). Supports `{slug}.mxp.co`, `staging-{slug}.mxp.co`, and custom domains.
3. **HTML** — `index.html` is fetched from S3 and dynamically injected with `window.__MIXPEEK__` (runtime config, auth, monitoring)
4. **Assets** — JS, CSS, and images are served from S3 via CDN with immutable cache headers (content-hashed filenames → permanent caching)
5. **SPA routing** — non-asset paths fall through to `index.html` so client-side routing (React Router, etc.) works out of the box
6. **API proxy** — `/api/*` requests are forwarded to `api.mixpeek.com` with `Authorization` and `X-Namespace` headers injected server-side

> [!NOTE]
>
> Your API key is **never** sent to the browser. The `/api` proxy runs server-side and injects credentials on every request. Your frontend code only calls relative paths like `/api/v1/retrievers/execute`.

### Runtime config injection

The canvas runtime injects a `window.__MIXPEEK__` object into your app's HTML at serve time:

```javascript theme={null}
window.__MIXPEEK__ = {
  apiUrl: "https://api.mixpeek.com",  // Always present
  // Your custom env vars from build_config.env_vars
  MY_CUSTOM_VAR: "value",
  // Auth config (if enabled)
  auth: {
    mode: "clerk",
    publishableKey: "pk_live_...",
    orgId: "org_xyz",
    providers: ["google", "github", "email"],
  },
}
```

Access these values in your app code:

```jsx theme={null}
const apiUrl = window.__MIXPEEK__?.apiUrl
const customVar = window.__MIXPEEK__?.MY_CUSTOM_VAR
```

> [!WARNING]
>
> Do not use `process.env` in your app — it will crash in the browser. Use `window.__MIXPEEK__` for runtime config or `import.meta.env` for Vite build-time variables.

***

## Quickstart

**1. Create an App in Studio**

Go to **Apps** → **Create App**. Give it a name and a globally-unique slug — this becomes your default URL at `https://{slug}.mxp.co`.

**2. Build your frontend**

Write a React app (or any static site) that calls Mixpeek APIs via the canvas proxy. See the [example below](#canvas-sdk).

```bash theme={null}
# Bootstrap a React app
npm create vite@latest my-search-app -- --template react
cd my-search-app
npm install
npm run build
# Zip the dist/ folder
zip -r my-search-app.zip dist/
```

**3. Deploy the bundle**

In the App details page, drag & drop your `.zip` onto the Deploy panel and click **Deploy**. Your build is queued immediately.

Or via API:

```bash theme={null}
# 1. Get a presigned upload URL
curl -X POST https://api.mixpeek.com/v1/apps/$APP_ID/deploy/upload-url \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "my-app.zip", "content_type": "application/zip"}'

# 2. PUT the zip to the returned upload_url
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/zip" \
  --data-binary @my-app.zip

# 3. Trigger the build
curl -X POST https://api.mixpeek.com/v1/apps/$APP_ID/deploy \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"source": "cli_upload", "bundle_s3_key": "$BUNDLE_KEY", "environment": "production", "message": "Initial deploy"}'
```

**4. Visit your App**

Your app is live at `https://{slug}.mxp.co` within seconds of a successful build. Add a custom domain to use your own URL.

***

## Canvas SDK

The canvas runtime injects credentials **server-side** — your API key never reaches the browser. Call any Mixpeek API through the `/api` proxy:

```jsx theme={null}
// src/App.jsx — a minimal React search app
import { useState } from 'react'

async function search(query) {
  // No auth headers needed — the canvas proxy injects them server-side
  const res = await fetch('/api/v1/retrievers/ret_abc123/execute', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      inputs: { query },
      settings: { limit: 20 },
    }),
  })
  return res.json()
}

export default function App() {
  const [query, setQuery] = useState('')
  const [results, setResults] = useState([])
  const [loading, setLoading] = useState(false)

  const handleSearch = async (e) => {
    e.preventDefault()
    setLoading(true)
    const data = await search(query)
    setResults(data.documents ?? [])
    setLoading(false)
  }

  return (
    <div style={{ maxWidth: 800, margin: '0 auto', padding: 24 }}>
      <h1>Search</h1>
      <form onSubmit={handleSearch}>
        <input
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="Search..."
          style={{ width: '100%', padding: 8, fontSize: 16 }}
        />
        <button type="submit" disabled={loading}>
          {loading ? 'Searching...' : 'Search'}
        </button>
      </form>
      <ul>
        {results.map((r) => (
          <li key={r.document_id}>
            <strong>{r.fields?.title ?? r.document_id}</strong>
            <p>{r.fields?.description}</p>
          </li>
        ))}
      </ul>
    </div>
  )
}
```

### What `/api` supports

The proxy forwards **all** Mixpeek API methods with your org's credentials injected:

| Category | Example paths |
| - | - |
| **Retrievers** | `/api/v1/retrievers/{id}/execute`, `/api/v1/retrievers/list` |
| **Collections** | `/api/v1/collections/list`, `/api/v1/collections/{id}` |
| **Documents** | `/api/v1/documents/{id}`, `/api/v1/documents/list` |
| **Namespaces** | `/api/v1/namespaces/list` |
| **Batches** | `/api/v1/batches/list`, `/api/v1/batches/{id}` |
| **Tasks** | `/api/v1/tasks/{id}`, `/api/v1/tasks/list` |
| **Marketplace** | `/api/v1/marketplace/catalog/{name}/execute` |
| **Taxonomies** | `/api/v1/taxonomies/list`, `/api/v1/taxonomies/{id}/classify` |

> [!NOTE]
>
> Use `/api/v1/...` (relative path) instead of `https://api.mixpeek.com/v1/...` — the canvas proxy injects `Authorization` and `X-Namespace` headers automatically, avoiding CORS and keeping API keys out of your bundle.

### Using the Mixpeek JS SDK

You can also use the `mixpeek` npm package pointed at `/api`:

```jsx theme={null}
import { Mixpeek } from 'mixpeek'

// apiKey is a placeholder — the proxy injects the real key server-side
const client = new Mixpeek({ apiKey: 'canvas', baseUrl: '/api' })

// Execute a retriever
const results = await client.request({
  method: 'POST',
  url: '/v1/retrievers/ret_abc123/execute',
  data: { inputs: { query: 'red shoes' }, settings: { limit: 10 } },
})

// Execute a marketplace retriever
const marketplace = await client.request({
  method: 'POST',
  url: '/v1/marketplace/catalog/brand-safety/execute',
  data: { inputs: { query: 'test content' }, settings: { limit: 5 } },
})
```

### Billing

All API calls through `/api` are billed to the organization that owns the canvas app. End-users don't need their own Mixpeek API keys or accounts — usage is attributed to your org automatically.

***

## Environments

Each app supports two independent environments:

| Environment | URL | Use case |
| - | - | - |
| **Staging** | `staging-{slug}.mxp.co` | Test changes before going live |
| **Production** | `{slug}.mxp.co` | Live, user-facing deployment |

Each environment has its own S3 asset prefix, so staging and production can serve different versions simultaneously. Deploy to either environment from Studio or via the API.

### Deploying to staging

Set `"environment": "staging"` in the deploy request. Your staging build is live at `https://staging-{slug}.mxp.co`.

### Promoting to production

Deploy the same bundle to production, or use the restore endpoint to point production at a staging version's assets:

```bash theme={null}
curl -X POST https://api.mixpeek.com/v1/apps/$APP_ID/versions/$STAGING_VERSION/restore \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"environment": "production"}'
```

***

## Creating an App

### Via Studio

Go to **Apps** → **Create App**. The wizard collects:

1. **Name + Slug** — the name is display-only; the slug becomes your URL (`{slug}.mxp.co`) and must be globally unique
2. **Access Control** — choose how end-users authenticate (public, password, API key, JWT, or SSO)

### Via API

Only `slug` and `meta` are required. After creation, deploy your code via the deploy pipeline.

```python Python theme={null}
from mixpeek import Mixpeek

client = Mixpeek(api_key="your-api-key")

app = client.apps.create(
    slug="product-search",
    meta={"title": "Product Search"},
    auth_config={"mode": "clerk"},  # optional — enable Clerk auth
)
print(f"App created: {app.app_id}")
print(f"URL: https://{app.slug}.mxp.co")
```

```bash cURL theme={null}
curl -X POST https://api.mixpeek.com/v1/apps \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "product-search",
    "meta": { "title": "Product Search" },
    "auth_config": { "mode": "clerk" }
  }'
```

***

## Deploy lifecycle

Every deploy creates a new versioned build. The lifecycle is:

| Stage | Description |
| - | - |
| `queued` | Bundle uploaded, build queued |
| `building` | Mixpeek is packaging and deploying your bundle |
| `complete` | Build complete — app is live |
| `failed` | Build failed — previous version stays live, check error message |

### Operations

| Operation | What it does | Creates a version? |
| - | - | - |
| **Deploy** (`POST /v1/apps/{id}/deploy`) | Upload and serve a new code bundle | Yes — with asset manifest and message |
| **Restore** (`POST /v1/apps/{id}/versions/{v}/restore`) | Roll back to any previous version's assets | No — instant redirect |
| **Promote** (`POST /v1/apps/{id}/promote`) | Promote staging deploy to production | No — swaps environments |
| **Update** (`PATCH /v1/apps/{id}`) | Update app config (auth, meta, etc.) | No |

Each deploy requires a commit message describing what changed. See [Version History](https://docs.mixpeek.com/docs/canvas/apps/versions.md) for the full version control system.

***

## Authentication modes

| Mode | Description |
| - | - |
| `public` | Anyone can access — no login required |
| `password` | Shared password gate |
| `api_key` | Requires `X-App-Key` header |
| `clerk` | Clerk-powered sign-in with [per-app user management](https://docs.mixpeek.com/docs/canvas/apps/users.md) |
| `jwt` | Customer-managed tokens |
| `sso_oidc` | Okta, Auth0, Azure AD |
| `sso_saml` | Enterprise SAML 2.0 IdP |

> [!NOTE]
>
> Auth enforcement activates when the canvas runtime is deployed. Config is stored now and takes effect automatically.

***

## Custom domains

Every app gets a default URL at `https://{slug}.mxp.co`. You can also add your own subdomain:

1. Enter your subdomain (e.g., `search.yourdomain.com`) in the **Domains** panel
2. Add a DNS `CNAME` record pointing to the target shown in the response
3. Click **Verify** — Mixpeek provisions a TLS certificate automatically via Cloudflare

See [Custom Domains](https://docs.mixpeek.com/docs/canvas/apps/domains.md) for the full setup guide.

***

## Example apps

> [card] E-commerce product search
>
> React app with multimodal search — text queries, image upload, and faceted filters backed by a Mixpeek retriever.

> [card] Internal knowledge base
>
> Password-protected semantic search over company documents, PDFs, and meeting transcripts.

> [card] Media library
>
> Video + image search portal with scene-level results, thumbnails, and timestamp previews.

> [card] Content moderation dashboard
>
> Real-time moderation queue pulling from alert webhooks and showing flagged content with similarity scores.

***

## Related

* [Version History](https://docs.mixpeek.com/docs/canvas/apps/versions.md)
* [Deploy from Code](https://docs.mixpeek.com/docs/canvas/apps/deploy.md)
* [Authentication](https://docs.mixpeek.com/docs/canvas/apps/authentication.md)
* [User Management](https://docs.mixpeek.com/docs/canvas/apps/users.md)
* [Custom Domains](https://docs.mixpeek.com/docs/canvas/apps/domains.md)
* [Create App (API)](https://docs.mixpeek.com/docs/api-reference/apps/create-app.md)
* [Deploy App (API)](https://docs.mixpeek.com/docs/api-reference/apps/deploy-app.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.