> ## 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.

# Deploy from Code

> Upload a zip of your frontend code and Mixpeek builds and hosts it automatically. Deploy to staging or production with full versioning.

## Overview

Deploy any React app, vanilla JS site, or static bundle to a Canvas app. Upload a `.zip` of your build output and Mixpeek handles hosting, CDN distribution, and versioning — no infrastructure needed.

Every deploy creates an immutable version record with a commit message, asset manifest, and content hashes. See [Version History](/canvas/apps/versions) for the full version control system.

***

## Quickstart

<Steps>
  <Step title="Build your frontend">
    Build your app to a static output directory.

    ```bash theme={null}
    # React / Vite
    npm run build
    # Output: dist/

    # Next.js (static export)
    npm run build && npm run export
    # Output: out/
    ```
  </Step>

  <Step title="Zip the output directory">
    ```bash theme={null}
    zip -r my-app.zip dist/
    ```

    The zip should contain an `index.html` at the root (or inside a single top-level folder). Mixpeek auto-detects the entry point.
  </Step>

  <Step title="Get a presigned upload URL">
    ```bash theme={null}
    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"}'
    ```

    Response:

    ```json theme={null}
    {
      "upload_url": "https://s3.us-east-2.amazonaws.com/...",
      "bundle_s3_key": "app-builds/<app_id>/<token>/my-app.zip",
      "expires_in": 3600
    }
    ```

    Save `upload_url` for the next step and `bundle_s3_key` for the deploy request.
  </Step>

  <Step title="Upload the zip">
    ```bash theme={null}
    curl -X PUT "$UPLOAD_URL" \
      -H "Content-Type: application/zip" \
      --data-binary @my-app.zip
    ```
  </Step>

  <Step title="Trigger the deploy">
    A commit `message` is required — it describes what changed in this version.

    ```bash theme={null}
    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",
        "source_files": {
          "index.html": "<!DOCTYPE html>...",
          "src/app.js": "// your source"
        }
      }'
    ```

    <Warning>
      `source_files` is **required** for `cli_upload` deploys. Without it the deploy returns 400.
      Pass a `{path: content}` map of your source files so versions can be diffed and re-deployed.
    </Warning>

    Include `git_commit_sha` / `git_author` for CI/CD traceability.
  </Step>
</Steps>

Your app is live at `https://{slug}.mxp.co` within seconds of a successful build.

***

## Environments

Each app supports two independent environments with separate URLs and asset prefixes:

| Environment    | URL pattern             | Deploy field                  |
| -------------- | ----------------------- | ----------------------------- |
| **Production** | `{slug}.mxp.co`         | `"environment": "production"` |
| **Staging**    | `staging-{slug}.mxp.co` | `"environment": "staging"`    |

Staging and production can serve different versions simultaneously. Deploy to staging first to test, then promote to production.

### Deploying to staging

```bash theme={null}
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": "staging",
    "message": "Test new search layout"
  }'
```

Your staging build is live at `https://staging-{slug}.mxp.co`.

### Promoting staging to production

Once you've verified staging, promote it to production by deploying the same bundle to the production environment, or use the restore endpoint to point production at the staging version:

```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"}'
```

Restore is instant — no rebuild required.

***

## Deploy lifecycle

| Stage      | Description                                                 |
| ---------- | ----------------------------------------------------------- |
| `queued`   | Bundle uploaded, deploy queued                              |
| `building` | Mixpeek is validating, packaging, and uploading assets      |
| `complete` | Deploy complete — new version is live                       |
| `failed`   | Deploy failed — previous version stays live, error returned |

### Check deploy status

`GET /v1/apps/{app_id}/deploys/{deploy_id}` returns the current status of a deploy:

```bash theme={null}
curl https://api.mixpeek.com/v1/apps/$APP_ID/deploys/$DEPLOY_ID \
  -H "Authorization: Bearer $API_KEY"
```

Response:

```json theme={null}
{
  "deploy_id": "dep_abc123",
  "status": "building",
  "environment": "production",
  "message": "Initial deploy",
  "created_at": "2025-01-15T10:30:00Z"
}
```

The `status` field will be one of: `queued`, `building`, `complete`, or `failed`.

### Stream build logs

`GET /v1/apps/{app_id}/deploys/{deploy_id}/logs/stream` returns a Server-Sent Events (SSE) stream of build logs in real time. Use this to monitor progress or debug failed deploys:

```bash theme={null}
curl -N https://api.mixpeek.com/v1/apps/$APP_ID/deploys/$DEPLOY_ID/logs/stream \
  -H "Authorization: Bearer $API_KEY"
```

Each SSE event contains a log line from the build process. The stream closes automatically when the deploy reaches `complete` or `failed`.

***

## Multi-file output

Your zip can contain any number of files — HTML, JS, CSS, images, fonts. The only requirement is an `index.html` at the root. All files are uploaded to S3 and served with appropriate cache headers:

* **`index.html`** — no-cache (always fresh)
* **Content-hashed files** (e.g., `app-a1b2c3.js`) — immutable, permanent cache
* **Other assets** — standard cache headers

***

## Canvas SDK

Your app runs inside the Mixpeek canvas runtime. Call Mixpeek APIs through the built-in `/api` proxy — credentials are injected **server-side**, so your API key never reaches the browser:

```jsx theme={null}
// src/App.jsx — no auth headers needed
async function search(query) {
  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()
}
```

<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.
</Note>

***

## Pre-deploy validation

Mixpeek runs automatic checks on every bundle before deploying:

| Check                      | What it catches                                             |
| -------------------------- | ----------------------------------------------------------- |
| **index.html exists**      | Missing entry point (zip structure wrong)                   |
| **Script tags present**    | Blank page (no executable code)                             |
| **Asset references valid** | Broken `src`/`href` links in index.html                     |
| **No bare `process.env`**  | Runtime crash in browser (use `window.__MIXPEEK__` instead) |
| **Bundle size \< 50 MB**   | Accidentally included `node_modules` or large assets        |
| **JS bundles non-empty**   | Failed build that produced empty files                      |

If any check fails, the deploy is rejected with a descriptive error message.

***

## Deploy via Studio

In the App details page, drag & drop your `.zip` file onto the **Deploy** panel and click **Deploy**. The build is queued immediately and status updates in real time.

***

## Rollback

Every deploy is versioned. You have two rollback options:

**Quick rollback** — restore the previous config:

```bash theme={null}
curl -X POST https://api.mixpeek.com/v1/apps/$APP_ID/rollback \
  -H "Authorization: Bearer $API_KEY"
```

**Restore any version** — point an environment to a specific version's assets:

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

Restore is instant — no rebuild required. All previous assets are retained in S3.

See [Version History](/canvas/apps/versions) for the full version control system including diffs, downloads, and git metadata.

***

## Source files

When you include `source_files` in your deploy request, those files are stored alongside the version record. This enables:

* **Source-level diffs** — compare actual source code between versions, not just built output
* **Download and re-deploy** — download a version's source files and deploy modified code

```bash theme={null}
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": "Add dark mode",
    "source_files": {
      "src/App.tsx": "import React from ...",
      "src/components/Header.tsx": "export function Header() ...",
      "src/styles.css": "body { ... }"
    }
  }'
```

***

## Related

* [Apps overview](/canvas/apps)
* [Version History](/canvas/apps/versions)
* [Custom Domains](/canvas/apps/domains)
* [Deploy App (API)](/api-reference/apps/deploy-app)
* [List Versions (API)](/api-reference/apps/list-versions)
