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

# Publishing Assets via API

> Publish a hosted site or file bundle programmatically: mint an access token, upload your files to the artifacts service, then manage sharing, versions, and analytics over REST.

Assets are Octave's hosted artifacts — self-contained HTML sites, downloadable file bundles, and managed skill folders — served from a dedicated artifacts service. The `/api/v2/asset/*` endpoints cover everything about an asset's lifecycle *after* it exists: listing, metadata and privacy, share links, versions, access requests, and analytics.

Creating an asset (the file upload itself) happens against the artifacts service directly, authenticated with a per-user access token you mint over REST. The full publish flow is three steps.

<Steps>
  <Step title="Mint an access token">
    Call [Generate Asset Access Token](/v2-api-reference/asset/generate-asset-access-token) with your regular API key:

    ```bash theme={null}
    curl -X POST https://app.octavehq.com/api/v2/asset/access-token/generate \
      -H "api_key: YOUR_API_KEY"
    ```

    The response contains the plaintext `accessToken` (valid \~30 days), its non-secret `prefix`, and `expiresAt`. **The token is shown only in this response** — store it securely; it cannot be retrieved again.
  </Step>

  <Step title="Upload your files to the artifacts service">
    `POST` a multipart request to the artifacts service with the token as a Bearer credential. Send a `metadata` part (a JSON string) plus either repeated `files` parts or a single `file` part containing a zip of the folder:

    ```bash theme={null}
    curl -X POST https://artifacts.octavehq.com/api/v1/artifacts/ \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -F 'metadata={"identifier":"q3-launch-microsite","description":"Q3 launch landing page","type":"website","entryPoint":"index.html"}' \
      -F "files=@index.html"
    ```

    * `metadata` fields: `identifier` (required, workspace-unique), `description` (required), and optional `type` (`website` | `storage`), `entryPoint`, `privacy` (`only_me` | `workspace` | `public`), `status` (`published` | `unpublished`).
    * Each `files` part's filename is its path within the asset, so a multi-file site can carry paths like `assets/app.js`.
    * The service enforces a **30 MB total** per upload, answers **409** when the identifier is already taken, and **400** on validation failures.
    * The response is the created asset, including its `uuid` (the id every `/api/v2/asset/*` endpoint takes) and its URLs.
  </Step>

  <Step title="Manage it over REST">
    Everything else is plain `api_key` REST. For example, make it public and check how it is doing:

    ```bash theme={null}
    curl -X POST https://app.octavehq.com/api/v2/asset/update \
      -H "api_key: YOUR_API_KEY" -H "Content-Type: application/json" \
      -d '{"uuid":"ASSET_UUID","privacy":"public"}'

    curl "https://app.octavehq.com/api/v2/asset/stats?uuid=ASSET_UUID" \
      -H "api_key: YOUR_API_KEY"
    ```

    See [List Assets](/v2-api-reference/asset/list-assets) for discovery, the share endpoints for email/domain-gated links, and the version endpoints for rollbacks.
  </Step>
</Steps>

## Token semantics you must handle

One access token is active per user and workspace. Minting a new one rotates out the previous token — and other Octave surfaces acting as the same user (the in-app asset gallery, MCP tools) can rotate yours at any time.

**Treat a `401` from the artifacts service as "mint again and retry once":** call [Generate Asset Access Token](/v2-api-reference/asset/generate-asset-access-token) and repeat the request with the fresh token. With that retry rule the flow is robust; without it you will see intermittent 401s that look random. If the integration can run under a dedicated service user's API key rather than a person's, its token churn also stops competing with that person's own sessions.

## Notes

* **Skills are managed assets.** Skill folders appear in asset listings with `type: "skill"`, but their files must be edited through the [Skill endpoints](/v2-api-reference/skill/list-skills) so the searchable skill registry stays in sync — the generic asset surface refuses to delete them or roll their files back.
* **Publishing and privacy are independent.** `status: "published"` does not make an asset public; set `privacy: "public"` for that. Non-public assets are reachable for you via the tokenized `previewUrl` on the asset.
