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

# Hosting Admin SDK

> Manage hosting projects, apps, deploy keys, previews, production, and domains with @embeddables/admin

Hosting methods are available on the authenticated `@embeddables/admin` client. Reads require
`project.read`; writes require `project.write`.

## Install

<CodeGroup>
  ```bash npm theme={null}
  npm install @embeddables/admin
  ```

  ```bash pnpm theme={null}
  pnpm add @embeddables/admin
  ```

  ```bash yarn theme={null}
  yarn add @embeddables/admin
  ```
</CodeGroup>

Example:

```typescript theme={null}
import { initAdmin } from '@embeddables/admin'

const admin = initAdmin()
await admin.requestOtp({ email: 'operator@example.com' })
await admin.verifyOtp({ email: 'operator@example.com', token: '123456' })
```

## Project and apps

<AccordionGroup>
  <Accordion title="getHostingProject()" description="Read project hosting enrollment">
    <ParamField path="projectId" type="string" required>Project UUID.</ParamField>

    <ResponseField name="result" type="GetHostingProjectResult">
      `{ slug, enrolled, app_count }`.
    </ResponseField>
  </Accordion>

  <Accordion title="enrollHostingProject()" description="Set the project slug once">
    <ParamField path="projectId" type="string" required>
      Project UUID.
    </ParamField>

    <ParamField body="slug" type="string" required>
      A 3–16 character hosting slug.
    </ParamField>

    <ResponseField name="result" type="GetHostingProjectResult">
      Updated enrollment.
    </ResponseField>
  </Accordion>

  <Accordion title="listHostingApps()" description="List registered apps">
    <ParamField path="projectId" type="string" required>
      Project UUID.
    </ParamField>

    <ResponseField name="apps" type="HostingApp[]">
      Registered apps.
    </ResponseField>
  </Accordion>

  <Accordion title="createHostingApp()" description="Register an app">
    <ParamField path="projectId" type="string" required>
      Project UUID.
    </ParamField>

    <ParamField body="slug" type="string" required>
      A unique 3–12 character app slug.
    </ParamField>

    <ParamField body="repo_path" type="string | null">
      Optional repository path.
    </ParamField>

    <ParamField body="production_branch" type="string">
      Git branch to auto-promote; defaults to `main`.
    </ParamField>

    <ResponseField name="app" type="HostingApp">
      Created app.
    </ResponseField>
  </Accordion>

  <Accordion title="updateHostingApp()" description="Update app settings">
    <ParamField path="projectId" type="string" required>
      Project UUID.
    </ParamField>

    <ParamField path="appId" type="string" required>
      Hosting app UUID.
    </ParamField>

    <ParamField body="repo_path" type="string | null">
      New repository path.
    </ParamField>

    <ParamField body="production_branch" type="string">
      New production branch. Its latest deploy is promoted when the value changes and a deploy exists.
    </ParamField>

    <ResponseField name="app" type="HostingApp">
      Updated app.
    </ResponseField>
  </Accordion>

  <Accordion title="deleteHostingApp()" description="Delete an app and its domains">
    <ParamField path="projectId" type="string" required>Project UUID.</ParamField>
    <ParamField path="appId" type="string" required>Hosting app UUID.</ParamField>
    <ResponseField name="ok" type="true">Confirms deletion.</ResponseField>
  </Accordion>
</AccordionGroup>

The project slug accepts 3–16 characters and the app slug 3–12 characters. Both use lowercase
letters, numbers, and single hyphens. The project slug is set once, and app slugs do not change.
`production_branch` defaults to `main`.

Changing `production_branch` immediately promotes that branch's latest deploy when one exists.

## Deploy keys

<AccordionGroup>
  <Accordion title="createHostingDeployKey()" description="Create a CI deploy key">
    <ParamField path="projectId" type="string" required>Project UUID.</ParamField>

    <ResponseField name="result" type="CreateHostingDeployKeyResult">
      `{ id, prefix, secret, created_at }`.
    </ResponseField>
  </Accordion>

  <Accordion title="listHostingDeployKeys()" description="List deploy-key metadata">
    <ParamField path="projectId" type="string" required>
      Project UUID.
    </ParamField>

    <ResponseField name="deploy_keys" type="HostingDeployKey[]">
      Key ids, prefixes, creation, revocation, and last-used timestamps.
    </ResponseField>
  </Accordion>

  <Accordion title="revokeHostingDeployKey()" description="Revoke a CI deploy key">
    <ParamField path="projectId" type="string" required>Project UUID.</ParamField>
    <ParamField path="keyId" type="string" required>Deploy-key UUID.</ParamField>
    <ResponseField name="ok" type="true">Confirms revocation.</ResponseField>
  </Accordion>
</AccordionGroup>

<Warning>
  `createHostingDeployKey` is the only response that contains the plaintext `edk_…` secret. Store it
  immediately. `listHostingDeployKeys` returns only the key id, prefix, creation and revocation
  timestamps, and last-used timestamp.
</Warning>

## Previews and production

