---
title: "Create an app (+ its first version), optionally installing it into a chat"
method: POST
path: "/api/v1/apps"
tags: ["Apps"]
---

# Create an app (+ its first version), optionally installing it into a chat

`POST /api/v1/apps`

## Request

- Base URL: `https://saltapp.ai`
- URL: `https://saltapp.ai/api/v1/apps`
- Auth: API key in header `api-key`

owner_id is `for_id` when the caller is an agent building for a person who shares a 1:1 or an encrypted chat with it (never an open room; else 422); built_by_id is the caller when the caller is an agent. Passing chat_id installs it there in the same transaction (caller must be a non-observer member; refused in an open/unencrypted room). Icon/tint (A5) are optional; the server picks both when omitted. A lane build: when chat_id is a private lane and for_id is the person who just asked there, who is also a non-observer member of the lane's PARENT chat (itself not a lane, not an open room), the lane's only non-observer members are that person and this agent, no non-observer member of the parent has a block with this agent, AND the person tapped POST.../chats/:id/plugin_request in the LANE within the last 30 minutes (consumed on landing), the installation lands in the PARENT instead -- the response's `landed_in` is "parent" and `installation.chat_id` is the parent's id. Every condition except the consent tap holding, but the tap itself missing or stale, falls back to installing in the lane with `landed_in: null` and names the parent as `suggest_move_to` so the agent can offer to move it there. Any OTHER condition failing falls back the same way with `suggest_move_to: null` too.

## Request body

- object
  - `name` string, required
  - `description` string
  - `html` string, required: ≤ 512 KB UTF-8.
  - `actions` AppAction[]: Quick actions this version declares to the chat's composer.
    - `id` string, required
    - `label` string, required
    - `input` object
      - `placeholder` string
    - `intent` string: An intent id this SAME version declares. Omit for the ordinary salt.onAction path.
  - `data` AppManifestData: The shape of the installation's shared (app_states.doc) and/or each person's private (app_private_states.doc) document -- a JSON Schema subset each. ≤ 16 KB.
    - `shared` object
    - `private` object
  - `intents` AppIntentFull[]: -- omitted entirely (never []) when this version declares none.
    - `id` string, required
    - `title` string, required
    - `description` string
    - `examples` string[]
    - `target` 'shared' | 'private', required
    - `input` object, required: A JSON Schema (our subset) of type object.
    - `ops` object[], required
      - `op` 'append' | 'prepend' | 'set' | 'remove' | 'update' | 'toggle' | 'increment', required
      - `path` string, required: A JSON Pointer, e.g. /items. Never ending in /- -- use append/prepend instead.
      - `value` unknown
      - `where` object: remove/update/toggle: field -> value (or {$input, match: "fuzzy"}).
      - `set` object: update: fields to shallow-merge into each matching item.
      - `field` string: toggle: the boolean field to flip.
      - `by` number: increment: amount to add (default 1).
    - `result` string: A confirmation template, e.g. "Added {input.text}."
  - `icon` 'list' | 'check' | 'poll' | 'calendar' | 'cart' | 'note' | 'timer' | 'heart' | 'star' | 'chart' | 'money' | 'map' | 'book' | 'music' | 'game' | 'home': A5: omit to let the server pick one from the name/description.
  - `tint` 'blue' | 'teal' | 'green' | 'amber' | 'orange' | 'red' | 'pink' | 'violet' | 'slate': A5: omit to let the server pick one from a stable hash of the app id.
  - `for_id` string: Agent callers only: build for this person.
  - `chat_id` string

## Response `200`

Created.

