i99dash docs
Getting started

Upload your app

End-to-end publish flow for an app you already wrote — from CLI install to the catalog row going live.

You've built (or are building) a mini-app and want to get it live on the i99dash catalog. This page walks through the whole thing: what you need, what to run, what happens after you hit publish, and what to do when something fails.

If you're starting from scratch and want scaffolding, read the Quickstart instead — it gets you from nothing to a published hello-world in five minutes. This page is for devs who already have their own app code.

Before you start

You'll need:

  • Your app code. Any static site works: plain HTML, a Next.js project with output: 'export', a Vite / Vue / Svelte build, etc. The host loads your bundle into a sandboxed web view, so anything that compiles down to static files is fair game.
  • Node ≥ 20 and a package manager (pnpm, npm, or yarn). New machine? See Installation for the full prerequisites.
  • An i99dash user account. Sign up first if you don't have one; you'll approve the CLI's login request against this account in step 2.

Step 1 — install the CLI

No global install needed:

pnpm dlx @i99dash/sdk-cli --help

If you'd rather have sdk-i99dash on $PATH:

pnpm add -g @i99dash/sdk-cli

Step 2 — log in

sdk-i99dash login

The CLI prints a user code and opens the admin dashboard's device authorisation page in your browser. You sign in with your i99dash account and approve the code. The CLI polls in the background; on approval it receives a long-lived API key and stores it in your OS keychain.

Nothing about your password / session cookie ever touches the CLI. Full flow — including headless / CI machines and key rotation — in Authentication.

Verify:

sdk-i99dash whoami

Should print your dev id and email.

Step 3 — describe your app (manifest.json)

The manifest is the catalog row for your app. It lives at the root of your project and has to be there before you can publish. Minimal example:

{
  "id": "fuel_prices",
  "name": { "en": "Fuel Prices", "ar": "أسعار الوقود" },
  "iconUrl": "https://your-cdn.example.com/icons/fuel.png",
  "url": "https://your-cdn.example.com/fuel-prices/",
  "version": "1.0.0",
  "category": "info"
}

Three rules the CLI and backend are strict about. Getting these wrong once is fine; getting them wrong after publish is painful.

  1. id is forever. URL-safe, globally unique, baked into every pinned home-screen shortcut on users' devices. Rotating it orphans every launcher icon anyone ever pinned. Pick something you won't regret.
  2. version must increment on every publish. The backend rejects a resubmission with the same (id, version). Semver (1.2.3) is the convention; any monotonically increasing string works.
  3. url and iconUrl must be on the host allow-list. Your chosen origin needs to be in the host's allow-list config — coordinate with ops before publishing.

Full field reference (locales, minHostVersion, safeWhileDriving, zod schema): MiniAppManifest.

Step 4 — tell the CLI where your app lives (sdk.config.json)

The CLI needs two things: the directory that holds your built output (appRoot), and how to produce it (buildCommand, optional). Also at the project root:

{
  "appRoot": "./dist",
  "buildCommand": "pnpm build"
}

The common shapes:

Project typeappRootbuildCommand
Plain HTML / static./src(omit)
Next.js (static export)./outnext build
Vite / Vue / Svelte / Rollup./distvite build
Anything elseyour diryour build cmd

The CLI runs buildCommand (if set), tarballs appRoot, and uploads the tarball. It does not bundle, transpile, or minify for you — that's your build's job.

Step 5 — dry-run first

Before your first live upload, run:

sdk-i99dash publish --dry-run

This validates the manifest, runs your build, and produces the tarball in your temp directory. It prints the bundle size + SHA-256, then stops — no upload, no submission. Use it to catch the common mistakes (missing locale on name, http:// instead of https://, origin not allow-listed) before the backend records them.

Step 6 — ship it

sdk-i99dash publish

What the CLI does next:

  1. POST /api/v1/mini-apps/upload-url — asks the backend for a presigned upload URL and reserves a bundle id.
  2. PUT <presigned-url> — streams your tarball directly to the CDN.
  3. POST /api/v1/mini-apps/submit — registers the manifest + bundle id with the catalog.

What happens after publish

The CLI prints two URLs on success:

  • A status URL where you can track review state.
  • A CDN URL where your bundle is now fetchable (host-managed origin; the exact base URL is set by ops).

Your submission lands in one of two states:

  • auto-approved — your app is live in the catalog immediately. This is the default for apps you own.
  • pending — a human review is queued. You can keep iterating locally; re-running publish with a bumped version stacks a new bundle under the same review ticket.

Check back later with:

sdk-i99dash whoami       # your account

Or reopen the status URL for a specific submission.

Iterating

Every update is just another publish:

# bump version in manifest.json
sdk-i99dash publish

Same (id, version) is rejected. Same id with a new version is the update path — that's it, no separate "update" command. The host picks up the new bundle on the user's next launch.

For how to choose patch / minor / major, see Publishing — versioning strategy.

Common failures

Exit codeUsually meansFix
2 not logged inAPI key missing or revoked.Re-run sdk-i99dash login.
3 manifest badZod validation failed.Read the zod error. Most common: missing name locale, http:// URL.
4 networkTransport failure mid-upload.Retry. If the presigned URL expired, the CLI re-requests one.
5 backend rejectBackend refused the submission.Almost always VERSION_CONFLICT — bump version. Also: quota, origin not allow-listed.
6 local IODisk or temp-dir error building the tarball.Check free space in your system temp directory.

CI recipe

GitHub Actions, runs on tag push:

name: Publish mini-app
on:
  push:
    tags: ['v*']

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v5
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: sdk-i99dash validate
      - run: sdk-i99dash build
      - run: sdk-i99dash publish
        env:
          I99DASH_API_KEY: ${{ secrets.I99DASH_API_KEY }}

Use validate as a PR check; reserve publish for tag-triggered workflows. For how to provision the I99DASH_API_KEY secret on a headless machine, see Authentication — CI / headless machines.

Where to go next

  • Local development — fixture grammar for realistic mocked responses + driving-state simulation.
  • MiniAppClient — full reference for the runtime bridge.

On this page