<AccordionGroup>
  <Accordion title="listHostingPreviews()" description="List previews for one git branch">
    <ParamField path="projectId" type="string" required>Project UUID.</ParamField>
    <ParamField path="branch" type="string" required>Git branch name.</ParamField>
    <ResponseField name="previews" type="HostingPreview[]">Branch previews across the project's apps.</ResponseField>
  </Accordion>

  <Accordion title="listHostingDeploys()" description="List an app's commit builds">
    <ParamField path="projectId" type="string" required>
      Project UUID.
    </ParamField>

    <ParamField path="appId" type="string" required>
      Hosting app UUID.
    </ParamField>

    <ParamField body="limit" type="number">
      1–100; defaults to 20.
    </ParamField>

    <ResponseField name="deploys" type="HostingDeploy[]">
      Commit builds, newest first.
    </ResponseField>

    <ResponseField name="production" type="HostingPreview & { script_name: string } | null">
      What production serves now, even when outside the returned page.
    </ResponseField>
  </Accordion>

  <Accordion title="getHostingProduction()" description="Read the production alias">
    <ParamField path="projectId" type="string" required>Project UUID.</ParamField>
    <ParamField path="appId" type="string" required>Hosting app UUID.</ParamField>

    <ResponseField name="result" type="GetHostingProductionResult">
      `{ production_label, script_name, updated_at }`.
    </ResponseField>
  </Accordion>

  <Accordion title="listHostingPromotions()" description="List production changes">
    <ParamField path="projectId" type="string" required>
      Project UUID.
    </ParamField>

    <ParamField path="appId" type="string" required>
      Hosting app UUID.
    </ParamField>

    <ParamField body="limit" type="number">
      1–100.
    </ParamField>

    <ResponseField name="promotions" type="HostingPromotion[]">
      Promotions, newest first.
    </ResponseField>
  </Accordion>

  <Accordion title="promoteHostingPreview()" description="Promote a preview to production">
    <ParamField path="projectId" type="string" required>Project UUID.</ParamField>
    <ParamField path="appId" type="string" required>Hosting app UUID.</ParamField>

    <ParamField body="label" type="string" required>
      Branch or immutable commit preview label. Older commit labels support rollback.
    </ParamField>

    <ResponseField name="result" type="PromoteHostingPreviewResult">
      `{ production_label, script_name }`.
    </ResponseField>
  </Accordion>
</AccordionGroup>

Deploy and promotion limits accept 1–100. Deploys default to 20. Manual promotion can select an
older immutable commit preview for rollback.

## Custom domains

<AccordionGroup>
  <Accordion title="listHostingDomains()" description="List custom domains">
    <ParamField path="projectId" type="string" required>Project UUID.</ParamField>
    <ParamField path="appId" type="string" required>Hosting app UUID.</ParamField>
    <ResponseField name="domains" type="HostingDomain[]">Domains and their current status.</ResponseField>
  </Accordion>

  <Accordion title="addHostingDomain()" description="Register a custom domain">
    <ParamField path="projectId" type="string" required>
      Project UUID.
    </ParamField>

    <ParamField path="appId" type="string" required>
      Hosting app UUID.
    </ParamField>

    <ParamField body="hostname" type="string" required>
      Full hostname, without a scheme or path.
    </ParamField>

    <ResponseField name="domain" type="HostingDomain">
      Created domain in `pending` status.
    </ResponseField>
  </Accordion>

  <Accordion title="removeHostingDomain()" description="Remove a custom domain">
    <ParamField path="projectId" type="string" required>Project UUID.</ParamField>
    <ParamField path="appId" type="string" required>Hosting app UUID.</ParamField>
    <ParamField path="domainId" type="string" required>Hosting-domain UUID.</ParamField>
    <ResponseField name="ok" type="true">Confirms removal.</ResponseField>
  </Accordion>
</AccordionGroup>

After you add a domain, follow the dashboard's DNS instructions. Embeddables activates it after
verification.

## Returns

<ResponseField name="HostingApp" type="object">
  `{ id, project_id, slug, repo_path, production_branch, created_at }`.
</ResponseField>

<ResponseField name="HostingDomain" type="object">
  `{ id, project_id, app_id, hostname, status, cloudflare_hostname_id, verified_at, created_at }`.
  Status is `pending`, `active`, or `failed`.
</ResponseField>

<ResponseField name="HostingPreview" type="object">
  `{ label, url, app_slug, project_slug, ref }`. Production has a `null` ref.
</ResponseField>

<ResponseField name="HostingDeploy" type="HostingPreview">
  Also includes `{ commit, committed_at, created_at, branches, is_production }`.
</ResponseField>

<ResponseField name="HostingPromotion" type="object">
  `{ id, from_script_name, to_script_name, source_label, trigger, created_at }`.
</ResponseField>

## Errors

All SDK failures extend `AdminError`.

| When | Error |
| - | - |
| A hosting method is called before OTP verification or session adoption | `ConfigError` |
| The session is missing, invalid, or expired | `ApiError` with status `401` |
| A slug, hostname, UUID, branch, label, or limit is invalid | `ApiError` with status `400` |
| The operator is not a project member or lacks `project.read` / `project.write` | `ApiError` with status `403` |
| A project, app, domain, deploy key, preview, or production alias is missing | `ApiError` with status `404` |
| A project slug is locked or taken, the project is not enrolled, or an app slug or hostname is already registered | `ApiError` with status `409` |


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