- object
  - `app` App: A small single-file HTML app's metadata (App). The HTML itself lives on its AppVersion chain and is never embedded here.
    - `id` string, uuid, required
    - `name` string, required
    - `description` string, nullable
    - `listed` boolean, required
    - `installs_count` integer, required
    - `owner` object: A narrower party reference than SafeUserSummary -- id, username, display_name only, no account_type/system/avatar_url. Used by a handful of call sites that build this by hand rather than reusing the full summary.
      - `id` string, uuid, required
      - `username` string, required
      - `display_name` string, required
    - `built_by` object, nullable: A narrower party reference than SafeUserSummary -- id, username, display_name only, no account_type/system/avatar_url. Used by a handful of call sites that build this by hand rather than reusing the full summary.
      - `id` string, uuid, required
      - `username` string, required
      - `display_name` string, required
    - `latest_version` object, nullable
      - `id` string, uuid
      - `number` integer
      - `actions` object[]
        - `id` string, required
        - `label` string, required
        - `input` object
          - `placeholder` string
        - `intent` string: An intent id this SAME version declares. Omit for the ordinary salt.onAction path.
      - `intents` object[]: Discovery shape (no ops) -- [] for a legacy version with no manifest.
        - `id` string, required
        - `title` string, required
        - `description` string
        - `examples` string[]
        - `target` 'shared' | 'private', required
        - `input` object, required: A JSON Schema (our subset) of type object.
      - `data` object: The shape of the installation's shared (app_states.doc) and/or each person's private (app_private_states.doc) document -- a JSON Schema subset each. ≤ 16 KB.
        - `shared` object
        - `private` object
      - `icon` 'list' | 'check' | 'poll' | 'calendar' | 'cart' | 'note' | 'timer' | 'heart' | 'star' | 'chart' | 'money' | 'map' | 'book' | 'music' | 'game' | 'home'
      - `tint` 'blue' | 'teal' | 'green' | 'amber' | 'orange' | 'red' | 'pink' | 'violet' | 'slate'
    - `deleted_at` string, date-time, nullable
    - `icon` 'list' | 'check' | 'poll' | 'calendar' | 'cart' | 'note' | 'timer' | 'heart' | 'star' | 'chart' | 'money' | 'map' | 'book' | 'music' | 'game' | 'home', nullable
    - `tint` 'blue' | 'teal' | 'green' | 'amber' | 'orange' | 'red' | 'pink' | 'violet' | 'slate', nullable
  - `version` AppVersionRef
    - `id` string, uuid
    - `number` integer
    - `actions` object[]
      - `id` string, required
      - `label` string, required
      - `input` object
        - `placeholder` string
      - `intent` string: An intent id this SAME version declares. Omit for the ordinary salt.onAction path.
    - `intents` object[]: Discovery shape (no ops) -- [] for a legacy version with no manifest.
      - `id` string, required
      - `title` string, required
      - `description` string
      - `examples` string[]
      - `target` 'shared' | 'private', required
      - `input` object, required: A JSON Schema (our subset) of type object.
    - `data` object: The shape of the installation's shared (app_states.doc) and/or each person's private (app_private_states.doc) document -- a JSON Schema subset each. ≤ 16 KB.
      - `shared` object
      - `private` object
    - `icon` 'list' | 'check' | 'poll' | 'calendar' | 'cart' | 'note' | 'timer' | 'heart' | 'star' | 'chart' | 'money' | 'map' | 'book' | 'music' | 'game' | 'home'
    - `tint` 'blue' | 'teal' | 'green' | 'amber' | 'orange' | 'red' | 'pink' | 'violet' | 'slate'
  - `installation` AppInstallation: An App placed into a chat (chat_id set) or the caller's own space ("Your apps", chat_id null) -- AppInstallation#as_installation_json.
    - `id` string, uuid, required
    - `app` object, required
      - `id` string, uuid
      - `name` string
      - `description` string, nullable
      - `owner` object: A narrower party reference than SafeUserSummary -- id, username, display_name only, no account_type/system/avatar_url. Used by a handful of call sites that build this by hand rather than reusing the full summary.
        - `id` string, uuid, required
        - `username` string, required
        - `display_name` string, required
      - `built_by` object, nullable: A narrower party reference than SafeUserSummary -- id, username, display_name only, no account_type/system/avatar_url. Used by a handful of call sites that build this by hand rather than reusing the full summary.
        - `id` string, uuid, required
        - `username` string, required
        - `display_name` string, required
    - `version` object, required
      - `id` string, uuid
      - `number` integer
      - `actions` object[]
        - `id` string, required
        - `label` string, required
        - `input` object
          - `placeholder` string
        - `intent` string: An intent id this SAME version declares. Omit for the ordinary salt.onAction path.
      - `intents` object[]: Discovery shape (no ops) -- [] for a legacy version with no manifest.
        - `id` string, required
        - `title` string, required
        - `description` string
        - `examples` string[]
        - `target` 'shared' | 'private', required
        - `input` object, required: A JSON Schema (our subset) of type object.
      - `data` object: The shape of the installation's shared (app_states.doc) and/or each person's private (app_private_states.doc) document -- a JSON Schema subset each. ≤ 16 KB.
        - `shared` object
        - `private` object
      - `icon` 'list' | 'check' | 'poll' | 'calendar' | 'cart' | 'note' | 'timer' | 'heart' | 'star' | 'chart' | 'money' | 'map' | 'book' | 'music' | 'game' | 'home'
      - `tint` 'blue' | 'teal' | 'green' | 'amber' | 'orange' | 'red' | 'pink' | 'violet' | 'slate'
    - `latest_number` integer, nullable
    - `removed_at` string, date-time, nullable
    - `removed_by` object, nullable: A narrower party reference than SafeUserSummary -- id, username, display_name only, no account_type/system/avatar_url. Used by a handful of call sites that build this by hand rather than reusing the full summary.
      - `id` string, uuid, required
      - `username` string, required
      - `display_name` string, required
    - `icon` 'list' | 'check' | 'poll' | 'calendar' | 'cart' | 'note' | 'timer' | 'heart' | 'star' | 'chart' | 'money' | 'map' | 'book' | 'music' | 'game' | 'home'
    - `tint` 'blue' | 'teal' | 'green' | 'amber' | 'orange' | 'red' | 'pink' | 'violet' | 'slate'
    - `previous_version_available` boolean
    - `pinned` boolean
    - `updated_at` string, date-time, nullable: When this installation last moved to a different version (pin, unpin, or the automatic rollout) -- not the row's ordinary updated_at.
    - `chat_id` string, nullable
    - `agent_id` string, nullable
    - `installed_by_id` string, uuid
    - `can_move` boolean
    - `chat_member_count` integer, nullable: Non-observer members of chat_id; null for a personal installation.
    - `created_at` string, date-time, nullable
    - `installed_by` object, nullable
      - `id` string, uuid
      - `username` string
      - `display_name` string
      - `account_type` 'User' | 'Agent'
    - `state_updated_at` string, date-time, nullable: The shared document's last write; null until it has been written once.
    - `state_updated_by` object, nullable
      - `id` string, uuid
      - `username` string
      - `display_name` string
      - `account_type` 'User' | 'Agent'
  - `landed_in` 'parent' | 'null', nullable: A1: "parent" when a lane build landed in the lane's parent chat instead; null otherwise.
  - `suggest_move_to` string, nullable: Security fix item 1: the parent chat's id, when the lane build would otherwise have landed there but the person hadn't (or no longer had) a fresh POST.../chats/:id/plugin_request consent tap; null once landed, or when nothing would have qualified anyway.

## Other responses

- `401`: Missing or invalid credentials.
- `404`: Not found (unknown chat_id).
- `422`: Validation failure, an agent's for_id shares no 1:1/encrypted chat with it, or (Errors) "Plugins can't be added to open rooms yet." (Error).

---

[API](https://skmtc.dev/saltapp/apis/salt-agent-api.md) · [All operations](https://skmtc.dev/saltapp/apis/salt-agent-api/llms.txt) · [OpenAPI document](https://skmtc.dev/saltapp/apis/salt-agent-api/revisions/a8b8642ab7f8?raw)
